@leadpoet/client 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +59 -35
  2. package/cli.js +47 -2
  3. package/cli.js.map +2 -2
  4. package/client.js +47 -2
  5. package/client.js.map +2 -2
  6. package/mcp.js +3969 -71
  7. package/mcp.js.map +4 -4
  8. package/package.json +13 -2
  9. package/skill/SKILL.md +83 -9
  10. package/types/scripts/leadpoet-mcp.d.ts +1 -7
  11. package/types/shared/account-monitoring.d.ts +1169 -0
  12. package/types/shared/assistant-brief.d.ts +1681 -0
  13. package/types/shared/assistant-memories.d.ts +62 -0
  14. package/types/shared/assistant-send-forecast.d.ts +442 -0
  15. package/types/shared/assistant.d.ts +238 -0
  16. package/types/shared/employee-count.d.ts +90 -0
  17. package/types/shared/gtm-context.d.ts +233 -0
  18. package/types/shared/icp-product-selection.d.ts +27 -0
  19. package/types/shared/icp-profile.d.ts +2625 -0
  20. package/types/shared/industry-taxonomy.d.ts +1 -0
  21. package/types/shared/lead-request-limits.d.ts +4 -0
  22. package/types/shared/leadpoet-client.d.ts +1315 -0
  23. package/types/shared/leadpoet-mcp-server.d.ts +39 -0
  24. package/types/shared/mcp-connect.d.ts +22 -0
  25. package/types/shared/monitor-technologies.d.ts +38 -0
  26. package/types/shared/outreach-analytics.d.ts +334 -0
  27. package/types/shared/outreach-audience-filters.d.ts +156 -0
  28. package/types/shared/outreach-audience-segments.d.ts +135 -0
  29. package/types/shared/outreach-enums.d.ts +22 -0
  30. package/types/shared/outreach-v2-contract.d.ts +253 -0
  31. package/types/shared/plan-entitlements.d.ts +75 -0
  32. package/types/shared/plan-features.d.ts +50 -0
  33. package/types/shared/request-credit-constants.d.ts +13 -0
  34. package/types/shared/website-fact-language.d.ts +9 -0
  35. package/types/shared/workspace-operations.d.ts +1975 -29
  36. package/types/shared/workspace-tool-metadata.d.ts +1083 -0
