@elevasis/sdk 1.20.2 → 1.21.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 (58) hide show
  1. package/dist/cli.cjs +3386 -1529
  2. package/dist/index.d.ts +412 -149
  3. package/dist/index.js +955 -721
  4. package/dist/node/index.d.ts +0 -3
  5. package/dist/node/index.js +21 -48
  6. package/dist/test-utils/index.d.ts +395 -128
  7. package/dist/test-utils/index.js +599 -368
  8. package/dist/worker/index.js +536 -323
  9. package/package.json +2 -2
  10. package/reference/_navigation.md +9 -7
  11. package/reference/_reference-manifest.json +1 -1
  12. package/reference/claude-config/rules/agent-start-here.md +4 -0
  13. package/reference/claude-config/rules/frontend.md +2 -2
  14. package/reference/claude-config/rules/organization-model.md +44 -2
  15. package/reference/claude-config/rules/organization-os.md +12 -12
  16. package/reference/claude-config/rules/ui.md +14 -14
  17. package/reference/claude-config/rules/vibe.md +37 -33
  18. package/reference/claude-config/skills/explore/SKILL.md +6 -6
  19. package/reference/claude-config/skills/knowledge/SKILL.md +73 -29
  20. package/reference/claude-config/skills/knowledge/operations/codify-level-a.md +1 -1
  21. package/reference/claude-config/skills/knowledge/operations/codify-level-b.md +25 -24
  22. package/reference/claude-config/skills/knowledge/operations/features.md +56 -93
  23. package/reference/claude-config/skills/knowledge/operations/labels.md +19 -14
  24. package/reference/claude-config/skills/knowledge/operations/offerings.md +6 -6
  25. package/reference/claude-config/skills/save/SKILL.md +2 -2
  26. package/reference/claude-config/skills/setup/SKILL.md +1 -1
  27. package/reference/claude-config/skills/tutorial/technical.md +23 -26
  28. package/reference/claude-config/skills/tutorial/vibe-coder.md +9 -9
  29. package/reference/claude-config/sync-notes/2026-05-12-sdk-ready-release-train.md +30 -0
  30. package/reference/cli.mdx +140 -0
  31. package/reference/deployment/provided-features.mdx +29 -15
  32. package/reference/examples/organization-model.ts +1 -1
  33. package/reference/packages/core/src/knowledge/README.md +8 -7
  34. package/reference/packages/core/src/organization-model/README.md +66 -26
  35. package/reference/packages/ui/src/provider/README.md +5 -5
  36. package/reference/scaffold/core/organization-graph.mdx +16 -15
  37. package/reference/scaffold/core/organization-model.mdx +89 -41
  38. package/reference/scaffold/index.mdx +9 -9
  39. package/reference/scaffold/operations/propagation-pipeline.md +3 -3
  40. package/reference/scaffold/operations/scaffold-maintenance.md +11 -11
  41. package/reference/scaffold/recipes/add-a-feature.md +26 -24
  42. package/reference/scaffold/recipes/add-a-resource.md +10 -14
  43. package/reference/scaffold/recipes/customize-crm-actions.md +439 -439
  44. package/reference/scaffold/recipes/customize-knowledge-browser.md +384 -0
  45. package/reference/scaffold/recipes/customize-organization-model.md +72 -44
  46. package/reference/scaffold/recipes/extend-crm.md +40 -39
  47. package/reference/scaffold/recipes/extend-lead-gen.md +15 -16
  48. package/reference/scaffold/recipes/gate-by-feature-or-admin.md +34 -30
  49. package/reference/scaffold/recipes/index.md +13 -12
  50. package/reference/scaffold/recipes/query-the-knowledge-graph.md +200 -0
  51. package/reference/scaffold/reference/contracts.md +362 -99
  52. package/reference/scaffold/reference/feature-registry.md +9 -20
  53. package/reference/scaffold/reference/glossary.md +18 -18
  54. package/reference/scaffold/ui/composition-extensibility.mdx +23 -23
  55. package/reference/scaffold/ui/customization.md +11 -11
  56. package/reference/scaffold/ui/feature-flags-and-gating.md +8 -8
  57. package/reference/scaffold/ui/feature-shell.mdx +19 -19
  58. package/reference/scaffold/ui/recipes.md +29 -28
package/dist/index.js CHANGED
@@ -171,776 +171,464 @@ DisplayMetadataSchema.extend({
171
171
  id: ModelIdSchema,
172
172
  resourceId: z.string().trim().min(1).max(255),
173
173
  resourceType: z.enum(["workflow", "agent", "trigger", "integration", "external", "human_checkpoint"]),
174
- featureIds: ReferenceIdsSchema,
174
+ systemIds: ReferenceIdsSchema,
175
175
  entityIds: ReferenceIdsSchema,
176
176
  surfaceIds: ReferenceIdsSchema,
177
- capabilityIds: ReferenceIdsSchema,
177
+ actionIds: ReferenceIdsSchema,
178
178
  /** Optional tech-stack metadata for external-SaaS integrations. */
179
179
  techStack: TechStackEntrySchema.optional()
180
180
  });
181
- var SalesStageSemanticClassSchema = z.enum(["open", "active", "nurturing", "closed_won", "closed_lost"]);
182
- var SalesStageSchema = DisplayMetadataSchema.extend({
183
- id: ModelIdSchema,
184
- order: z.number().int().min(0),
185
- semanticClass: SalesStageSemanticClassSchema,
186
- surfaceIds: ReferenceIdsSchema,
187
- resourceIds: ReferenceIdsSchema
181
+
182
+ // ../core/src/organization-model/domains/entities.ts
183
+ var EntityIdSchema = ModelIdSchema;
184
+ var EntityLinkKindSchema = z.enum(["belongs-to", "has-many", "has-one", "many-to-many"]).meta({ label: "Link kind" });
185
+ var EntityLinkSchema = z.object({
186
+ toEntity: EntityIdSchema.meta({ ref: "entity" }),
187
+ kind: EntityLinkKindSchema,
188
+ via: z.string().trim().min(1).max(255).optional(),
189
+ label: LabelSchema.optional()
188
190
  });
189
- var SalesPipelineSchema = z.object({
190
- id: ModelIdSchema,
191
- label: z.string().trim().min(1).max(120),
191
+ var EntitySchema = z.object({
192
+ id: EntityIdSchema,
193
+ /** Domain-map iteration order. Convention: multiples of 10 (10, 20, 30, ...) to allow easy insertion. */
194
+ order: z.number(),
195
+ label: LabelSchema,
192
196
  description: DescriptionSchema.optional(),
193
- entityId: ModelIdSchema,
194
- stages: z.array(SalesStageSchema).min(1)
195
- });
196
- z.object({
197
- entityId: ModelIdSchema,
198
- defaultPipelineId: ModelIdSchema,
199
- pipelines: z.array(SalesPipelineSchema).min(1)
197
+ ownedBySystemId: ModelIdSchema.meta({ ref: "system" }),
198
+ table: z.string().trim().min(1).max(255).optional(),
199
+ rowSchema: ModelIdSchema.optional(),
200
+ stateCatalogId: ModelIdSchema.optional(),
201
+ links: z.array(EntityLinkSchema).optional()
200
202
  });
201
- var CRM_DISCOVERY_REPLIED_STATE = {
202
- stateKey: "discovery_replied",
203
- label: "Discovery Replied"
204
- };
205
- var CRM_DISCOVERY_LINK_SENT_STATE = {
206
- stateKey: "discovery_link_sent",
207
- label: "Discovery Link Sent"
208
- };
209
- var CRM_DISCOVERY_NUDGING_STATE = {
210
- stateKey: "discovery_nudging",
211
- label: "Discovery Nudging"
212
- };
213
- var CRM_DISCOVERY_BOOKING_CANCELLED_STATE = {
214
- stateKey: "discovery_booking_cancelled",
215
- label: "Discovery Booking Cancelled"
216
- };
217
- var CRM_REPLY_SENT_STATE = {
218
- stateKey: "reply_sent",
219
- label: "Reply Sent"
220
- };
221
- var CRM_FOLLOWUP_1_SENT_STATE = {
222
- stateKey: "followup_1_sent",
223
- label: "Follow-up 1 Sent"
224
- };
225
- var CRM_FOLLOWUP_2_SENT_STATE = {
226
- stateKey: "followup_2_sent",
227
- label: "Follow-up 2 Sent"
228
- };
229
- var CRM_FOLLOWUP_3_SENT_STATE = {
230
- stateKey: "followup_3_sent",
231
- label: "Follow-up 3 Sent"
232
- };
233
- var CRM_PIPELINE_DEFINITION = {
234
- pipelineKey: "crm",
235
- label: "CRM",
236
- entityKey: "crm.deal",
237
- stages: [
238
- {
239
- stageKey: "interested",
240
- label: "Interested",
241
- color: "blue",
242
- states: [
243
- CRM_DISCOVERY_REPLIED_STATE,
244
- CRM_DISCOVERY_LINK_SENT_STATE,
245
- CRM_DISCOVERY_NUDGING_STATE,
246
- CRM_DISCOVERY_BOOKING_CANCELLED_STATE,
247
- CRM_REPLY_SENT_STATE,
248
- CRM_FOLLOWUP_1_SENT_STATE,
249
- CRM_FOLLOWUP_2_SENT_STATE,
250
- CRM_FOLLOWUP_3_SENT_STATE
251
- ]
252
- },
253
- { stageKey: "proposal", label: "Proposal", color: "yellow", states: [] },
254
- { stageKey: "closing", label: "Closing", color: "orange", states: [] },
255
- { stageKey: "closed_won", label: "Closed Won", color: "green", states: [] },
256
- { stageKey: "closed_lost", label: "Closed Lost", color: "red", states: [] },
257
- { stageKey: "nurturing", label: "Nurturing", color: "grape", states: [] }
258
- ]
259
- };
260
- var LEAD_GEN_STAGE_CATALOG = {
261
- // Prospecting — company population
262
- scraped: {
263
- key: "scraped",
264
- label: "Scraped",
265
- description: "Company was scraped from a source directory (Apify actor run).",
266
- order: 1,
267
- entity: "company"
268
- },
269
- populated: {
270
- key: "populated",
271
- label: "Companies found",
272
- description: "Companies have been found and added to the lead-gen list.",
273
- order: 2,
274
- entity: "company"
275
- },
276
- crawled: {
277
- key: "crawled",
278
- label: "Websites crawled",
279
- description: "Company websites have been crawled (e.g. via Apify) and raw page content stored for downstream LLM analysis.",
280
- order: 2.5,
281
- entity: "company"
282
- },
283
- extracted: {
284
- key: "extracted",
285
- label: "Websites analyzed",
286
- description: "Company websites have been analyzed for business signals.",
287
- order: 3,
288
- entity: "company"
289
- },
290
- enriched: {
291
- key: "enriched",
292
- label: "Enriched",
293
- description: "Company or contact enriched with third-party data (e.g. Tomba, Anymailfinder).",
294
- order: 4,
295
- entity: "company"
203
+ z.record(z.string(), EntitySchema).refine((record) => Object.entries(record).every(([key, entry]) => entry.id === key), {
204
+ message: "Each entity entry id must match its map key"
205
+ }).default({});
206
+ var ENTITY_ENTRY_INPUTS = [
207
+ {
208
+ id: "crm.deal",
209
+ order: 10,
210
+ label: "Deal",
211
+ description: "A CRM opportunity or sales pipeline record.",
212
+ ownedBySystemId: "sales.crm",
213
+ table: "crm_deals",
214
+ stateCatalogId: "crm.pipeline",
215
+ links: [{ toEntity: "crm.contact", kind: "has-many", via: "deal_contacts", label: "contacts" }]
296
216
  },
297
- "decision-makers-enriched": {
298
- key: "decision-makers-enriched",
299
- label: "Decision-makers found",
300
- description: "Decision-maker contacts discovered and attached to a qualified company.",
301
- order: 6,
302
- entity: "company",
303
- recordEntity: "contact",
304
- recordStageKey: "discovered"
217
+ {
218
+ id: "crm.contact",
219
+ order: 20,
220
+ label: "CRM Contact",
221
+ description: "A person associated with a CRM relationship or deal.",
222
+ ownedBySystemId: "sales.crm",
223
+ table: "crm_contacts"
305
224
  },
306
- // Prospecting — contact discovery
307
- discovered: {
308
- key: "discovered",
309
- label: "Decision-makers found",
310
- description: "Decision-maker contact details have been found.",
311
- order: 5,
312
- entity: "contact"
225
+ {
226
+ id: "leadgen.list",
227
+ order: 30,
228
+ label: "Lead List",
229
+ description: "A prospecting list that groups companies and contacts for acquisition workflows.",
230
+ ownedBySystemId: "sales.lead-gen",
231
+ table: "acq_lists",
232
+ links: [
233
+ { toEntity: "leadgen.company", kind: "has-many", via: "acq_list_companies", label: "companies" },
234
+ { toEntity: "leadgen.contact", kind: "has-many", via: "acq_list_members", label: "contacts" }
235
+ ]
313
236
  },
314
- verified: {
315
- key: "verified",
316
- label: "Emails verified",
317
- description: "Contact email addresses have been checked for deliverability.",
318
- order: 7,
319
- entity: "contact"
237
+ {
238
+ id: "leadgen.company",
239
+ order: 40,
240
+ label: "Lead Company",
241
+ description: "A company record sourced, enriched, and qualified during prospecting.",
242
+ ownedBySystemId: "sales.lead-gen",
243
+ table: "acq_list_companies",
244
+ stateCatalogId: "lead-gen.company",
245
+ links: [
246
+ { toEntity: "leadgen.list", kind: "belongs-to", via: "list_id", label: "list" },
247
+ { toEntity: "leadgen.contact", kind: "has-many", via: "company_id", label: "contacts" }
248
+ ]
320
249
  },
321
- // Qualification
322
- qualified: {
323
- key: "qualified",
324
- label: "Companies qualified",
325
- description: "Companies have been scored against the qualification criteria.",
326
- order: 8,
327
- entity: "company"
250
+ {
251
+ id: "leadgen.contact",
252
+ order: 50,
253
+ label: "Lead Contact",
254
+ description: "A prospect contact discovered or enriched during lead generation.",
255
+ ownedBySystemId: "sales.lead-gen",
256
+ table: "acq_list_members",
257
+ stateCatalogId: "lead-gen.contact",
258
+ links: [
259
+ { toEntity: "leadgen.list", kind: "belongs-to", via: "list_id", label: "list" },
260
+ { toEntity: "leadgen.company", kind: "belongs-to", via: "company_id", label: "company" }
261
+ ]
328
262
  },
329
- // Outreach
330
- personalized: {
331
- key: "personalized",
332
- label: "Personalized",
333
- description: "Outreach message personalized for the contact (Instantly personalization workflow).",
334
- order: 9,
335
- entity: "contact"
263
+ {
264
+ id: "delivery.project",
265
+ order: 60,
266
+ label: "Project",
267
+ description: "A client delivery project.",
268
+ ownedBySystemId: "projects",
269
+ table: "projects",
270
+ links: [
271
+ { toEntity: "delivery.milestone", kind: "has-many", via: "project_id", label: "milestones" },
272
+ { toEntity: "delivery.task", kind: "has-many", via: "project_id", label: "tasks" }
273
+ ]
336
274
  },
337
- uploaded: {
338
- key: "uploaded",
339
- label: "Reviewed and exported",
340
- description: "Approved records have been reviewed and exported for handoff.",
341
- order: 10,
342
- entity: "company",
343
- additionalEntities: ["contact"]
275
+ {
276
+ id: "delivery.milestone",
277
+ order: 70,
278
+ label: "Milestone",
279
+ description: "A delivery checkpoint within a project.",
280
+ ownedBySystemId: "projects",
281
+ table: "project_milestones",
282
+ links: [
283
+ { toEntity: "delivery.project", kind: "belongs-to", via: "project_id", label: "project" },
284
+ { toEntity: "delivery.task", kind: "has-many", via: "milestone_id", label: "tasks" }
285
+ ]
344
286
  },
345
- interested: {
346
- key: "interested",
347
- label: "Interested",
348
- description: "Contact replied with a positive signal (Instantly reply-handler transition).",
349
- order: 11,
350
- entity: "contact"
287
+ {
288
+ id: "delivery.task",
289
+ order: 80,
290
+ label: "Task",
291
+ description: "A delivery task that can move through the task status catalog.",
292
+ ownedBySystemId: "projects",
293
+ table: "project_tasks",
294
+ stateCatalogId: "delivery.task",
295
+ links: [
296
+ { toEntity: "delivery.project", kind: "belongs-to", via: "project_id", label: "project" },
297
+ { toEntity: "delivery.milestone", kind: "belongs-to", via: "milestone_id", label: "milestone" }
298
+ ]
351
299
  }
352
- };
353
- var ProspectingLifecycleStageSchema = DisplayMetadataSchema.extend({
354
- id: ModelIdSchema,
355
- order: z.number().min(0)
300
+ ];
301
+ Object.fromEntries(
302
+ ENTITY_ENTRY_INPUTS.map((entity) => {
303
+ const parsed = EntitySchema.parse(entity);
304
+ return [parsed.id, parsed];
305
+ })
306
+ );
307
+
308
+ // ../core/src/organization-model/domains/actions.ts
309
+ var ActionResourceIdSchema = z.string().trim().min(1).max(255).regex(/^[A-Za-z0-9]+(?:[-._][A-Za-z0-9]+)*$/, "Resource IDs must use letters, numbers, -, _, or . separators");
310
+ z.enum(["slash-command", "mcp-tool", "api-endpoint", "script-execution"]).meta({ label: "Invocation kind" });
311
+ var ActionIdSchema = ModelIdSchema;
312
+ var ActionScopeSchema = z.union([
313
+ z.literal("global"),
314
+ z.object({
315
+ domain: ModelIdSchema
316
+ })
317
+ ]);
318
+ z.object({
319
+ actionId: ActionIdSchema.meta({ ref: "action" }),
320
+ intent: z.enum(["exposes", "consumes"]).meta({ label: "Intent" })
356
321
  });
357
- var RecordColumnConfigSchema = z.object({
358
- key: ModelIdSchema,
359
- label: z.string().trim().min(1).max(120),
360
- path: z.string().trim().min(1).max(500),
361
- width: z.union([z.number().positive(), z.string().trim().min(1).max(100)]).optional(),
362
- renderType: z.enum(["text", "badge", "datetime", "count", "json"]).optional(),
363
- badgeColor: z.string().trim().min(1).max(40).optional()
322
+ var SlashCommandInvocationSchema = z.object({
323
+ kind: z.literal("slash-command"),
324
+ command: z.string().trim().min(1).max(200).regex(/^\/[^\s].*$/, "Slash commands must start with /"),
325
+ toolFactory: ModelIdSchema.optional()
364
326
  });
365
- var RecordColumnsConfigSchema = z.object({
366
- company: z.array(RecordColumnConfigSchema).optional(),
367
- contact: z.array(RecordColumnConfigSchema).optional()
368
- }).refine((columns) => Boolean(columns.company?.length || columns.contact?.length), {
369
- message: "recordColumns must include at least one entity column set"
327
+ var McpToolInvocationSchema = z.object({
328
+ kind: z.literal("mcp-tool"),
329
+ server: ModelIdSchema,
330
+ name: ModelIdSchema
370
331
  });
371
- var CredentialRequirementSchema = z.object({
372
- key: ModelIdSchema,
373
- provider: ModelIdSchema,
374
- credentialType: z.enum(["api-key", "api-key-secret", "oauth", "webhook-secret"]),
375
- label: z.string().trim().min(1).max(120),
376
- required: z.boolean(),
377
- selectionMode: z.enum(["single", "multiple"]).optional(),
378
- inputPath: z.string().trim().min(1).max(500),
379
- verifyOnRun: z.boolean().optional()
332
+ var ApiEndpointInvocationSchema = z.object({
333
+ kind: z.literal("api-endpoint"),
334
+ method: z.enum(["GET", "POST", "PATCH", "DELETE"]).meta({ label: "HTTP method" }),
335
+ path: z.string().trim().startsWith("/").max(500),
336
+ requestSchema: ModelIdSchema.optional(),
337
+ responseSchema: ModelIdSchema.optional()
380
338
  });
381
- var ProspectingBuildTemplateStepSchema = DisplayMetadataSchema.extend({
382
- id: ModelIdSchema,
383
- primaryEntity: z.enum(["company", "contact"]),
384
- outputs: z.array(z.enum(["company", "contact", "export"])).min(1),
385
- stageKey: ModelIdSchema,
386
- recordEntity: z.enum(["company", "contact"]).optional(),
387
- recordsStageKey: ModelIdSchema.optional(),
388
- recordSourceStageKey: ModelIdSchema.optional(),
389
- dependsOn: z.array(ModelIdSchema).optional(),
390
- dependencyMode: z.literal("per-record-eligibility"),
391
- capabilityKey: ModelIdSchema,
392
- defaultBatchSize: z.number().int().positive(),
393
- maxBatchSize: z.number().int().positive(),
394
- recordColumns: RecordColumnsConfigSchema.optional(),
395
- credentialRequirements: z.array(CredentialRequirementSchema).optional()
396
- }).refine((step) => step.defaultBatchSize <= step.maxBatchSize, {
397
- message: "defaultBatchSize must be less than or equal to maxBatchSize",
398
- path: ["defaultBatchSize"]
339
+ var ScriptExecutionInvocationSchema = z.object({
340
+ kind: z.literal("script-execution"),
341
+ resourceId: ActionResourceIdSchema
399
342
  });
400
- var ProspectingBuildTemplateSchema = DisplayMetadataSchema.extend({
401
- id: ModelIdSchema,
402
- steps: z.array(ProspectingBuildTemplateStepSchema).min(1)
343
+ var ActionInvocationSchema = z.discriminatedUnion("kind", [
344
+ SlashCommandInvocationSchema,
345
+ McpToolInvocationSchema,
346
+ ApiEndpointInvocationSchema,
347
+ ScriptExecutionInvocationSchema
348
+ ]);
349
+ var ActionSchema = z.object({
350
+ id: ActionIdSchema,
351
+ /** Domain-map iteration order. Convention: multiples of 10 (10, 20, 30, ...) to allow easy insertion. */
352
+ order: z.number(),
353
+ label: LabelSchema,
354
+ description: DescriptionSchema.optional(),
355
+ scope: ActionScopeSchema.default("global"),
356
+ resourceId: ActionResourceIdSchema.optional(),
357
+ affects: z.array(EntityIdSchema.meta({ ref: "entity" })).optional(),
358
+ invocations: z.array(ActionInvocationSchema).default([]),
359
+ knowledge: z.array(ModelIdSchema.meta({ ref: "knowledge" })).default([]).optional(),
360
+ lifecycle: z.enum(["draft", "beta", "active", "deprecated", "archived"]).meta({ label: "Lifecycle", color: "teal" }).default("active")
403
361
  });
404
- var DTC_RECORD_COLUMNS = {
405
- populated: {
406
- company: [
407
- { key: "name", label: "Company", path: "company.name" },
408
- { key: "domain", label: "Domain", path: "company.domain" },
409
- { key: "employee-count", label: "Employees", path: "company.numEmployees", renderType: "count" },
410
- { key: "apollo-industry", label: "Apollo industry", path: "company.category" },
411
- { key: "location", label: "Location", path: "company.locationState" }
412
- ]
362
+ z.record(z.string(), ActionSchema).refine((record) => Object.entries(record).every(([key, entry]) => entry.id === key), {
363
+ message: "Each action entry id must match its map key"
364
+ }).default({});
365
+ var LEAD_GEN_ACTION_ENTRY_INPUTS = [
366
+ {
367
+ id: "lead-gen.company.source",
368
+ order: 10,
369
+ label: "Source companies",
370
+ description: "Import source companies from a list provider.",
371
+ scope: { domain: "sales" },
372
+ resourceId: "lgn-import-workflow",
373
+ invocations: [{ kind: "api-endpoint", method: "POST", path: "/api/prospecting/companies/source" }]
413
374
  },
414
- crawled: {
415
- company: [
416
- { key: "name", label: "Company", path: "company.name" },
417
- { key: "domain", label: "Domain", path: "company.domain" },
418
- { key: "page-count", label: "Pages", path: "company.enrichmentData.websiteCrawl.pageCount", renderType: "count" },
419
- { key: "crawl-status", label: "Crawl status", path: "processingState.crawled.status", renderType: "badge" }
420
- ]
421
- },
422
- extracted: {
423
- company: [
424
- { key: "name", label: "Company", path: "company.name" },
425
- { key: "domain", label: "Domain", path: "company.domain" },
426
- { key: "description", label: "Description", path: "company.enrichmentData.websiteCrawl.companyDescription" },
427
- { key: "services", label: "Services", path: "company.enrichmentData.websiteCrawl.services", renderType: "json" },
428
- { key: "automation-gaps", label: "Automation gaps", path: "company.enrichmentData.websiteCrawl.automationGaps", renderType: "json" },
429
- { key: "contact-count", label: "Contacts", path: "company.enrichmentData.websiteCrawl.emailCount", renderType: "count" }
430
- ]
431
- },
432
- qualified: {
433
- company: [
434
- { key: "name", label: "Company", path: "company.name" },
435
- { key: "domain", label: "Domain", path: "company.domain" },
436
- { key: "score", label: "Score", path: "company.qualificationScore", renderType: "badge", badgeColor: "green" },
437
- { key: "signals", label: "Signals", path: "company.qualificationSignals", renderType: "json" },
438
- { key: "disqualified-reason", label: "Disqualified reason", path: "processingState.qualified.data.disqualifiedReason" }
439
- ]
440
- },
441
- decisionMakers: {
442
- contact: [
443
- { key: "name", label: "Name", path: "contact.name" },
444
- { key: "title", label: "Title", path: "contact.title" },
445
- { key: "email", label: "Email", path: "contact.email" },
446
- { key: "linkedin", label: "LinkedIn", path: "contact.linkedinUrl" },
447
- { key: "priority-score", label: "Priority", path: "contact.enrichmentData.apollo.priorityScore", renderType: "badge" }
448
- ]
449
- },
450
- uploaded: {
451
- company: [
452
- { key: "name", label: "Company", path: "company.name" },
453
- { key: "domain", label: "Domain", path: "company.domain" },
454
- { key: "contacts", label: "Contacts", path: "company.enrichmentData.approvedLeadListExport.contacts", renderType: "json" },
455
- { key: "score", label: "Score", path: "company.qualificationScore", renderType: "badge", badgeColor: "green" },
456
- { key: "approval", label: "Approval", path: "company.enrichmentData.approvedLeadListExport.approvalStatus", renderType: "badge" }
457
- ]
458
- }
459
- };
460
- z.object({
461
- id: ModelIdSchema,
462
- label: z.string(),
463
- description: z.string(),
464
- resourceId: ModelIdSchema
465
- });
466
- var CAPABILITY_REGISTRY = [
467
- {
468
- id: "lead-gen.company.source",
469
- label: "Source companies",
470
- description: "Import source companies from a list provider.",
471
- resourceId: "lgn-import-workflow"
472
- },
473
- {
474
- id: "lead-gen.company.apollo-import",
475
- label: "Import from Apollo",
476
- description: "Pull companies and seed contact data from an Apollo search or list.",
477
- resourceId: "lgn-01c-apollo-import-workflow"
375
+ {
376
+ id: "lead-gen.company.apollo-import",
377
+ order: 20,
378
+ label: "Import from Apollo",
379
+ description: "Pull companies and seed contact data from an Apollo search or list.",
380
+ scope: { domain: "sales" },
381
+ resourceId: "lgn-01c-apollo-import-workflow",
382
+ invocations: [{ kind: "api-endpoint", method: "POST", path: "/api/prospecting/companies/apollo-import" }]
478
383
  },
479
384
  {
480
385
  id: "lead-gen.contact.discover",
386
+ order: 30,
481
387
  label: "Discover contact emails",
482
388
  description: "Find email addresses for contacts at qualified companies.",
483
- resourceId: "lgn-04-email-discovery-workflow"
389
+ scope: { domain: "sales" },
390
+ resourceId: "lgn-04-email-discovery-workflow",
391
+ invocations: [{ kind: "api-endpoint", method: "POST", path: "/api/prospecting/contacts/discover" }]
484
392
  },
485
393
  {
486
394
  id: "lead-gen.contact.verify-email",
395
+ order: 40,
487
396
  label: "Verify emails",
488
397
  description: "Check email deliverability before outreach.",
489
- resourceId: "lgn-05-email-verification-workflow"
398
+ scope: { domain: "sales" },
399
+ resourceId: "lgn-05-email-verification-workflow",
400
+ invocations: [{ kind: "api-endpoint", method: "POST", path: "/api/prospecting/contacts/verify-email" }]
490
401
  },
491
402
  {
492
403
  id: "lead-gen.company.apify-crawl",
404
+ order: 50,
493
405
  label: "Crawl websites",
494
406
  description: "Crawl company websites via Apify and store raw page markdown in enrichmentData.websiteCrawl.pages for downstream LLM analysis.",
495
- resourceId: "lgn-02a-apify-website-crawl-workflow"
407
+ scope: { domain: "sales" },
408
+ resourceId: "lgn-02a-apify-website-crawl-workflow",
409
+ invocations: [{ kind: "api-endpoint", method: "POST", path: "/api/prospecting/companies/apify-crawl" }]
496
410
  },
497
411
  {
498
412
  id: "lead-gen.company.website-extract",
413
+ order: 60,
499
414
  label: "Extract website signals",
500
415
  description: "Scrape and analyze company websites for qualification signals.",
501
- resourceId: "lgn-02-website-extract-workflow"
416
+ scope: { domain: "sales" },
417
+ resourceId: "lgn-02-website-extract-workflow",
418
+ invocations: [{ kind: "api-endpoint", method: "POST", path: "/api/prospecting/companies/website-extract" }]
502
419
  },
503
420
  {
504
421
  id: "lead-gen.company.qualify",
422
+ order: 70,
505
423
  label: "Qualify companies",
506
424
  description: "Score and filter companies against the ICP rubric.",
507
- resourceId: "lgn-03-company-qualification-workflow"
425
+ scope: { domain: "sales" },
426
+ resourceId: "lgn-03-company-qualification-workflow",
427
+ invocations: [{ kind: "api-endpoint", method: "POST", path: "/api/prospecting/companies/qualify" }]
508
428
  },
509
429
  {
510
430
  id: "lead-gen.company.dtc-subscription-qualify",
431
+ order: 80,
511
432
  label: "Qualify DTC subscription fit",
512
433
  description: "Classify subscription potential and consumable-product fit for DTC brands.",
513
- resourceId: "lgn-03b-dtc-subscription-score-workflow"
434
+ scope: { domain: "sales" },
435
+ resourceId: "lgn-03b-dtc-subscription-score-workflow",
436
+ invocations: [{ kind: "api-endpoint", method: "POST", path: "/api/prospecting/companies/dtc-subscription-qualify" }]
514
437
  },
515
438
  {
516
439
  id: "lead-gen.contact.apollo-decision-maker-enrich",
440
+ order: 90,
517
441
  label: "Enrich decision-makers",
518
442
  description: "Find and enrich qualified contacts at qualified companies via Apollo.",
519
- resourceId: "lgn-04b-apollo-decision-maker-enrich-workflow"
443
+ scope: { domain: "sales" },
444
+ resourceId: "lgn-04b-apollo-decision-maker-enrich-workflow",
445
+ invocations: [
446
+ { kind: "api-endpoint", method: "POST", path: "/api/prospecting/contacts/apollo-decision-maker-enrich" }
447
+ ]
520
448
  },
521
449
  {
522
450
  id: "lead-gen.contact.personalize",
451
+ order: 100,
523
452
  label: "Personalize outreach",
524
453
  description: "Generate personalized opening lines for each contact.",
525
- resourceId: "ist-personalization-workflow"
454
+ scope: { domain: "sales" },
455
+ resourceId: "ist-personalization-workflow",
456
+ invocations: [{ kind: "api-endpoint", method: "POST", path: "/api/prospecting/contacts/personalize" }]
526
457
  },
527
458
  {
528
459
  id: "lead-gen.review.outreach-ready",
460
+ order: 110,
529
461
  label: "Upload to outreach",
530
462
  description: "Upload approved contacts to the outreach sequence after QC review.",
531
- resourceId: "ist-upload-contacts-workflow"
463
+ scope: { domain: "sales" },
464
+ resourceId: "ist-upload-contacts-workflow",
465
+ invocations: [{ kind: "api-endpoint", method: "POST", path: "/api/prospecting/review/outreach-ready" }]
532
466
  },
533
467
  {
534
468
  id: "lead-gen.export.list",
469
+ order: 120,
535
470
  label: "Export lead list",
536
471
  description: "Export approved leads as a downloadable lead list.",
537
- resourceId: "lgn-06-export-list-workflow"
472
+ scope: { domain: "sales" },
473
+ resourceId: "lgn-06-export-list-workflow",
474
+ invocations: [{ kind: "api-endpoint", method: "POST", path: "/api/prospecting/export/list" }]
538
475
  },
539
476
  {
540
477
  id: "lead-gen.company.cleanup",
478
+ order: 130,
541
479
  label: "Clean up companies",
542
480
  description: "Remove disqualified or duplicate companies from the list.",
543
- resourceId: "lgn-company-cleanup-workflow"
481
+ scope: { domain: "sales" },
482
+ resourceId: "lgn-company-cleanup-workflow",
483
+ invocations: [{ kind: "api-endpoint", method: "POST", path: "/api/prospecting/companies/cleanup" }]
544
484
  }
545
485
  ];
546
- var PROSPECTING_STEPS = {
547
- localServices: {
548
- sourceCompanies: {
549
- id: "source-companies",
550
- label: "Companies found",
551
- primaryEntity: "company",
552
- outputs: ["company"],
553
- stageKey: "populated",
554
- dependencyMode: "per-record-eligibility",
555
- capabilityKey: "lead-gen.company.source",
556
- defaultBatchSize: 100,
557
- maxBatchSize: 250
558
- },
559
- analyzeWebsites: {
560
- id: "analyze-websites",
561
- label: "Websites analyzed",
562
- primaryEntity: "company",
563
- outputs: ["company"],
564
- stageKey: "extracted",
565
- dependsOn: ["source-companies"],
566
- dependencyMode: "per-record-eligibility",
567
- capabilityKey: "lead-gen.company.website-extract",
568
- defaultBatchSize: 50,
569
- maxBatchSize: 100
570
- },
571
- qualifyCompanies: {
572
- id: "qualify-companies",
573
- label: "Companies qualified",
574
- primaryEntity: "company",
575
- outputs: ["company"],
576
- stageKey: "qualified",
577
- dependsOn: ["analyze-websites"],
578
- dependencyMode: "per-record-eligibility",
579
- capabilityKey: "lead-gen.company.qualify",
580
- defaultBatchSize: 100,
581
- maxBatchSize: 250
582
- },
583
- findContacts: {
584
- id: "find-contacts",
585
- label: "Decision-makers found",
586
- primaryEntity: "contact",
587
- outputs: ["contact"],
588
- stageKey: "discovered",
589
- dependsOn: ["qualify-companies"],
590
- dependencyMode: "per-record-eligibility",
591
- capabilityKey: "lead-gen.contact.discover",
592
- defaultBatchSize: 50,
593
- maxBatchSize: 100
594
- },
595
- verifyEmails: {
596
- id: "verify-emails",
597
- label: "Emails verified",
598
- primaryEntity: "contact",
599
- outputs: ["contact"],
600
- stageKey: "verified",
601
- dependsOn: ["find-contacts"],
602
- dependencyMode: "per-record-eligibility",
603
- capabilityKey: "lead-gen.contact.verify-email",
604
- defaultBatchSize: 100,
605
- maxBatchSize: 500
606
- },
607
- personalize: {
608
- id: "personalize",
609
- label: "Personalize",
610
- primaryEntity: "contact",
611
- outputs: ["contact"],
612
- stageKey: "personalized",
613
- dependsOn: ["verify-emails"],
614
- dependencyMode: "per-record-eligibility",
615
- capabilityKey: "lead-gen.contact.personalize",
616
- defaultBatchSize: 25,
617
- maxBatchSize: 100
618
- },
619
- review: {
620
- id: "review",
621
- label: "Reviewed and exported",
622
- primaryEntity: "contact",
623
- outputs: ["export"],
624
- stageKey: "uploaded",
625
- dependsOn: ["personalize"],
626
- dependencyMode: "per-record-eligibility",
627
- capabilityKey: "lead-gen.review.outreach-ready",
628
- defaultBatchSize: 25,
629
- maxBatchSize: 100
486
+ var LEAD_GEN_ACTION_ENTRIES = Object.fromEntries(
487
+ LEAD_GEN_ACTION_ENTRY_INPUTS.map((action) => {
488
+ const parsed = ActionSchema.parse(action);
489
+ return [parsed.id, parsed];
490
+ })
491
+ );
492
+
493
+ // ../core/src/platform/registry/reserved.ts
494
+ var RESERVED_RESOURCE_IDS = /* @__PURE__ */ new Set(["command-center-assistant"]);
495
+ Array.from(RESERVED_RESOURCE_IDS);
496
+ function isReservedResourceId(resourceId) {
497
+ return RESERVED_RESOURCE_IDS.has(resourceId);
498
+ }
499
+
500
+ // ../core/src/organization-model/helpers.ts
501
+ function listAllSystems(model) {
502
+ const results = [];
503
+ function walk(map, prefix) {
504
+ for (const [localId, system] of Object.entries(map)) {
505
+ const fullPath = prefix ? `${prefix}.${localId}` : localId;
506
+ results.push({ path: fullPath, system });
507
+ if (system.subsystems) {
508
+ walk(system.subsystems, fullPath);
509
+ }
510
+ }
511
+ }
512
+ walk(model.systems, "");
513
+ return results;
514
+ }
515
+
516
+ // ../core/src/execution/engine/base/errors.ts
517
+ var ExecutionError2 = class extends Error {
518
+ /**
519
+ * Additional context/metadata for the error.
520
+ * Stored in execution_errors.metadata JSONB column.
521
+ */
522
+ context;
523
+ /**
524
+ * @param message - Human-readable error message
525
+ * @param context - Additional context/metadata for observability
526
+ */
527
+ constructor(message, context) {
528
+ super(message);
529
+ this.name = this.constructor.name;
530
+ this.context = context;
531
+ if (Error.captureStackTrace) {
532
+ Error.captureStackTrace(this, this.constructor);
630
533
  }
631
- },
632
- dtcApolloClickup: {
633
- importApolloSearch: {
634
- id: "import-apollo-search",
635
- label: "Companies found",
636
- description: "Pull companies and seed contact data from a predefined Apollo search or list.",
637
- primaryEntity: "company",
638
- outputs: ["company", "contact"],
639
- stageKey: "populated",
640
- dependencyMode: "per-record-eligibility",
641
- capabilityKey: "lead-gen.company.apollo-import",
642
- defaultBatchSize: 250,
643
- maxBatchSize: 1e3,
644
- recordColumns: DTC_RECORD_COLUMNS.populated,
645
- credentialRequirements: [
646
- {
647
- key: "apollo",
648
- provider: "apollo",
649
- credentialType: "api-key-secret",
650
- label: "Apollo API key",
651
- required: true,
652
- selectionMode: "single",
653
- inputPath: "credential"
654
- }
655
- ]
656
- },
657
- apifyCrawl: {
658
- id: "apify-crawl",
659
- label: "Websites crawled",
660
- description: "Crawl company websites via Apify and store raw page markdown in enrichmentData.websiteCrawl.pages for downstream LLM analysis. Overwrites the synthetic seed Apollo Import wrote with real page content.",
661
- primaryEntity: "company",
662
- outputs: ["company"],
663
- stageKey: "crawled",
664
- dependsOn: ["import-apollo-search"],
665
- dependencyMode: "per-record-eligibility",
666
- capabilityKey: "lead-gen.company.apify-crawl",
667
- defaultBatchSize: 50,
668
- maxBatchSize: 100,
669
- recordColumns: DTC_RECORD_COLUMNS.crawled,
670
- credentialRequirements: [
671
- {
672
- key: "apify",
673
- provider: "apify",
674
- credentialType: "api-key-secret",
675
- label: "Apify API token",
676
- required: true,
677
- selectionMode: "single",
678
- inputPath: "credential",
679
- verifyOnRun: true
680
- }
681
- ]
682
- },
683
- analyzeWebsites: {
684
- id: "analyze-websites",
685
- label: "Websites analyzed",
686
- description: "Extract subscription, product, retention, and tech-stack signals from each brand website.",
687
- primaryEntity: "company",
688
- outputs: ["company"],
689
- stageKey: "extracted",
690
- dependsOn: ["apify-crawl"],
691
- dependencyMode: "per-record-eligibility",
692
- capabilityKey: "lead-gen.company.website-extract",
693
- defaultBatchSize: 50,
694
- maxBatchSize: 100,
695
- recordColumns: DTC_RECORD_COLUMNS.extracted
696
- },
697
- scoreDtcFit: {
698
- id: "score-dtc-fit",
699
- label: "Companies qualified",
700
- description: "Classify subscription potential, consumable-product fit, retention maturity, and disqualifiers.",
701
- primaryEntity: "company",
702
- outputs: ["company"],
703
- stageKey: "qualified",
704
- dependsOn: ["analyze-websites"],
705
- dependencyMode: "per-record-eligibility",
706
- capabilityKey: "lead-gen.company.dtc-subscription-qualify",
707
- defaultBatchSize: 100,
708
- maxBatchSize: 250,
709
- recordColumns: DTC_RECORD_COLUMNS.qualified
710
- },
711
- enrichDecisionMakers: {
712
- id: "enrich-decision-makers",
713
- label: "Decision-makers found",
714
- description: "Use Apollo to find qualified contacts at qualified companies - founders, retention leads, lifecycle leads, and marketing owners.",
715
- primaryEntity: "company",
716
- outputs: ["contact"],
717
- stageKey: "decision-makers-enriched",
718
- recordEntity: "contact",
719
- dependsOn: ["score-dtc-fit"],
720
- dependencyMode: "per-record-eligibility",
721
- capabilityKey: "lead-gen.contact.apollo-decision-maker-enrich",
722
- defaultBatchSize: 100,
723
- maxBatchSize: 250,
724
- recordColumns: DTC_RECORD_COLUMNS.decisionMakers,
725
- credentialRequirements: [
726
- {
727
- key: "apollo",
728
- provider: "apollo",
729
- credentialType: "api-key-secret",
730
- label: "Apollo API key",
731
- required: true,
732
- selectionMode: "single",
733
- inputPath: "credential"
734
- }
735
- ]
736
- },
737
- reviewAndExport: {
738
- id: "review-and-export",
739
- label: "Reviewed and exported",
740
- description: "Operator QC approves or rejects qualified companies, then approved records are exported as a lead list with unverified emails.",
741
- primaryEntity: "company",
742
- outputs: ["export"],
743
- stageKey: "uploaded",
744
- recordsStageKey: "uploaded",
745
- recordSourceStageKey: "qualified",
746
- dependsOn: ["enrich-decision-makers"],
747
- dependencyMode: "per-record-eligibility",
748
- capabilityKey: "lead-gen.export.list",
749
- defaultBatchSize: 100,
750
- maxBatchSize: 250,
751
- recordColumns: DTC_RECORD_COLUMNS.uploaded,
752
- credentialRequirements: [
753
- {
754
- key: "clickup",
755
- provider: "clickup",
756
- credentialType: "api-key-secret",
757
- label: "ClickUp API token",
758
- required: true,
759
- selectionMode: "single",
760
- inputPath: "clickupCredential",
761
- verifyOnRun: true
762
- }
763
- ]
764
- }
765
- }
766
- };
767
- z.object({
768
- listEntityId: ModelIdSchema,
769
- companyEntityId: ModelIdSchema,
770
- contactEntityId: ModelIdSchema,
771
- description: DescriptionSchema.optional(),
772
- companyStages: z.array(ProspectingLifecycleStageSchema).min(1),
773
- contactStages: z.array(ProspectingLifecycleStageSchema).min(1),
774
- defaultBuildTemplateId: ModelIdSchema,
775
- buildTemplates: z.array(ProspectingBuildTemplateSchema).min(1)
776
- });
777
- function toProspectingLifecycleStage(stage) {
778
- return {
779
- id: stage.key,
780
- label: stage.label,
781
- order: stage.order
782
- };
783
- }
784
- function leadGenStagesForEntity(entity) {
785
- return Object.values(LEAD_GEN_STAGE_CATALOG).filter((stage) => stage.entity === entity || stage.additionalEntities?.includes(entity)).sort((a, b) => a.order - b.order).map(toProspectingLifecycleStage);
786
- }
787
- var DEFAULT_ORGANIZATION_MODEL_PROSPECTING = {
788
- companyStages: leadGenStagesForEntity("company"),
789
- contactStages: leadGenStagesForEntity("contact"),
790
- buildTemplates: [
791
- {
792
- id: "local-services",
793
- label: "Local Services Prospecting",
794
- description: "Curated local-services list build using company sourcing, website analysis, qualification, contact discovery, verification, personalization, and review.",
795
- steps: [
796
- PROSPECTING_STEPS.localServices.sourceCompanies,
797
- PROSPECTING_STEPS.localServices.analyzeWebsites,
798
- PROSPECTING_STEPS.localServices.qualifyCompanies,
799
- PROSPECTING_STEPS.localServices.findContacts,
800
- PROSPECTING_STEPS.localServices.verifyEmails,
801
- PROSPECTING_STEPS.localServices.personalize,
802
- PROSPECTING_STEPS.localServices.review
803
- ]
804
- },
805
- {
806
- id: "dtc-subscription-apollo-clickup",
807
- label: "DTC Subscription Apollo Export",
808
- description: "Prospecting pipeline for DTC subscription or subscription-ready brands where Apollo is the source and contact-enrichment layer, Elevasis handles company research and fit scoring, and approved leads export as an approved lead list.",
809
- steps: [
810
- PROSPECTING_STEPS.dtcApolloClickup.importApolloSearch,
811
- PROSPECTING_STEPS.dtcApolloClickup.apifyCrawl,
812
- PROSPECTING_STEPS.dtcApolloClickup.analyzeWebsites,
813
- PROSPECTING_STEPS.dtcApolloClickup.scoreDtcFit,
814
- PROSPECTING_STEPS.dtcApolloClickup.enrichDecisionMakers,
815
- PROSPECTING_STEPS.dtcApolloClickup.reviewAndExport
816
- ]
817
- }
818
- ]
819
- };
820
-
821
- // ../core/src/platform/registry/reserved.ts
822
- var RESERVED_RESOURCE_IDS = /* @__PURE__ */ new Set(["command-center-assistant"]);
823
- Array.from(RESERVED_RESOURCE_IDS);
824
- function isReservedResourceId(resourceId) {
825
- return RESERVED_RESOURCE_IDS.has(resourceId);
826
- }
827
-
828
- // ../core/src/execution/engine/base/errors.ts
829
- var ExecutionError2 = class extends Error {
830
- /**
831
- * Additional context/metadata for the error.
832
- * Stored in execution_errors.metadata JSONB column.
833
- */
834
- context;
835
- /**
836
- * @param message - Human-readable error message
837
- * @param context - Additional context/metadata for observability
838
- */
839
- constructor(message, context) {
840
- super(message);
841
- this.name = this.constructor.name;
842
- this.context = context;
843
- if (Error.captureStackTrace) {
844
- Error.captureStackTrace(this, this.constructor);
845
- }
846
- }
847
- /**
848
- * Indicates whether this error type is retryable.
849
- * Default: false (safe default - only retry when explicitly safe to do so)
850
- *
851
- * Subclasses should override to return true for retryable scenarios:
852
- * - Network/infrastructure errors (exponential backoff)
853
- * - Rate limiting (linear backoff)
854
- * - Service availability (exponential backoff)
855
- * - Circuit breaker (circuit breaker's own delay)
856
- *
857
- * DO NOT retry:
858
- * - Authentication/authorization errors
859
- * - Validation errors
860
- * - Configuration errors
861
- * - Resource exhaustion errors
862
- */
863
- isRetryable() {
864
- return false;
865
- }
866
- };
867
- var GPT5OptionsSchema = z.object({
868
- reasoning_effort: z.enum(["minimal", "low", "medium", "high"]).optional(),
869
- verbosity: z.enum(["low", "medium", "high"]).optional()
870
- });
871
- var GPT5ConfigSchema = z.object({
872
- model: z.enum(["gpt-5", "gpt-5.4-mini", "gpt-5.4-nano"]),
873
- provider: z.enum(["openai"]),
874
- apiKey: z.string(),
875
- temperature: z.literal(1),
876
- // Required to be exactly 1
877
- maxOutputTokens: z.number().min(4e3).optional(),
878
- topP: z.number().min(0).max(1).optional(),
879
- modelOptions: GPT5OptionsSchema.optional()
880
- });
881
- var MockConfigSchema = z.object({
882
- model: z.enum(["mock"]),
883
- provider: z.enum(["mock"]),
884
- apiKey: z.string(),
885
- temperature: z.number().min(0).max(2).optional(),
886
- maxOutputTokens: z.number().min(500).optional(),
887
- topP: z.number().min(0).max(1).optional(),
888
- modelOptions: z.object({}).strict().optional()
889
- // No options supported
890
- });
891
- var OpenRouterOptionsSchema = z.object({
892
- /** Optional transforms to apply (e.g., 'middle-out' for long context) */
893
- transforms: z.array(z.string()).optional(),
894
- /** Routing strategy (e.g., 'fallback' for automatic provider failover) */
895
- route: z.enum(["fallback"]).optional()
896
- });
897
- var OpenRouterConfigSchema = z.object({
898
- model: z.enum(["openrouter/z-ai/glm-5"]),
899
- provider: z.literal("openrouter"),
900
- apiKey: z.string(),
901
- temperature: z.number().min(0).max(2).optional(),
902
- maxOutputTokens: z.number().min(500).optional(),
903
- topP: z.number().min(0).max(1).optional(),
904
- modelOptions: OpenRouterOptionsSchema.optional()
905
- });
906
- var GoogleOptionsSchema = z.object({
907
- /** Thinking level for Gemini 3 models (controls reasoning depth) */
908
- thinkingLevel: z.enum(["minimal", "low", "medium", "high"]).optional()
909
- });
910
- var GoogleConfigSchema = z.object({
911
- model: z.enum(["gemini-3-flash-preview", "gemini-3.1-flash-lite-preview"]),
912
- provider: z.literal("google"),
913
- apiKey: z.string(),
914
- temperature: z.number().min(0).max(2).optional(),
915
- maxOutputTokens: z.number().min(500).optional(),
916
- topP: z.number().min(0).max(1).optional(),
917
- modelOptions: GoogleOptionsSchema.optional()
918
- });
919
- var AnthropicOptionsSchema = z.object({});
920
- var AnthropicConfigSchema = z.object({
921
- model: z.enum(["claude-sonnet-4-5"]),
922
- provider: z.literal("anthropic"),
923
- apiKey: z.string(),
924
- temperature: z.number().min(0).max(1).optional(),
925
- maxOutputTokens: z.number().min(1e3).optional(),
926
- // Anthropic requires max_tokens
927
- topP: z.number().min(0).max(1).optional(),
928
- modelOptions: AnthropicOptionsSchema.optional()
929
- });
930
- var MODEL_INFO = {
931
- // OpenAI GPT-5 (Reasoning Models)
932
- "gpt-5": {
933
- inputCostPer1M: 125,
934
- // $1.25 per 1M tokens
935
- outputCostPer1M: 1e3,
936
- // $10.00 per 1M tokens
937
- minTokens: 4e3,
938
- // Reasoning models need more
939
- recommendedTokens: 8e3,
940
- maxTokens: 4e5,
941
- // 400k context window
942
- category: "reasoning",
943
- configSchema: GPT5ConfigSchema
534
+ }
535
+ /**
536
+ * Indicates whether this error type is retryable.
537
+ * Default: false (safe default - only retry when explicitly safe to do so)
538
+ *
539
+ * Subclasses should override to return true for retryable scenarios:
540
+ * - Network/infrastructure errors (exponential backoff)
541
+ * - Rate limiting (linear backoff)
542
+ * - Service availability (exponential backoff)
543
+ * - Circuit breaker (circuit breaker's own delay)
544
+ *
545
+ * DO NOT retry:
546
+ * - Authentication/authorization errors
547
+ * - Validation errors
548
+ * - Configuration errors
549
+ * - Resource exhaustion errors
550
+ */
551
+ isRetryable() {
552
+ return false;
553
+ }
554
+ };
555
+ var GPT5OptionsSchema = z.object({
556
+ reasoning_effort: z.enum(["minimal", "low", "medium", "high"]).optional(),
557
+ verbosity: z.enum(["low", "medium", "high"]).optional()
558
+ });
559
+ var GPT5ConfigSchema = z.object({
560
+ model: z.enum(["gpt-5", "gpt-5.4-mini", "gpt-5.4-nano"]),
561
+ provider: z.enum(["openai"]),
562
+ apiKey: z.string(),
563
+ temperature: z.literal(1),
564
+ // Required to be exactly 1
565
+ maxOutputTokens: z.number().min(4e3).optional(),
566
+ topP: z.number().min(0).max(1).optional(),
567
+ modelOptions: GPT5OptionsSchema.optional()
568
+ });
569
+ var MockConfigSchema = z.object({
570
+ model: z.enum(["mock"]),
571
+ provider: z.enum(["mock"]),
572
+ apiKey: z.string(),
573
+ temperature: z.number().min(0).max(2).optional(),
574
+ maxOutputTokens: z.number().min(500).optional(),
575
+ topP: z.number().min(0).max(1).optional(),
576
+ modelOptions: z.object({}).strict().optional()
577
+ // No options supported
578
+ });
579
+ var OpenRouterOptionsSchema = z.object({
580
+ /** Optional transforms to apply (e.g., 'middle-out' for long context) */
581
+ transforms: z.array(z.string()).optional(),
582
+ /** Routing strategy (e.g., 'fallback' for automatic provider failover) */
583
+ route: z.enum(["fallback"]).optional()
584
+ });
585
+ var OpenRouterConfigSchema = z.object({
586
+ model: z.enum(["openrouter/z-ai/glm-5"]),
587
+ provider: z.literal("openrouter"),
588
+ apiKey: z.string(),
589
+ temperature: z.number().min(0).max(2).optional(),
590
+ maxOutputTokens: z.number().min(500).optional(),
591
+ topP: z.number().min(0).max(1).optional(),
592
+ modelOptions: OpenRouterOptionsSchema.optional()
593
+ });
594
+ var GoogleOptionsSchema = z.object({
595
+ /** Thinking level for Gemini 3 models (controls reasoning depth) */
596
+ thinkingLevel: z.enum(["minimal", "low", "medium", "high"]).optional()
597
+ });
598
+ var GoogleConfigSchema = z.object({
599
+ model: z.enum(["gemini-3-flash-preview", "gemini-3.1-flash-lite-preview"]),
600
+ provider: z.literal("google"),
601
+ apiKey: z.string(),
602
+ temperature: z.number().min(0).max(2).optional(),
603
+ maxOutputTokens: z.number().min(500).optional(),
604
+ topP: z.number().min(0).max(1).optional(),
605
+ modelOptions: GoogleOptionsSchema.optional()
606
+ });
607
+ var AnthropicOptionsSchema = z.object({});
608
+ var AnthropicConfigSchema = z.object({
609
+ model: z.enum(["claude-sonnet-4-5"]),
610
+ provider: z.literal("anthropic"),
611
+ apiKey: z.string(),
612
+ temperature: z.number().min(0).max(1).optional(),
613
+ maxOutputTokens: z.number().min(1e3).optional(),
614
+ // Anthropic requires max_tokens
615
+ topP: z.number().min(0).max(1).optional(),
616
+ modelOptions: AnthropicOptionsSchema.optional()
617
+ });
618
+ var MODEL_INFO = {
619
+ // OpenAI GPT-5 (Reasoning Models)
620
+ "gpt-5": {
621
+ inputCostPer1M: 125,
622
+ // $1.25 per 1M tokens
623
+ outputCostPer1M: 1e3,
624
+ // $10.00 per 1M tokens
625
+ minTokens: 4e3,
626
+ // Reasoning models need more
627
+ recommendedTokens: 8e3,
628
+ maxTokens: 4e5,
629
+ // 400k context window
630
+ category: "reasoning",
631
+ configSchema: GPT5ConfigSchema
944
632
  },
945
633
  "gpt-5.4-mini": {
946
634
  inputCostPer1M: 75,
@@ -1131,25 +819,27 @@ function getRuntimeResources(resources) {
1131
819
  }
1132
820
  function validateResourceGovernance(orgName, deployment, organizationModel = deployment.organizationModel, options = {}) {
1133
821
  const mode = getResourceValidatorMode(options.mode);
1134
- const omResources = organizationModel?.resources?.entries;
1135
- const omSystems = organizationModel?.systems?.systems;
822
+ const omResourcesMap = organizationModel?.resources;
823
+ const omSystemsMap = organizationModel?.systems;
1136
824
  const issues = [];
1137
- if (!omResources || !omSystems) {
825
+ if (!omResourcesMap || !omSystemsMap) {
1138
826
  return { valid: true, mode, issues };
1139
827
  }
1140
- const systemsById = new Map(omSystems.map((system) => [system.id, system]));
1141
- const activeOmResources = omResources.filter((resource) => resource.status === "active");
828
+ const systemsById = new Map(
829
+ listAllSystems({ systems: omSystemsMap }).map(({ path, system }) => [path, system])
830
+ );
831
+ const activeOmResources = Object.values(omResourcesMap).filter((resource) => resource.status === "active");
1142
832
  const omResourcesById = new Map(activeOmResources.map((resource) => [resource.id, resource]));
1143
833
  const runtimeResources = getRuntimeResources(deployment);
1144
834
  const runtimeResourcesById = new Map(runtimeResources.map((resource) => [resource.resourceId, resource]));
1145
835
  for (const resource of activeOmResources) {
1146
- if (!systemsById.has(resource.systemId)) {
836
+ if (!systemsById.has(resource.systemPath)) {
1147
837
  addGovernanceIssue(
1148
838
  issues,
1149
839
  "missing-om-system",
1150
840
  orgName,
1151
841
  resource.id,
1152
- `[${orgName}] OM resource '${resource.id}' references missing System '${resource.systemId}'.`
842
+ `[${orgName}] OM resource '${resource.id}' references missing system path '${resource.systemPath}'.`
1153
843
  );
1154
844
  }
1155
845
  const runtimeResource = runtimeResourcesById.get(resource.id);
@@ -1172,13 +862,13 @@ function validateResourceGovernance(orgName, deployment, organizationModel = dep
1172
862
  `[${orgName}] Resource '${resource.id}' type mismatch: code has '${runtimeResource.type}', OM has '${resource.kind}'.`
1173
863
  );
1174
864
  }
1175
- if (runtimeResource.descriptor && runtimeResource.descriptor.systemId !== resource.systemId) {
865
+ if (runtimeResource.descriptor && runtimeResource.descriptor.systemPath !== resource.systemPath) {
1176
866
  addGovernanceIssue(
1177
867
  issues,
1178
868
  "system-mismatch",
1179
869
  orgName,
1180
870
  resource.id,
1181
- `[${orgName}] Resource '${resource.id}' system mismatch: code descriptor has '${runtimeResource.descriptor.systemId}', OM has '${resource.systemId}'.`
871
+ `[${orgName}] Resource '${resource.id}' system mismatch: code descriptor has '${runtimeResource.descriptor.systemPath}', OM has '${resource.systemPath}'.`
1182
872
  );
1183
873
  }
1184
874
  }
@@ -3753,21 +3443,21 @@ function summarizeSystem(system) {
3753
3443
  if (!system) return void 0;
3754
3444
  return {
3755
3445
  id: system.id,
3756
- title: system.title,
3446
+ title: system.label ?? system.title,
3757
3447
  description: system.description,
3758
3448
  kind: system.kind,
3759
- status: system.status
3449
+ lifecycle: system.lifecycle
3760
3450
  };
3761
3451
  }
3762
3452
  function createGovernanceMetadataResolver(resources) {
3763
- const resourcesById = new Map((resources.organizationModel?.resources?.entries ?? []).map((r) => [r.id, r]));
3764
- const systemsById = new Map((resources.organizationModel?.systems?.systems ?? []).map((s) => [s.id, s]));
3453
+ const resourcesById = new Map(Object.values(resources.organizationModel?.resources ?? {}).map((r) => [r.id, r]));
3454
+ const systemsById = new Map(Object.values(resources.organizationModel?.systems ?? {}).map((s) => [s.id, s]));
3765
3455
  return (resourceId, descriptor) => {
3766
3456
  const resource = descriptor ?? resourcesById.get(resourceId);
3767
3457
  if (!resource) return {};
3768
3458
  return {
3769
- systemId: resource.systemId,
3770
- system: summarizeSystem(systemsById.get(resource.systemId)),
3459
+ systemPath: resource.systemPath,
3460
+ system: summarizeSystem(systemsById.get(resource.systemPath)),
3771
3461
  governanceStatus: resource.status
3772
3462
  };
3773
3463
  };
@@ -3989,10 +3679,10 @@ function summarizeSystem2(system) {
3989
3679
  if (!system) return void 0;
3990
3680
  return {
3991
3681
  id: system.id,
3992
- title: system.title,
3682
+ title: system.label ?? system.title,
3993
3683
  description: system.description,
3994
3684
  kind: system.kind,
3995
- status: system.status
3685
+ lifecycle: system.lifecycle
3996
3686
  };
3997
3687
  }
3998
3688
  var ResourceRegistry = class {
@@ -4141,14 +3831,17 @@ var ResourceRegistry = class {
4141
3831
  environment
4142
3832
  };
4143
3833
  }
4144
- const resourcesById = new Map((orgResources.organizationModel?.resources?.entries ?? []).map((r) => [r.id, r]));
4145
- const systemsById = new Map((orgResources.organizationModel?.systems?.systems ?? []).map((s) => [s.id, s]));
3834
+ const resourcesById = new Map(Object.values(orgResources.organizationModel?.resources ?? {}).map((r) => [r.id, r]));
3835
+ const omSystems = orgResources.organizationModel?.systems;
3836
+ const systemsByPath = new Map(
3837
+ omSystems ? listAllSystems({ systems: omSystems }).map(({ path, system }) => [path, system]) : []
3838
+ );
4146
3839
  const getGovernanceMetadata = (resourceId, descriptor) => {
4147
3840
  const resource = descriptor ?? resourcesById.get(resourceId);
4148
3841
  if (!resource) return {};
4149
3842
  return {
4150
- systemId: resource.systemId,
4151
- system: summarizeSystem2(systemsById.get(resource.systemId)),
3843
+ systemPath: resource.systemPath,
3844
+ system: summarizeSystem2(systemsByPath.get(resource.systemPath)),
4152
3845
  governanceStatus: resource.status
4153
3846
  };
4154
3847
  };
@@ -4596,22 +4289,563 @@ var ResourceRegistry = class {
4596
4289
  edges: []
4597
4290
  };
4598
4291
  }
4599
- return cache.commandView;
4292
+ return cache.commandView;
4293
+ }
4294
+ };
4295
+ DisplayMetadataSchema.extend({
4296
+ id: ModelIdSchema,
4297
+ order: z.number().min(0)
4298
+ });
4299
+ var RecordColumnConfigSchema = z.object({
4300
+ key: ModelIdSchema,
4301
+ label: z.string().trim().min(1).max(120),
4302
+ path: z.string().trim().min(1).max(500),
4303
+ width: z.union([z.number().positive(), z.string().trim().min(1).max(100)]).optional(),
4304
+ renderType: z.enum(["text", "badge", "datetime", "count", "json"]).optional(),
4305
+ badgeColor: z.string().trim().min(1).max(40).optional()
4306
+ });
4307
+ var RecordColumnsConfigSchema = z.object({
4308
+ company: z.array(RecordColumnConfigSchema).optional(),
4309
+ contact: z.array(RecordColumnConfigSchema).optional()
4310
+ }).refine((columns) => Boolean(columns.company?.length || columns.contact?.length), {
4311
+ message: "recordColumns must include at least one entity column set"
4312
+ });
4313
+ var CredentialRequirementSchema = z.object({
4314
+ key: ModelIdSchema,
4315
+ provider: ModelIdSchema,
4316
+ credentialType: z.enum(["api-key", "api-key-secret", "oauth", "webhook-secret"]),
4317
+ label: z.string().trim().min(1).max(120),
4318
+ required: z.boolean(),
4319
+ selectionMode: z.enum(["single", "multiple"]).optional(),
4320
+ inputPath: z.string().trim().min(1).max(500),
4321
+ verifyOnRun: z.boolean().optional()
4322
+ });
4323
+ var ProspectingBuildTemplateStepSchema = DisplayMetadataSchema.extend({
4324
+ id: ModelIdSchema,
4325
+ primaryEntity: z.enum(["company", "contact"]),
4326
+ outputs: z.array(z.enum(["company", "contact", "export"])).min(1),
4327
+ stageKey: ModelIdSchema,
4328
+ recordEntity: z.enum(["company", "contact"]).optional(),
4329
+ recordsStageKey: ModelIdSchema.optional(),
4330
+ recordSourceStageKey: ModelIdSchema.optional(),
4331
+ dependsOn: z.array(ModelIdSchema).optional(),
4332
+ dependencyMode: z.literal("per-record-eligibility"),
4333
+ actionKey: ModelIdSchema,
4334
+ defaultBatchSize: z.number().int().positive(),
4335
+ maxBatchSize: z.number().int().positive(),
4336
+ recordColumns: RecordColumnsConfigSchema.optional(),
4337
+ credentialRequirements: z.array(CredentialRequirementSchema).optional()
4338
+ }).refine((step) => step.defaultBatchSize <= step.maxBatchSize, {
4339
+ message: "defaultBatchSize must be less than or equal to maxBatchSize",
4340
+ path: ["defaultBatchSize"]
4341
+ });
4342
+ var ProspectingBuildTemplateSchema = DisplayMetadataSchema.extend({
4343
+ id: ModelIdSchema,
4344
+ steps: z.array(ProspectingBuildTemplateStepSchema).min(1)
4345
+ });
4346
+ var DTC_RECORD_COLUMNS = {
4347
+ populated: {
4348
+ company: [
4349
+ { key: "name", label: "Company", path: "company.name" },
4350
+ { key: "domain", label: "Domain", path: "company.domain" },
4351
+ { key: "employee-count", label: "Employees", path: "company.numEmployees", renderType: "count" },
4352
+ { key: "apollo-industry", label: "Apollo industry", path: "company.category" },
4353
+ { key: "location", label: "Location", path: "company.locationState" }
4354
+ ]
4355
+ },
4356
+ crawled: {
4357
+ company: [
4358
+ { key: "name", label: "Company", path: "company.name" },
4359
+ { key: "domain", label: "Domain", path: "company.domain" },
4360
+ { key: "page-count", label: "Pages", path: "company.enrichmentData.websiteCrawl.pageCount", renderType: "count" },
4361
+ { key: "crawl-status", label: "Crawl status", path: "processingState.crawled.status", renderType: "badge" }
4362
+ ]
4363
+ },
4364
+ extracted: {
4365
+ company: [
4366
+ { key: "name", label: "Company", path: "company.name" },
4367
+ { key: "domain", label: "Domain", path: "company.domain" },
4368
+ { key: "description", label: "Description", path: "company.enrichmentData.websiteCrawl.companyDescription" },
4369
+ { key: "services", label: "Services", path: "company.enrichmentData.websiteCrawl.services", renderType: "json" },
4370
+ {
4371
+ key: "automation-gaps",
4372
+ label: "Automation gaps",
4373
+ path: "company.enrichmentData.websiteCrawl.automationGaps",
4374
+ renderType: "json"
4375
+ },
4376
+ {
4377
+ key: "contact-count",
4378
+ label: "Contacts",
4379
+ path: "company.enrichmentData.websiteCrawl.emailCount",
4380
+ renderType: "count"
4381
+ }
4382
+ ]
4383
+ },
4384
+ qualified: {
4385
+ company: [
4386
+ { key: "name", label: "Company", path: "company.name" },
4387
+ { key: "domain", label: "Domain", path: "company.domain" },
4388
+ { key: "score", label: "Score", path: "company.qualificationScore", renderType: "badge", badgeColor: "green" },
4389
+ { key: "signals", label: "Signals", path: "company.qualificationSignals", renderType: "json" },
4390
+ {
4391
+ key: "disqualified-reason",
4392
+ label: "Disqualified reason",
4393
+ path: "processingState.qualified.data.disqualifiedReason"
4394
+ }
4395
+ ]
4396
+ },
4397
+ decisionMakers: {
4398
+ contact: [
4399
+ { key: "name", label: "Name", path: "contact.name" },
4400
+ { key: "title", label: "Title", path: "contact.title" },
4401
+ { key: "email", label: "Email", path: "contact.email" },
4402
+ { key: "linkedin", label: "LinkedIn", path: "contact.linkedinUrl" },
4403
+ {
4404
+ key: "priority-score",
4405
+ label: "Priority",
4406
+ path: "contact.enrichmentData.apollo.priorityScore",
4407
+ renderType: "badge"
4408
+ }
4409
+ ]
4410
+ },
4411
+ uploaded: {
4412
+ company: [
4413
+ { key: "name", label: "Company", path: "company.name" },
4414
+ { key: "domain", label: "Domain", path: "company.domain" },
4415
+ {
4416
+ key: "contacts",
4417
+ label: "Contacts",
4418
+ path: "company.enrichmentData.approvedLeadListExport.contacts",
4419
+ renderType: "json"
4420
+ },
4421
+ { key: "score", label: "Score", path: "company.qualificationScore", renderType: "badge", badgeColor: "green" },
4422
+ {
4423
+ key: "approval",
4424
+ label: "Approval",
4425
+ path: "company.enrichmentData.approvedLeadListExport.approvalStatus",
4426
+ renderType: "badge"
4427
+ }
4428
+ ]
4429
+ }
4430
+ };
4431
+ var ACTION_REGISTRY = Object.values(LEAD_GEN_ACTION_ENTRIES);
4432
+ var PROSPECTING_STEPS = {
4433
+ localServices: {
4434
+ sourceCompanies: {
4435
+ id: "source-companies",
4436
+ label: "Companies found",
4437
+ primaryEntity: "company",
4438
+ outputs: ["company"],
4439
+ stageKey: "populated",
4440
+ dependencyMode: "per-record-eligibility",
4441
+ actionKey: "lead-gen.company.source",
4442
+ defaultBatchSize: 100,
4443
+ maxBatchSize: 250
4444
+ },
4445
+ analyzeWebsites: {
4446
+ id: "analyze-websites",
4447
+ label: "Websites analyzed",
4448
+ primaryEntity: "company",
4449
+ outputs: ["company"],
4450
+ stageKey: "extracted",
4451
+ dependsOn: ["source-companies"],
4452
+ dependencyMode: "per-record-eligibility",
4453
+ actionKey: "lead-gen.company.website-extract",
4454
+ defaultBatchSize: 50,
4455
+ maxBatchSize: 100
4456
+ },
4457
+ qualifyCompanies: {
4458
+ id: "qualify-companies",
4459
+ label: "Companies qualified",
4460
+ primaryEntity: "company",
4461
+ outputs: ["company"],
4462
+ stageKey: "qualified",
4463
+ dependsOn: ["analyze-websites"],
4464
+ dependencyMode: "per-record-eligibility",
4465
+ actionKey: "lead-gen.company.qualify",
4466
+ defaultBatchSize: 100,
4467
+ maxBatchSize: 250
4468
+ },
4469
+ findContacts: {
4470
+ id: "find-contacts",
4471
+ label: "Decision-makers found",
4472
+ primaryEntity: "contact",
4473
+ outputs: ["contact"],
4474
+ stageKey: "discovered",
4475
+ dependsOn: ["qualify-companies"],
4476
+ dependencyMode: "per-record-eligibility",
4477
+ actionKey: "lead-gen.contact.discover",
4478
+ defaultBatchSize: 50,
4479
+ maxBatchSize: 100
4480
+ },
4481
+ verifyEmails: {
4482
+ id: "verify-emails",
4483
+ label: "Emails verified",
4484
+ primaryEntity: "contact",
4485
+ outputs: ["contact"],
4486
+ stageKey: "verified",
4487
+ dependsOn: ["find-contacts"],
4488
+ dependencyMode: "per-record-eligibility",
4489
+ actionKey: "lead-gen.contact.verify-email",
4490
+ defaultBatchSize: 100,
4491
+ maxBatchSize: 500
4492
+ },
4493
+ personalize: {
4494
+ id: "personalize",
4495
+ label: "Personalize",
4496
+ primaryEntity: "contact",
4497
+ outputs: ["contact"],
4498
+ stageKey: "personalized",
4499
+ dependsOn: ["verify-emails"],
4500
+ dependencyMode: "per-record-eligibility",
4501
+ actionKey: "lead-gen.contact.personalize",
4502
+ defaultBatchSize: 25,
4503
+ maxBatchSize: 100
4504
+ },
4505
+ review: {
4506
+ id: "review",
4507
+ label: "Reviewed and exported",
4508
+ primaryEntity: "contact",
4509
+ outputs: ["export"],
4510
+ stageKey: "uploaded",
4511
+ dependsOn: ["personalize"],
4512
+ dependencyMode: "per-record-eligibility",
4513
+ actionKey: "lead-gen.review.outreach-ready",
4514
+ defaultBatchSize: 25,
4515
+ maxBatchSize: 100
4516
+ }
4517
+ },
4518
+ dtcApolloClickup: {
4519
+ importApolloSearch: {
4520
+ id: "import-apollo-search",
4521
+ label: "Companies found",
4522
+ description: "Pull companies and seed contact data from a predefined Apollo search or list.",
4523
+ primaryEntity: "company",
4524
+ outputs: ["company", "contact"],
4525
+ stageKey: "populated",
4526
+ dependencyMode: "per-record-eligibility",
4527
+ actionKey: "lead-gen.company.apollo-import",
4528
+ defaultBatchSize: 250,
4529
+ maxBatchSize: 1e3,
4530
+ recordColumns: DTC_RECORD_COLUMNS.populated,
4531
+ credentialRequirements: [
4532
+ {
4533
+ key: "apollo",
4534
+ provider: "apollo",
4535
+ credentialType: "api-key-secret",
4536
+ label: "Apollo API key",
4537
+ required: true,
4538
+ selectionMode: "single",
4539
+ inputPath: "credential"
4540
+ }
4541
+ ]
4542
+ },
4543
+ apifyCrawl: {
4544
+ id: "apify-crawl",
4545
+ label: "Websites crawled",
4546
+ description: "Crawl company websites via Apify and store raw page markdown in enrichmentData.websiteCrawl.pages for downstream LLM analysis. Overwrites the synthetic seed Apollo Import wrote with real page content.",
4547
+ primaryEntity: "company",
4548
+ outputs: ["company"],
4549
+ stageKey: "crawled",
4550
+ dependsOn: ["import-apollo-search"],
4551
+ dependencyMode: "per-record-eligibility",
4552
+ actionKey: "lead-gen.company.apify-crawl",
4553
+ defaultBatchSize: 50,
4554
+ maxBatchSize: 100,
4555
+ recordColumns: DTC_RECORD_COLUMNS.crawled,
4556
+ credentialRequirements: [
4557
+ {
4558
+ key: "apify",
4559
+ provider: "apify",
4560
+ credentialType: "api-key-secret",
4561
+ label: "Apify API token",
4562
+ required: true,
4563
+ selectionMode: "single",
4564
+ inputPath: "credential",
4565
+ verifyOnRun: true
4566
+ }
4567
+ ]
4568
+ },
4569
+ analyzeWebsites: {
4570
+ id: "analyze-websites",
4571
+ label: "Websites analyzed",
4572
+ description: "Extract subscription, product, retention, and tech-stack signals from each brand website.",
4573
+ primaryEntity: "company",
4574
+ outputs: ["company"],
4575
+ stageKey: "extracted",
4576
+ dependsOn: ["apify-crawl"],
4577
+ dependencyMode: "per-record-eligibility",
4578
+ actionKey: "lead-gen.company.website-extract",
4579
+ defaultBatchSize: 50,
4580
+ maxBatchSize: 100,
4581
+ recordColumns: DTC_RECORD_COLUMNS.extracted
4582
+ },
4583
+ scoreDtcFit: {
4584
+ id: "score-dtc-fit",
4585
+ label: "Companies qualified",
4586
+ description: "Classify subscription potential, consumable-product fit, retention maturity, and disqualifiers.",
4587
+ primaryEntity: "company",
4588
+ outputs: ["company"],
4589
+ stageKey: "qualified",
4590
+ dependsOn: ["analyze-websites"],
4591
+ dependencyMode: "per-record-eligibility",
4592
+ actionKey: "lead-gen.company.dtc-subscription-qualify",
4593
+ defaultBatchSize: 100,
4594
+ maxBatchSize: 250,
4595
+ recordColumns: DTC_RECORD_COLUMNS.qualified
4596
+ },
4597
+ enrichDecisionMakers: {
4598
+ id: "enrich-decision-makers",
4599
+ label: "Decision-makers found",
4600
+ description: "Use Apollo to find qualified contacts at qualified companies - founders, retention leads, lifecycle leads, and marketing owners.",
4601
+ primaryEntity: "company",
4602
+ outputs: ["contact"],
4603
+ stageKey: "decision-makers-enriched",
4604
+ recordEntity: "contact",
4605
+ dependsOn: ["score-dtc-fit"],
4606
+ dependencyMode: "per-record-eligibility",
4607
+ actionKey: "lead-gen.contact.apollo-decision-maker-enrich",
4608
+ defaultBatchSize: 100,
4609
+ maxBatchSize: 250,
4610
+ recordColumns: DTC_RECORD_COLUMNS.decisionMakers,
4611
+ credentialRequirements: [
4612
+ {
4613
+ key: "apollo",
4614
+ provider: "apollo",
4615
+ credentialType: "api-key-secret",
4616
+ label: "Apollo API key",
4617
+ required: true,
4618
+ selectionMode: "single",
4619
+ inputPath: "credential"
4620
+ }
4621
+ ]
4622
+ },
4623
+ reviewAndExport: {
4624
+ id: "review-and-export",
4625
+ label: "Reviewed and exported",
4626
+ description: "Operator QC approves or rejects qualified companies, then approved records are exported as a lead list with unverified emails.",
4627
+ primaryEntity: "company",
4628
+ outputs: ["export"],
4629
+ stageKey: "uploaded",
4630
+ recordsStageKey: "uploaded",
4631
+ recordSourceStageKey: "qualified",
4632
+ dependsOn: ["enrich-decision-makers"],
4633
+ dependencyMode: "per-record-eligibility",
4634
+ actionKey: "lead-gen.export.list",
4635
+ defaultBatchSize: 100,
4636
+ maxBatchSize: 250,
4637
+ recordColumns: DTC_RECORD_COLUMNS.uploaded,
4638
+ credentialRequirements: [
4639
+ {
4640
+ key: "clickup",
4641
+ provider: "clickup",
4642
+ credentialType: "api-key-secret",
4643
+ label: "ClickUp API token",
4644
+ required: true,
4645
+ selectionMode: "single",
4646
+ inputPath: "clickupCredential",
4647
+ verifyOnRun: true
4648
+ }
4649
+ ]
4650
+ }
4600
4651
  }
4601
4652
  };
4602
4653
 
4603
4654
  // ../core/src/business/acquisition/build-templates.ts
4604
- var PROSPECTING_BUILD_TEMPLATE_OPTIONS = DEFAULT_ORGANIZATION_MODEL_PROSPECTING.buildTemplates.map(
4605
- ({ id, label, description }) => ({
4606
- id,
4607
- label,
4608
- description
4609
- })
4610
- );
4655
+ var BUILD_TEMPLATE_CATALOG = [
4656
+ {
4657
+ id: "local-services",
4658
+ label: "Local Services",
4659
+ description: "Source, analyze, qualify, and personalize local service businesses for outreach.",
4660
+ steps: Object.values(PROSPECTING_STEPS.localServices)
4661
+ },
4662
+ {
4663
+ id: "dtc-subscription-apollo-clickup",
4664
+ label: "DTC Subscription (Apollo + ClickUp)",
4665
+ description: "Import DTC brand leads from Apollo, crawl their websites, score fit, enrich contacts, and export via ClickUp.",
4666
+ steps: Object.values(PROSPECTING_STEPS.dtcApolloClickup)
4667
+ }
4668
+ ];
4669
+ var PROSPECTING_BUILD_TEMPLATE_OPTIONS = BUILD_TEMPLATE_CATALOG.map(({ id, label, description }) => ({
4670
+ id,
4671
+ label,
4672
+ description
4673
+ }));
4611
4674
  function isProspectingBuildTemplateId(value) {
4612
4675
  return PROSPECTING_BUILD_TEMPLATE_OPTIONS.some((template) => template.id === value);
4613
4676
  }
4614
4677
 
4678
+ // ../core/src/organization-model/catalogs/lead-gen.ts
4679
+ var LEAD_GEN_STAGE_CATALOG = {
4680
+ // Prospecting - company population
4681
+ scraped: {
4682
+ key: "scraped",
4683
+ label: "Scraped",
4684
+ description: "Company was scraped from a source directory (Apify actor run).",
4685
+ order: 1,
4686
+ entity: "company"
4687
+ },
4688
+ populated: {
4689
+ key: "populated",
4690
+ label: "Companies found",
4691
+ description: "Companies have been found and added to the lead-gen list.",
4692
+ order: 2,
4693
+ entity: "company"
4694
+ },
4695
+ crawled: {
4696
+ key: "crawled",
4697
+ label: "Websites crawled",
4698
+ description: "Company websites have been crawled (e.g. via Apify) and raw page content stored for downstream LLM analysis.",
4699
+ order: 2.5,
4700
+ entity: "company"
4701
+ },
4702
+ extracted: {
4703
+ key: "extracted",
4704
+ label: "Websites analyzed",
4705
+ description: "Company websites have been analyzed for business signals.",
4706
+ order: 3,
4707
+ entity: "company"
4708
+ },
4709
+ enriched: {
4710
+ key: "enriched",
4711
+ label: "Enriched",
4712
+ description: "Company or contact enriched with third-party data (e.g. Tomba, Anymailfinder).",
4713
+ order: 4,
4714
+ entity: "company"
4715
+ },
4716
+ "decision-makers-enriched": {
4717
+ key: "decision-makers-enriched",
4718
+ label: "Decision-makers found",
4719
+ description: "Decision-maker contacts discovered and attached to a qualified company.",
4720
+ order: 6,
4721
+ entity: "company",
4722
+ recordEntity: "contact",
4723
+ recordStageKey: "discovered"
4724
+ },
4725
+ // Prospecting - contact discovery
4726
+ discovered: {
4727
+ key: "discovered",
4728
+ label: "Decision-makers found",
4729
+ description: "Decision-maker contact details have been found.",
4730
+ order: 5,
4731
+ entity: "contact"
4732
+ },
4733
+ verified: {
4734
+ key: "verified",
4735
+ label: "Emails verified",
4736
+ description: "Contact email addresses have been checked for deliverability.",
4737
+ order: 7,
4738
+ entity: "contact"
4739
+ },
4740
+ // Qualification
4741
+ qualified: {
4742
+ key: "qualified",
4743
+ label: "Companies qualified",
4744
+ description: "Companies have been scored against the qualification criteria.",
4745
+ order: 8,
4746
+ entity: "company"
4747
+ },
4748
+ // Outreach
4749
+ personalized: {
4750
+ key: "personalized",
4751
+ label: "Personalized",
4752
+ description: "Outreach message personalized for the contact (Instantly personalization workflow).",
4753
+ order: 9,
4754
+ entity: "contact"
4755
+ },
4756
+ uploaded: {
4757
+ key: "uploaded",
4758
+ label: "Reviewed and exported",
4759
+ description: "Approved records have been reviewed and exported for handoff.",
4760
+ order: 10,
4761
+ entity: "company",
4762
+ additionalEntities: ["contact"]
4763
+ },
4764
+ interested: {
4765
+ key: "interested",
4766
+ label: "Interested",
4767
+ description: "Contact replied with a positive signal (Instantly reply-handler transition).",
4768
+ order: 11,
4769
+ entity: "contact"
4770
+ }
4771
+ };
4772
+
4773
+ // ../core/src/organization-model/domains/sales.ts
4774
+ var SalesStageSemanticClassSchema = z.enum(["open", "active", "nurturing", "closed_won", "closed_lost"]);
4775
+ var SalesStageSchema = DisplayMetadataSchema.extend({
4776
+ id: ModelIdSchema,
4777
+ order: z.number().int().min(0),
4778
+ semanticClass: SalesStageSemanticClassSchema,
4779
+ surfaceIds: ReferenceIdsSchema,
4780
+ resourceIds: ReferenceIdsSchema
4781
+ });
4782
+ z.object({
4783
+ id: ModelIdSchema,
4784
+ label: z.string().trim().min(1).max(120),
4785
+ description: DescriptionSchema.optional(),
4786
+ entityId: ModelIdSchema,
4787
+ stages: z.array(SalesStageSchema).min(1)
4788
+ });
4789
+ var CRM_DISCOVERY_REPLIED_STATE = {
4790
+ stateKey: "discovery_replied",
4791
+ label: "Discovery Replied"
4792
+ };
4793
+ var CRM_DISCOVERY_LINK_SENT_STATE = {
4794
+ stateKey: "discovery_link_sent",
4795
+ label: "Discovery Link Sent"
4796
+ };
4797
+ var CRM_DISCOVERY_NUDGING_STATE = {
4798
+ stateKey: "discovery_nudging",
4799
+ label: "Discovery Nudging"
4800
+ };
4801
+ var CRM_DISCOVERY_BOOKING_CANCELLED_STATE = {
4802
+ stateKey: "discovery_booking_cancelled",
4803
+ label: "Discovery Booking Cancelled"
4804
+ };
4805
+ var CRM_REPLY_SENT_STATE = {
4806
+ stateKey: "reply_sent",
4807
+ label: "Reply Sent"
4808
+ };
4809
+ var CRM_FOLLOWUP_1_SENT_STATE = {
4810
+ stateKey: "followup_1_sent",
4811
+ label: "Follow-up 1 Sent"
4812
+ };
4813
+ var CRM_FOLLOWUP_2_SENT_STATE = {
4814
+ stateKey: "followup_2_sent",
4815
+ label: "Follow-up 2 Sent"
4816
+ };
4817
+ var CRM_FOLLOWUP_3_SENT_STATE = {
4818
+ stateKey: "followup_3_sent",
4819
+ label: "Follow-up 3 Sent"
4820
+ };
4821
+ var CRM_PIPELINE_DEFINITION = {
4822
+ pipelineKey: "crm",
4823
+ label: "CRM",
4824
+ entityKey: "crm.deal",
4825
+ stages: [
4826
+ {
4827
+ stageKey: "interested",
4828
+ label: "Interested",
4829
+ color: "blue",
4830
+ states: [
4831
+ CRM_DISCOVERY_REPLIED_STATE,
4832
+ CRM_DISCOVERY_LINK_SENT_STATE,
4833
+ CRM_DISCOVERY_NUDGING_STATE,
4834
+ CRM_DISCOVERY_BOOKING_CANCELLED_STATE,
4835
+ CRM_REPLY_SENT_STATE,
4836
+ CRM_FOLLOWUP_1_SENT_STATE,
4837
+ CRM_FOLLOWUP_2_SENT_STATE,
4838
+ CRM_FOLLOWUP_3_SENT_STATE
4839
+ ]
4840
+ },
4841
+ { stageKey: "proposal", label: "Proposal", color: "yellow", states: [] },
4842
+ { stageKey: "closing", label: "Closing", color: "orange", states: [] },
4843
+ { stageKey: "closed_won", label: "Closed Won", color: "green", states: [] },
4844
+ { stageKey: "closed_lost", label: "Closed Lost", color: "red", states: [] },
4845
+ { stageKey: "nurturing", label: "Nurturing", color: "grape", states: [] }
4846
+ ]
4847
+ };
4848
+
4615
4849
  // ../core/src/business/acquisition/deal-ownership.ts
4616
4850
  var INBOUND_EVENT_TYPES = ["reply_received"];
4617
4851
  var OUTBOUND_EVENT_TYPES = [
@@ -4673,8 +4907,8 @@ var ProcessingStageStatusSchema = z.enum(["success", "no_result", "skipped", "er
4673
4907
  var LeadGenStageKeySchema = z.string().refine((value) => Object.prototype.hasOwnProperty.call(LEAD_GEN_STAGE_CATALOG, value), {
4674
4908
  message: "processing state key must match LEAD_GEN_STAGE_CATALOG"
4675
4909
  });
4676
- var LeadGenCapabilityKeySchema = z.string().refine((value) => CAPABILITY_REGISTRY.some((c) => c.id === value), {
4677
- message: "capabilityKey must match CAPABILITY_REGISTRY"
4910
+ var LeadGenActionKeySchema = z.string().refine((value) => ACTION_REGISTRY.some((c) => c.id === value), {
4911
+ message: "actionKey must match ACTION_REGISTRY"
4678
4912
  });
4679
4913
  var crmStageKeys = CRM_PIPELINE_DEFINITION.stages.map((stage) => stage.stageKey);
4680
4914
  var crmStateKeys = CRM_PIPELINE_DEFINITION.stages.flatMap((stage) => stage.states.map((state) => state.stateKey));
@@ -4954,7 +5188,7 @@ var BuildPlanSnapshotStepSchema = z.object({
4954
5188
  recordSourceStageKey: LeadGenStageKeySchema.optional(),
4955
5189
  dependsOn: z.array(z.string().trim().min(1).max(100)).optional(),
4956
5190
  dependencyMode: z.literal("per-record-eligibility"),
4957
- capabilityKey: LeadGenCapabilityKeySchema,
5191
+ actionKey: LeadGenActionKeySchema,
4958
5192
  defaultBatchSize: z.number().int().positive(),
4959
5193
  maxBatchSize: z.number().int().positive(),
4960
5194
  recordColumns: z.object({