@@ -0,0 +1,135 @@
1
+ import { z } from "zod";
2
+ import { type AudienceContactFacts, type AudienceFilters, type AudienceSeniority } from "./outreach-audience-filters";
3
+ /**
4
+ * Seniority from title words alone. Null without a title, or when the title
5
+ * names neither a level nor an individual contributor's job, so a seniority
6
+ * filter excludes the contact instead of guessing.
7
+ */
8
+ export declare function contactSeniorityForTitle(title: unknown): AudienceSeniority | null;
9
+ export interface AudienceFactSources {
10
+ title: unknown;
11
+ contact: {
12
+ country?: unknown;
13
+ state?: unknown;
14
+ };
15
+ /** The linked company, or the contact's own company fields without one. Null hides company facts. */
16
+ company: {
17
+ industry?: unknown;
18
+ sub_industry?: unknown;
19
+ employee_count?: unknown;
20
+ hq_country?: unknown;
21
+ hq_state?: unknown;
22
+ } | null;
23
+ signals: readonly string[];
24
+ }
25
+ /** Location is the contact's own, else the company's HQ, never a mix. */
26
+ export declare function audienceContactFacts(sources: AudienceFactSources): AudienceContactFacts;
27
+ export declare const AUDIENCE_FACET_KEYS: readonly ["departments", "seniorities", "countries", "states", "industries", "sub_industries", "company_sizes", "signals"];
28
+ export type AudienceFacetKey = (typeof AUDIENCE_FACET_KEYS)[number];
29
+ export declare const audienceFacetsSchema: z.ZodObject<{
30
+ total: z.ZodNumber;
31
+ dimensions: z.ZodArray<z.ZodObject<{
32
+ key: z.ZodEnum<["departments", "seniorities", "countries", "states", "industries", "sub_industries", "company_sizes", "signals"]>;
33
+ options: z.ZodArray<z.ZodObject<{
34
+ value: z.ZodString;
35
+ label: z.ZodString;
36
+ count: z.ZodNumber;
37
+ }, "strip", z.ZodTypeAny, {
38
+ value: string;
39
+ label: string;
40
+ count: number;
41
+ }, {
42
+ value: string;
43
+ label: string;
44
+ count: number;
45
+ }>, "many">;
46
+ /** Contacts with no value here; an active filter excludes them. */
47
+ missing: z.ZodNumber;
48
+ }, "strip", z.ZodTypeAny, {
49
+ options: {
50
+ value: string;
51
+ label: string;
52
+ count: number;
53
+ }[];
54
+ key: "signals" | "departments" | "seniorities" | "industries" | "company_sizes" | "countries" | "states" | "sub_industries";
55
+ missing: number;
56
+ }, {
57
+ options: {
58
+ value: string;
59
+ label: string;
60
+ count: number;
61
+ }[];
62
+ key: "signals" | "departments" | "seniorities" | "industries" | "company_sizes" | "countries" | "states" | "sub_industries";
63
+ missing: number;
64
+ }>, "many">;
65
+ segments: z.ZodArray<z.ZodObject<{
66
+ id: z.ZodString;
67
+ label: z.ZodString;
68
+ count: z.ZodNumber;
69
+ filters: z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>;
70
+ }, "strip", z.ZodTypeAny, {
71
+ id: string;
72
+ label: string;
73
+ count: number;
74
+ filters: Record<string, string[]>;
75
+ }, {
76
+ id: string;
77
+ label: string;
78
+ count: number;
79
+ filters: Record<string, string[]>;
80
+ }>, "many">;
81
+ }, "strip", z.ZodTypeAny, {
82
+ total: number;
83
+ dimensions: {
84
+ options: {
85
+ value: string;
86
+ label: string;
87
+ count: number;
88
+ }[];
89
+ key: "signals" | "departments" | "seniorities" | "industries" | "company_sizes" | "countries" | "states" | "sub_industries";
90
+ missing: number;
91
+ }[];
92
+ segments: {
93
+ id: string;
94
+ label: string;
95
+ count: number;
96
+ filters: Record<string, string[]>;
97
+ }[];
98
+ }, {
99
+ total: number;
100
+ dimensions: {
101
+ options: {
102
+ value: string;
103
+ label: string;
104
+ count: number;
105
+ }[];
106
+ key: "signals" | "departments" | "seniorities" | "industries" | "company_sizes" | "countries" | "states" | "sub_industries";
107
+ missing: number;
108
+ }[];
109
+ segments: {
110
+ id: string;
111
+ label: string;
112
+ count: number;
113
+ filters: Record<string, string[]>;
114
+ }[];
115
+ }>;
116
+ export type AudienceFacets = z.infer<typeof audienceFacetsSchema>;
117
+ export type AudienceSegment = AudienceFacets["segments"][number];
118
+ /** Each contact's filterable values for one dimension; empty means missing. */
119
+ export declare function audienceFacetValues(key: AudienceFacetKey, facts: AudienceContactFacts): Array<{
120
+ value: string;
121
+ label: string;
122
+ }>;
123
+ /**
124
+ * Counts per filter value across the contacts given, before filters, plus up
125
+ * to four suggested groups worth their own messaging. Suggestions come only
126
+ * from the facts counted here, so applying one returns its count from the
127
+ * same contacts. The picker passes only contacts that can be added.
128
+ * `segmentFits` leaves out a group that could not be added as a campaign's
129
+ * whole audience, so the next group is suggested instead.
130
+ */
131
+ export declare function audienceFacets(contacts: readonly AudienceContactFacts[], options?: {
132
+ segmentFits?: (filters: AudienceFilters) => boolean;
133
+ }): AudienceFacets;
134
+ /** A suggested group as filters; the rest of the current filters are dropped. */
135
+ export declare function audienceSegmentFilters(segment: AudienceSegment): AudienceFilters;
@@ -0,0 +1,22 @@
1
+ export declare const OUTREACH_MODES: readonly ["copilot", "autopilot"];
2
+ export type OutreachMode = (typeof OUTREACH_MODES)[number];
3
+ export declare const OUTREACH_AUTOPILOT_SCOPES: readonly ["recommended", "all"];
4
+ export type OutreachAutopilotScope = (typeof OUTREACH_AUTOPILOT_SCOPES)[number];
5
+ export declare const OUTREACH_STRATEGY_GOALS: readonly ["meeting", "question", "referral", "website_traffic", "resource", "sample"];
6
+ export type OutreachStrategyGoal = (typeof OUTREACH_STRATEGY_GOALS)[number];
7
+ export declare const OUTREACH_EVIDENCE_TIERS: readonly ["signal_led", "role_problem_hypothesis", "insufficient"];
8
+ export type OutreachEvidenceTier = (typeof OUTREACH_EVIDENCE_TIERS)[number];
9
+ export declare const OUTREACH_RELATIONSHIP_STATES: readonly ["none", "existing_contact", "existing_company", "open_deal", "ambiguous", "unknown"];
10
+ export type OutreachRelationshipState = (typeof OUTREACH_RELATIONSHIP_STATES)[number];
11
+ export declare const OUTREACH_ACTIONS: readonly ["research_more", "draft", "queue_send", "wait", "review", "stop"];
12
+ export type OutreachAction = (typeof OUTREACH_ACTIONS)[number];
13
+ export declare const OUTREACH_SHADOW_VERDICTS: readonly ["would_send", "would_review", "would_no_send", "would_wait"];
14
+ export type OutreachShadowVerdict = (typeof OUTREACH_SHADOW_VERDICTS)[number];
15
+ export declare const OUTREACH_HUMAN_ACTIONS: readonly ["approved", "edited_approved", "denied", "skipped", "suppressed", "expired"];
16
+ export type OutreachHumanAction = (typeof OUTREACH_HUMAN_ACTIONS)[number];
17
+ export declare const OUTREACH_DIVERGENCE_CLASSES: readonly ["agree_send", "agree_hold", "content_divergence", "false_accept_style", "false_accept_safety", "false_reject"];
18
+ export type OutreachDivergenceClass = (typeof OUTREACH_DIVERGENCE_CLASSES)[number];
19
+ export declare const OUTREACH_REPLY_CLASSIFICATIONS: readonly ["opt_out", "bounce", "out_of_office", "automated", "positive", "question", "objection", "referral", "not_now", "negative", "ambiguous"];
20
+ export type OutreachReplyClassification = (typeof OUTREACH_REPLY_CLASSIFICATIONS)[number];
21
+ export declare const OUTREACH_OUTCOME_TYPES: readonly ["qualified_positive_reply", "question_substantive_response", "referral_qualified_person_named", "resource_requested", "resource_meaningfully_used", "sample_accepted", "meeting_requested", "meeting_accepted", "meeting_booked", "meeting_held", "meeting_qualified_held", "qualified_meeting_booked", "poor_fit_meeting", "qualified_meeting_held", "opportunity_created", "opportunity_qualified", "opportunity_disqualified", "opportunity_won", "opportunity_lost", "website_meaningful_visit", "meeting_no_show", "meeting_cancelled", "meeting_disqualified", "complaint", "hard_bounce", "opt_out", "negative_reply", "unsupported_claim_incident"];
22
+ export type OutreachOutcomeType = (typeof OUTREACH_OUTCOME_TYPES)[number];
@@ -0,0 +1,253 @@
1
+ import { OUTREACH_ACTIONS, OUTREACH_SHADOW_VERDICTS, OUTREACH_DIVERGENCE_CLASSES, OUTREACH_HUMAN_ACTIONS, OUTREACH_EVIDENCE_TIERS, OUTREACH_RELATIONSHIP_STATES, OUTREACH_REPLY_CLASSIFICATIONS, OUTREACH_OUTCOME_TYPES, OUTREACH_MODES, OUTREACH_AUTOPILOT_SCOPES, type OutreachAction, type OutreachShadowVerdict, type OutreachDivergenceClass, type OutreachHumanAction, type OutreachEvidenceTier, type OutreachRelationshipState, type OutreachMode, type OutreachAutopilotScope, type OutreachReplyClassification, type OutreachOutcomeType } from "./outreach-enums";
2
+ export { OUTREACH_ACTIONS, OUTREACH_SHADOW_VERDICTS, OUTREACH_DIVERGENCE_CLASSES, OUTREACH_HUMAN_ACTIONS, OUTREACH_EVIDENCE_TIERS, OUTREACH_RELATIONSHIP_STATES, OUTREACH_REPLY_CLASSIFICATIONS, OUTREACH_OUTCOME_TYPES, OUTREACH_MODES, OUTREACH_AUTOPILOT_SCOPES, };
3
+ export type { OutreachAction, OutreachShadowVerdict, OutreachDivergenceClass, OutreachHumanAction, OutreachEvidenceTier, OutreachRelationshipState, OutreachMode, OutreachAutopilotScope, OutreachReplyClassification, OutreachOutcomeType, };
4
+ /** Product outreach links must resolve to a public HTTPS host. */
5
+ export declare function isPublicOutboundHttpsUrl(value: string): boolean;
6
+ export interface OutreachRequestDeliveryState {
7
+ status?: string | null;
8
+ request_kind?: string | null;
9
+ }
10
+ export declare function isOutreachDeliveredRequest(request: OutreachRequestDeliveryState): boolean;
11
+ export declare const OUTREACH_V2_FLAGS: readonly ["OUTREACH_V2_UI", "OUTREACH_V2_ENGINE", "OUTREACH_V2_AUTOPILOT", "OUTREACH_V2_EXTERNAL_RECIPIENTS", "OUTREACH_V2_LINKEDIN_ASSIST", "OUTREACH_V2_LINKEDIN_TRANSPORT", "OUTREACH_V2_LINKEDIN_EXTERNAL_RECIPIENTS"];
12
+ export type OutreachV2Flag = (typeof OUTREACH_V2_FLAGS)[number];
13
+ export declare const OUTREACH_DENY_REASONS: readonly ["too_generic", "too_salesy", "unnatural_tone", "repeated_pitch", "did_not_answer", "wrong_angle", "wrong_proof", "too_long", "cta_too_strong", "unsupported_claim", "wrong_person", "timing_after_reply", "relationship_conflict", "compliance_issue", "custom"];
14
+ export type OutreachDenyReason = (typeof OUTREACH_DENY_REASONS)[number];
15
+ export declare const OUTREACH_DRAFT_EDIT_KINDS: readonly ["light", "shortened", "lengthened", "rewritten"];
16
+ export type OutreachDraftEditKind = (typeof OUTREACH_DRAFT_EDIT_KINDS)[number];
17
+ export declare const OUTREACH_SAFETY_DENY_REASONS: readonly OutreachDenyReason[];
18
+ export declare function isSafetyDenyReason(reason: string | null | undefined): boolean;
19
+ export declare const OUTREACH_V2_ERROR_CODES: readonly ["TENANT_MISMATCH", "NOT_ELIGIBLE", "DESTINATION_ACTIVE", "IDENTITY_AMBIGUOUS", "SUPPRESSED", "REPLY_RECEIVED", "COOLDOWN_ACTIVE", "RELATIONSHIP_BLOCKED", "COMPANY_PACING", "EVIDENCE_INSUFFICIENT", "EVIDENCE_UNTRUSTED", "PROOF_EXPIRED", "INJECTION_DETECTED", "QUALITY_FAILED", "ACCOUNT_UNAVAILABLE", "HEALTH_STALE", "CAP_REACHED", "COST_CAP_REACHED", "AUTOPILOT_NOT_AUTHORIZED", "VERSION_CONFLICT", "ALREADY_AUTHORIZED", "AUTHORIZATION_EXPIRED", "PROVIDER_RETRYABLE", "PROVIDER_TERMINAL"];
20
+ export type OutreachV2ErrorCode = (typeof OUTREACH_V2_ERROR_CODES)[number];
21
+ export declare const OUTREACH_V2_EVENT_CODES: readonly ["strategy_launched", "strategy_paused", "strategy_resumed", "strategy_mode_changed", "strategy_safety_reverted", "member_admitted", "member_rejected", "action_decided", "verdict_frozen", "verdict_revealed", "divergence_classified", "generation_passed", "generation_failed", "claim_validation_passed", "claim_validation_failed", "review_action", "send_authorized", "send_queued", "send_attempted", "send_succeeded", "send_failed", "send_blocked_by_recheck", "reply_detected", "reply_classified", "suppression_created", "outcome_derived", "outcome_corrected", "breaker_opened", "breaker_closed", "strategy_review_generated", "strategy_review_applied", "voice_profile_updated"];
22
+ export type OutreachV2EventCode = (typeof OUTREACH_V2_EVENT_CODES)[number];
23
+ export declare const OUTREACH_V2_JOB_KINDS: readonly ["prepare_approval", "plan_enrollment", "send_message", "reconcile_send", "classify_reply", "strategy_review", "recommendation_refresh"];
24
+ export type OutreachV2JobKind = (typeof OUTREACH_V2_JOB_KINDS)[number];
25
+ export interface SendMessageJobPayload {
26
+ message_id: string;
27
+ authorized_content_hash: string;
28
+ }
29
+ export interface SendGateContext {
30
+ mode: OutreachMode;
31
+ autopilotScope: OutreachAutopilotScope;
32
+ safetyReverted: boolean;
33
+ /** Cohort key of the enrollment (tier × touch), per §9.3. */
34
+ cohort: OutreachCohortKey;
35
+ /** Is this cohort currently ledger-recommended? */
36
+ cohortRecommended: boolean;
37
+ /** Deterministic eligibility result codes that currently fail (empty = pass). */
38
+ eligibilityFailures: string[];
39
+ /** Independent claim gate passed (§8.3). */
40
+ claimsEntailed: boolean;
41
+ /** Explicit candidate readiness; omitted by historical callers. */
42
+ autopilotQualityReady?: boolean;
43
+ /** Whether the editorial judge must pass before the send can proceed. */
44
+ editorialEvaluation?: "required" | "not_required";
45
+ /** Judge outcome. */
46
+ judgeScore: number | null;
47
+ judgeBlockingReasons: string[];
48
+ /** Versioned Autopilot threshold in force. */
49
+ autopilotJudgeThreshold: number;
50
+ /** Account health is fresh + sendable, caps clear. */
51
+ accountReady: boolean;
52
+ complianceComplete: boolean;
53
+ quietHoursClear: boolean;
54
+ intendedSendAt: string | null;
55
+ }
56
+ export interface SendGateEvaluation {
57
+ verdict: OutreachShadowVerdict;
58
+ reasonCodes: string[];
59
+ judgeScore: number | null;
60
+ riskClass: string | null;
61
+ intendedSendAt: string | null;
62
+ /** True only when verdict is would_send AND mode/scope authorize execution. */
63
+ execute: boolean;
64
+ }
65
+ export interface OutreachCohortKey {
66
+ evidenceTier: OutreachEvidenceTier;
67
+ /** 1 = first touch; 2 = any follow-up. Coarse on purpose. */
68
+ touchClass: 1 | 2;
69
+ }
70
+ export declare function cohortKeyString(key: OutreachCohortKey): string;
71
+ export interface CohortRecommendationDefaults {
72
+ /** Sample: the last N would_send verdicts... */
73
+ sampleSize: number;
74
+ /** ...none older than this many days. */
75
+ recencyDays: number;
76
+ maxFalseAcceptSafety: number;
77
+ maxFalseAcceptStyleRate: number;
78
+ maxEditRate: number;
79
+ }
80
+ export declare const OUTREACH_RECOMMENDATION_DEFAULTS: CohortRecommendationDefaults;
81
+ export declare const OUTREACH_STYLE_DIMENSIONS: readonly ["style", "angle", "cta", "opening", "subject", "tier"];
82
+ export type OutreachStyleDimension = (typeof OUTREACH_STYLE_DIMENSIONS)[number];
83
+ /** `learning` = not enough delivered to say anything; `on_par` = measured, no
84
+ * detectable difference. Neither is ever rendered as a win or a loss. */
85
+ export type OutreachStyleVerdict = "working" | "on_par" | "underperforming" | "learning";
86
+ export interface OutreachStyleRow {
87
+ key: string;
88
+ /** Raw enum parts; the client owns human labels. */
89
+ parts: string[];
90
+ delivered: number;
91
+ replies: number;
92
+ reply_rate: number;
93
+ qualified: number;
94
+ qualified_rate: number;
95
+ opt_outs: number;
96
+ verdict: OutreachStyleVerdict;
97
+ /** Delivered messages still needed before this row can earn a verdict. */
98
+ needed: number;
99
+ }
100
+ export interface OutreachStyleBoard {
101
+ window_days: number;
102
+ min_delivered: number;
103
+ baseline: {
104
+ delivered: number;
105
+ replies: number;
106
+ reply_rate: number;
107
+ qualified: number;
108
+ qualified_rate: number;
109
+ };
110
+ boards: Record<OutreachStyleDimension, OutreachStyleRow[]>;
111
+ }
112
+ export interface OutreachStyleBoardDefaults {
113
+ windowDays: number;
114
+ /** Verdict floor. Below this a row reads "learning", never a rate call. */
115
+ minDelivered: number;
116
+ /** Wilson z. 1.645 = 90% two-sided — the usual working confidence for a
117
+ * readout that informs a human decision rather than gating a send. */
118
+ z: number;
119
+ }
120
+ export declare const OUTREACH_STYLE_BOARD_DEFAULTS: OutreachStyleBoardDefaults;
121
+ export declare const OUTREACH_V2_DEFAULTS: {
122
+ readonly maxTouches: 3;
123
+ readonly campaignWindowDays: 21;
124
+ readonly minTouchSpacingHours: 72;
125
+ readonly cooldownDays: 90;
126
+ readonly evidenceTtlDays: 30;
127
+ readonly approvalTtlDays: 7;
128
+ /** Ramp cap for newly enabled Autopilot strategies (§9.3). Ten days at the
129
+ * same daily cap is the compensating control for the 40-sample
130
+ * recommendation window: ~2x the supervised volume before full rate. */
131
+ readonly autopilotRampDailyCap: 20;
132
+ readonly autopilotRampDays: 10;
133
+ /** Per-company pacing (§7.4). */
134
+ readonly companyConcurrencyCap: 2;
135
+ readonly companyFirstTouchSpacingHours: 24;
136
+ readonly negativeReplyPropagation: true;
137
+ /** Versioned Autopilot judge threshold (never silently lowered, §8.3). */
138
+ readonly autopilotJudgeThreshold: 0.8;
139
+ readonly copilotJudgeThreshold: 0.6;
140
+ /** Reply SLA target inside coverage hours (§5.5), minutes. */
141
+ readonly replySlaMinutes: 15;
142
+ /** OOO auto-resume buffer (§5.5), hours. */
143
+ readonly oooResumeBufferHours: 24;
144
+ /** Each weekly review reads two weeks, so a typical campaign clears the
145
+ * sample floor by its second review. */
146
+ readonly strategyReviewWindowDays: 14;
147
+ /** No review before a campaign has run this long: one week is too thin. */
148
+ readonly strategyReviewMinCampaignDays: 10;
149
+ readonly strategyReviewEveryNDelivered: 100;
150
+ /** Minimum cell size before a Strategy Review may cite a slice (§5.8). */
151
+ readonly strategyReviewMinSample: 20;
152
+ };
153
+ export declare function classifyDivergence(input: {
154
+ verdict: OutreachShadowVerdict | null;
155
+ humanAction: OutreachHumanAction;
156
+ denyReason?: string | null;
157
+ }): OutreachDivergenceClass | null;
158
+ export declare const OUTREACH_AUDIENCE_PUBLICATION_GATE_VERSION = "direct-bridge-ready-v1";
159
+ export interface QualifiedAudienceMemberImport {
160
+ lead_id: string;
161
+ canonical_person_id?: string | null;
162
+ company_id?: string | null;
163
+ evidence_tier: OutreachEvidenceTier;
164
+ qualification_evidence_ids?: string[];
165
+ destinations: {
166
+ email?: string | null;
167
+ linkedin?: string | null;
168
+ };
169
+ relationship_state?: OutreachRelationshipState;
170
+ membership_reason?: string | null;
171
+ }
172
+ export interface QualifiedAudienceSnapshotImport {
173
+ source_type: "saved_snapshot" | "pipeline_filter" | "explicit_selection";
174
+ source_id?: string | null;
175
+ source_version?: string | null;
176
+ qualification_definition_version?: string;
177
+ publication_gate_version?: string;
178
+ fresh_until?: string | null;
179
+ members: QualifiedAudienceMemberImport[];
180
+ }
181
+ export interface OutreachProofClaim {
182
+ id: string;
183
+ text: string;
184
+ type: "customer_result" | "metric" | "capability" | "social_proof";
185
+ customer_ref?: string | null;
186
+ attested_at?: string | null;
187
+ valid_until?: string | null;
188
+ status: "active" | "expired" | "retired";
189
+ }
190
+ /**
191
+ * A claim a message may cite: active, approved no later than `now` and not
192
+ * past its date. An unreadable date counts as not current. The claim writer
193
+ * and the agent's GTM context share this one rule.
194
+ */
195
+ export declare function isCurrentAttestedClaim(claim: {
196
+ status?: string | null;
197
+ attested_at?: string | null;
198
+ valid_until?: string | null;
199
+ }, now: Date): boolean;
200
+ export interface OutreachVoiceProfileContract {
201
+ rules: string[];
202
+ examples: string[];
203
+ version: number;
204
+ sender_name?: string | null;
205
+ sender_role?: string | null;
206
+ sender_company?: string | null;
207
+ sign_off?: string | null;
208
+ cta_style?: string | null;
209
+ website_cta?: {
210
+ url: string | null;
211
+ description?: string | null;
212
+ action_label?: string | null;
213
+ };
214
+ /** Optional per-sending-account overlays — deferred (§21), shape reserved. */
215
+ sender_overrides?: Record<string, {
216
+ rules?: string[];
217
+ examples?: string[];
218
+ }>;
219
+ }
220
+ export interface OutreachComplianceProfile {
221
+ legal_entity?: string | null;
222
+ postal_address?: string | null;
223
+ opt_out_method?: string | null;
224
+ /** Campaign render input only: the sender's own opt-out sentence. */
225
+ opt_out_text?: string | null;
226
+ regions?: string[];
227
+ }
228
+ export declare function complianceProfileComplete(c: OutreachComplianceProfile | null | undefined): boolean;
229
+ export declare const OUTREACH_EVIDENCE_TYPES: readonly ["funding", "hiring", "job_post", "product_launch", "tech_stack", "exec_move", "news", "content", "intent", "role", "company_fact"];
230
+ export type OutreachEvidenceType = (typeof OUTREACH_EVIDENCE_TYPES)[number];
231
+ export declare const OUTREACH_CTA_TYPES: readonly ["soft_interest", "question", "resource_offer", "meeting_ask", "referral_ask", "website_visit"];
232
+ export type OutreachCtaType = (typeof OUTREACH_CTA_TYPES)[number];
233
+ export declare const OUTREACH_ANGLES: readonly ["signal_observation", "role_problem_hypothesis", "proof_led", "referral_ask"];
234
+ export type OutreachAngle = (typeof OUTREACH_ANGLES)[number];
235
+ export interface OutreachMessageAttributes {
236
+ /** Composer contract that produced the persisted subject/body bytes. */
237
+ message_composition_policy_version?: string;
238
+ /** Free prose from the generator — unique per message, for the reviewer. */
239
+ angle: OutreachAngle | string;
240
+ /** The sliceable class derived from proof/CTA/tier. Analysis groups on this;
241
+ * grouping on `angle` yields one row per message and never earns a verdict. */
242
+ angle_class?: OutreachAngle | string;
243
+ cta_type: OutreachCtaType | string;
244
+ proof_claim_ids?: string[];
245
+ opening_style?: "observation" | "question" | "direct" | string;
246
+ subject_style?: "topic" | "question" | "name_drop" | "plain" | string;
247
+ /** Coarse, deterministic job family. This is never a personality inference. */
248
+ role_family?: "executive" | "sales" | "marketing" | "operations" | "product_technical" | "other" | string;
249
+ evidence_tier?: "signal_led" | "role_problem_hypothesis" | "insufficient" | string;
250
+ /** Human edits remain visible in reporting but are excluded from automatic promotion. */
251
+ human_edited?: boolean;
252
+ }
253
+ export declare function canonicalMessageContent(subject: string | null, body: string): string;
@@ -0,0 +1,75 @@
1
+ /**
2
+ * What each plan includes beyond its monthly credits. The database holds the
3
+ * same numbers (public.plan_outreach_account_limit_v1,
4
+ * public.plan_outreach_sender_total_v1,
5
+ * public.plan_included_contacts_per_company_v1,
6
+ * public.plan_ai_action_allowance_v1 and public.plan_workspace_daily_limit_v1)
7
+ * and enforces them; supabase/tests/plan_entitlements_test.sql,
8
+ * ai_action_allowance_test.sql and workspace_daily_limits_test.sql keep the
9
+ * two equal. Staff can
10
+ * raise one workspace above its plan, never lower it below.
11
+ */
12
+ export type PlanEntitlementTier = "free" | "launch" | "growth" | "accelerate" | "scale";
13
+ export type PlanEntitlements = {
14
+ emailInboxes: number;
15
+ linkedinAccounts: number;
16
+ /**
17
+ * Senders in total across both channels, or null when each channel has its
18
+ * own allowance. Free has one: an email inbox or a LinkedIn account.
19
+ * Add-ons and staff raises come on top.
20
+ */
21
+ sendersTotal: number | null;
22
+ contactsPerAccount: number;
23
+ /** AI-written contact sequences a calendar month (UTC); unused ones lapse. */
24
+ aiActionsPerMonth: number;
25
+ /**
26
+ * Small AI requests that use no AI action (reply suggestions, AI search,
27
+ * account summaries and the like), per aiRequestWindow. null: not limited.
28
+ */
29
+ aiRequests: number | null;
30
+ /** A UTC day, or a UTC week from Monday. */
31
+ aiRequestWindow: "day" | "week";
32
+ /** Find a person lookups, hit or miss, in any 24 hours. */
33
+ findPersonPerDay: number;
34
+ /**
35
+ * People in the workspace: its owner, each teammate and each pending
36
+ * invitation. 1 means the owner works alone, with no team.
37
+ */
38
+ teamSeats: number;
39
+ /** Whether the workspace can buy seats on top, a seat a month each. */
40
+ extraTeamSeats: boolean;
41
+ };
42
+ export declare const PLAN_ENTITLEMENTS: Record<PlanEntitlementTier, PlanEntitlements>;
43
+ export declare function planEntitlements(plan: string | null | undefined): PlanEntitlements;
44
+ export type SenderChannel = "email" | "linkedin";
45
+ export type SenderChannelState = {
46
+ addon: number;
47
+ base: number;
48
+ connected: number;
49
+ };
50
+ /**
51
+ * The add-ons a channel cannot drop below, given the add-ons chosen for the
52
+ * other channel: removing a paid slot a connected sender uses would leave the
53
+ * workspace over its limit. With a shared sender total (Free's one inbox or
54
+ * LinkedIn account) the plan's sender covers either channel, so a channel's
55
+ * share is what the total leaves after the other channel's senders that its
56
+ * add-ons do not pay for, as public.outreach_account_limit_v1 computes for a
57
+ * plan whose per-channel allowance equals its total (Free's does). An add-on
58
+ * increase on the other channel counts only once paid, so it never covers a
59
+ * removal here.
60
+ */
61
+ export declare function minimumSenderAddon(channel: SenderChannel, channels: Record<SenderChannel, SenderChannelState>, senderTotal: number | null, otherTarget: number): number;
62
+ /**
63
+ * The bought seats a workspace cannot drop below: removing a paid seat that a
64
+ * teammate or a pending invitation uses would leave the team over its limit.
65
+ * Seats that count nothing (a lapsed subscription's) can always be removed,
66
+ * and so can every seat on a plan that sells none, such as after a downgrade
67
+ * from Accelerate. Nobody is removed either way, so a team that plan does not
68
+ * include never has to keep paying for its seats.
69
+ */
70
+ export declare function minimumTeamSeatAddon(seats: {
71
+ plan: string | null | undefined;
72
+ planSeats: number;
73
+ addonSeats: number;
74
+ seatsUsed: number;
75
+ }): number;
@@ -0,0 +1,50 @@
1
+ export declare const plural: (count: number, one: string, many: string) => string;
2
+ /** What one AI action buys, as the metering counts it, for the pricing page and /pricing.md. */
3
+ export declare const AI_ACTION_DEFINITION = "One AI action writes one personalized outreach sequence for one contact in a campaign, follow-ups included, or gives one sales assistant answer or scheduled run.";
4
+ /**
5
+ * Credits Signals charges for each watched account a week. Mirrors
6
+ * `ACCOUNT_MONITORING_WEEKLY_CREDITS` in `account-monitoring.ts`, which this
7
+ * file can't import without zod; a spec keeps them equal.
8
+ */
9
+ export declare const SIGNALS_WEEKLY_CREDITS = 2;
10
+ /** What Signals watches, for the pricing page and /pricing.md. */
11
+ export declare const SIGNALS_DESCRIPTION = "Signals checks the accounts you choose and their contacts every week, and alerts you to new job postings, funding, company news, new technology and role changes.";
12
+ /** Signals as a plan item. It names its cost, because no plan includes Signals without credits. */
13
+ export declare const SIGNALS_FEATURE: string;
14
+ export declare const PLAN_FEATURES: {
15
+ readonly free: readonly ["Company & contact pipeline", "Email & LinkedIn outreach", string, string, "Personalized sequences & follow-ups", "Reply & campaign tracking", "AI pipeline search", string, "HubSpot, Slack & webhooks", "Excel (.xlsx) export"];
16
+ readonly launch: readonly ["Everything in Free", string, string, string, "Verified, intent-scored accounts", "25+ buying signal sources", "Company & role targeting", "Email support"];
17
+ readonly accelerate: readonly ["Everything in Free", string, string, string, string, "Verified, intent-scored accounts", "25+ buying signal sources", "Company & role targeting", "Onboarding & priority email support"];
18
+ readonly scale: readonly ["Everything in Free", "Verified, intent-scored accounts", "25+ buying signal sources", "Company & role targeting", "Custom LinkedIn & email account limits", "Custom team size", "Onboarding & priority email support", "Dedicated account manager", "Custom integrations"];
19
+ };
20
+ /**
21
+ * Mirrors the request credit costs in `request-credit-budget.ts` and
22
+ * `company-request-contacts.ts`, which this file can't import without zod; a
23
+ * spec keeps them equal.
24
+ */
25
+ export declare const SOLUTION_CREDIT_COSTS: {
26
+ readonly account: 10;
27
+ readonly extraContact: 5;
28
+ readonly mobileNumber: 1;
29
+ };
30
+ /** Accounts with one contact each that a month's credits buy. */
31
+ export declare const accountsPerMonth: (monthlyCredits: number) => number;
32
+ /**
33
+ * Add-ons at their lowest step. Senders and packs are sold on every plan,
34
+ * Free included; an extra team seat only on a plan with `extraTeamSeats`.
35
+ * Mirrors `SENDER_ADDONS`, `TEAM_SEAT_ADDON` and the pack definitions in
36
+ * `server/billing/catalog.ts`; a spec keeps them equal.
37
+ */
38
+ export declare const ADD_ON_PRICES: {
39
+ readonly emailInboxMonthlyUsd: 10;
40
+ readonly linkedinAccountMonthlyUsd: 40;
41
+ readonly teamSeatMonthlyUsd: 20;
42
+ readonly aiPack: {
43
+ readonly actions: 200;
44
+ readonly usd: 10;
45
+ };
46
+ readonly leadPack: {
47
+ readonly credits: 100;
48
+ readonly usd: 60;
49
+ };
50
+ };
@@ -0,0 +1,13 @@
1
+ export declare const COMPANY_REQUEST_CREDIT_COST = 10;
2
+ export declare const COMPANY_REQUEST_MIN_COMPANIES = 10;
3
+ export declare const COMPANY_REQUEST_MAX_COMPANIES = 100;
4
+ /** Lead Search: credits for each contact a search delivers (Gavin, Nov 11 2026). Nothing charges it yet. */
5
+ export declare const LEAD_SEARCH_CONTACT_CREDIT_COST = 2;
6
+ export declare const COMPANY_REQUEST_MIN_CREDITS: number;
7
+ export declare const COMPANY_REQUEST_MAX_CREDITS: number;
8
+ /**
9
+ * Find a person holds this many credits per lookup and keeps them only when a
10
+ * verified email is found. start_find_person_lookup holds the same amount.
11
+ */
12
+ export declare const FIND_PERSON_CREDIT_COST = 1;
13
+ export declare const FIND_PERSON_CREDIT_LABEL: string;
@@ -0,0 +1,9 @@
1
+ /** True when a sentence's English function words outnumber common Spanish, French, German,
2
+ * Nordic and Italian ones. */
3
+ export declare function websiteFactReadsAsEnglish(text: string): boolean;
4
+ /**
5
+ * True when a fact a person entered is clearly in another language: its non-English function words
6
+ * outnumber English ones, or most of its letters are not Latin. Unlike the scan's check, a short
7
+ * English fact without function words ("Search answers in under fifty milliseconds.") passes.
8
+ */
9
+ export declare function websiteFactIsNotEnglish(text: string): boolean;