@sellable/mcp 0.1.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 (200) hide show
  1. package/.claude-plugin/plugin.json +12 -0
  2. package/.mcp.json +9 -0
  3. package/README.md +355 -0
  4. package/dist/api.d.ts +21 -0
  5. package/dist/api.js +73 -0
  6. package/dist/auth.d.ts +60 -0
  7. package/dist/auth.js +246 -0
  8. package/dist/engage-memory.d.ts +63 -0
  9. package/dist/engage-memory.js +354 -0
  10. package/dist/index-dev.d.ts +2 -0
  11. package/dist/index-dev.js +17 -0
  12. package/dist/index.d.ts +7 -0
  13. package/dist/index.js +8 -0
  14. package/dist/server.d.ts +1 -0
  15. package/dist/server.js +499 -0
  16. package/dist/skills.d.ts +11 -0
  17. package/dist/skills.js +97 -0
  18. package/dist/tools/auth.d.ts +30 -0
  19. package/dist/tools/auth.js +124 -0
  20. package/dist/tools/blueprint-commit.d.ts +174 -0
  21. package/dist/tools/blueprint-commit.js +286 -0
  22. package/dist/tools/bootstrap.d.ts +64 -0
  23. package/dist/tools/bootstrap.js +246 -0
  24. package/dist/tools/campaigns.d.ts +589 -0
  25. package/dist/tools/campaigns.js +892 -0
  26. package/dist/tools/cells.d.ts +58 -0
  27. package/dist/tools/cells.js +48 -0
  28. package/dist/tools/context.d.ts +88 -0
  29. package/dist/tools/context.js +271 -0
  30. package/dist/tools/csv-domains.d.ts +73 -0
  31. package/dist/tools/csv-domains.js +464 -0
  32. package/dist/tools/csv-linkedin.d.ts +102 -0
  33. package/dist/tools/csv-linkedin.js +712 -0
  34. package/dist/tools/direct-campaigns.d.ts +240 -0
  35. package/dist/tools/direct-campaigns.js +250 -0
  36. package/dist/tools/engage-bootstrap.d.ts +94 -0
  37. package/dist/tools/engage-bootstrap.js +205 -0
  38. package/dist/tools/engage-discovery.d.ts +78 -0
  39. package/dist/tools/engage-discovery.js +150 -0
  40. package/dist/tools/engage-memory.d.ts +181 -0
  41. package/dist/tools/engage-memory.js +143 -0
  42. package/dist/tools/engage-state.d.ts +72 -0
  43. package/dist/tools/engage-state.js +62 -0
  44. package/dist/tools/enrichment.d.ts +167 -0
  45. package/dist/tools/enrichment.js +174 -0
  46. package/dist/tools/flow-preflight.d.ts +68 -0
  47. package/dist/tools/flow-preflight.js +138 -0
  48. package/dist/tools/framework.d.ts +44 -0
  49. package/dist/tools/framework.js +153 -0
  50. package/dist/tools/interaction-mode.d.ts +27 -0
  51. package/dist/tools/interaction-mode.js +102 -0
  52. package/dist/tools/leads.d.ts +2417 -0
  53. package/dist/tools/leads.js +2307 -0
  54. package/dist/tools/linkedin.d.ts +210 -0
  55. package/dist/tools/linkedin.js +229 -0
  56. package/dist/tools/navigation.d.ts +91 -0
  57. package/dist/tools/navigation.js +381 -0
  58. package/dist/tools/one-off.d.ts +229 -0
  59. package/dist/tools/one-off.js +273 -0
  60. package/dist/tools/processing.d.ts +70 -0
  61. package/dist/tools/processing.js +56 -0
  62. package/dist/tools/prompts.d.ts +211 -0
  63. package/dist/tools/prompts.js +210 -0
  64. package/dist/tools/provider-preflight.d.ts +21 -0
  65. package/dist/tools/provider-preflight.js +59 -0
  66. package/dist/tools/readiness.d.ts +261 -0
  67. package/dist/tools/readiness.js +510 -0
  68. package/dist/tools/rows.d.ts +126 -0
  69. package/dist/tools/rows.js +105 -0
  70. package/dist/tools/rubrics.d.ts +497 -0
  71. package/dist/tools/rubrics.js +681 -0
  72. package/dist/tools/senders.d.ts +44 -0
  73. package/dist/tools/senders.js +69 -0
  74. package/dist/tools/sequencer.d.ts +127 -0
  75. package/dist/tools/sequencer.js +194 -0
  76. package/dist/tools/tables.d.ts +35 -0
  77. package/dist/tools/tables.js +36 -0
  78. package/dist/tools/verify-row.d.ts +36 -0
  79. package/dist/tools/verify-row.js +38 -0
  80. package/dist/tools/workspaces.d.ts +140 -0
  81. package/dist/tools/workspaces.js +139 -0
  82. package/dist/utils/workspace-root.d.ts +1 -0
  83. package/dist/utils/workspace-root.js +39 -0
  84. package/package.json +46 -0
  85. package/skills/building-gtm-tables/SKILL.md +216 -0
  86. package/skills/building-gtm-tables/core/auto-execute.yaml +19 -0
  87. package/skills/building-gtm-tables/core/blueprint-schema.json +72 -0
  88. package/skills/building-gtm-tables/references/brief-to-blueprint.md +334 -0
  89. package/skills/building-gtm-tables/references/column-type-catalog.md +318 -0
  90. package/skills/building-gtm-tables/references/common-blueprints.fixtures.ts +199 -0
  91. package/skills/building-gtm-tables/references/common-blueprints.md +44 -0
  92. package/skills/building-gtm-tables/references/failure-taxonomy.md +197 -0
  93. package/skills/building-gtm-tables/references/uat-seed-prompts.md +37 -0
  94. package/skills/building-gtm-tables/references/verify-loop.md +74 -0
  95. package/skills/campaign-messages/SKILL.md +173 -0
  96. package/skills/campaign-messages/flow.v1.json +75 -0
  97. package/skills/craft-message/SKILL.md +401 -0
  98. package/skills/create-campaign/ARCHITECTURE.md +232 -0
  99. package/skills/create-campaign/DISCUSS.md +296 -0
  100. package/skills/create-campaign/FLOW_ASCII.md +240 -0
  101. package/skills/create-campaign/HOST-PARITY-CHECKLIST.md +49 -0
  102. package/skills/create-campaign/README.md +142 -0
  103. package/skills/create-campaign/SKILL.md +286 -0
  104. package/skills/create-campaign/context/README.md +67 -0
  105. package/skills/create-campaign/context/_TEMPLATE.md +12 -0
  106. package/skills/create-campaign/context/context.md +35 -0
  107. package/skills/create-campaign/context/learnings.md +16 -0
  108. package/skills/create-campaign/context/registry.json +19 -0
  109. package/skills/create-campaign/core/flow.v1.json +217 -0
  110. package/skills/create-campaign/core/policy.md +191 -0
  111. package/skills/create-campaign/core/providers/apollo.json +35 -0
  112. package/skills/create-campaign/core/providers/prospeo.json +34 -0
  113. package/skills/create-campaign/core/providers/registry.json +31 -0
  114. package/skills/create-campaign/core/providers/sales-nav.json +37 -0
  115. package/skills/create-campaign/core/providers/signal-discovery.json +42 -0
  116. package/skills/create-campaign/references/brief-template.md +64 -0
  117. package/skills/create-campaign/references/campaign-quality.md +84 -0
  118. package/skills/create-campaign/references/copy-calibration-examples.md +120 -0
  119. package/skills/create-campaign/references/offer-patterns.md +108 -0
  120. package/skills/create-campaign/references/provider-selection-strategy.md +212 -0
  121. package/skills/create-campaign/references/question-examples.md +167 -0
  122. package/skills/create-campaign/references/token-fill-examples.md +81 -0
  123. package/skills/create-campaign-brief/ARCHITECTURE.md +72 -0
  124. package/skills/create-campaign-brief/DISCUSS.md +64 -0
  125. package/skills/create-campaign-brief/README.md +176 -0
  126. package/skills/create-campaign-brief/SKILL.md +537 -0
  127. package/skills/create-campaign-brief/references/brief-synthesis-rules.md +100 -0
  128. package/skills/create-campaign-brief/references/brief-template.md +220 -0
  129. package/skills/create-campaign-brief/references/campaign-idea-options.md +30 -0
  130. package/skills/create-campaign-brief/references/copy-appendix-template.md +62 -0
  131. package/skills/create-campaign-brief/references/draft-lifecycle.md +23 -0
  132. package/skills/create-campaign-brief/references/examples/MANIFEST.json +89 -0
  133. package/skills/create-campaign-brief/references/examples/briefs/clover.md +223 -0
  134. package/skills/create-campaign-brief/references/examples/briefs/galley.md +222 -0
  135. package/skills/create-campaign-brief/references/examples/briefs/gelee.md +220 -0
  136. package/skills/create-campaign-brief/references/examples/briefs/hey-digital.md +234 -0
  137. package/skills/create-campaign-brief/references/examples/briefs/persona.md +231 -0
  138. package/skills/create-campaign-brief/references/examples/briefs/revvix.md +220 -0
  139. package/skills/create-campaign-brief/references/examples/briefs/sellable-dev.md +220 -0
  140. package/skills/create-campaign-brief/references/examples/briefs/superposition.md +233 -0
  141. package/skills/create-campaign-brief/references/examples/briefs/superpower.md +219 -0
  142. package/skills/create-campaign-brief/references/examples/briefs/westpark-villas.md +220 -0
  143. package/skills/create-campaign-brief/references/icp-lock-question-bank.md +43 -0
  144. package/skills/create-campaign-brief/references/messaging-inputs.md +58 -0
  145. package/skills/create-campaign-brief/references/output-acceptance-rubric.md +62 -0
  146. package/skills/create-campaign-brief/references/phase75-active-runtime-message-pack.md +248 -0
  147. package/skills/create-campaign-brief/references/phase75-canonical-brief-template.md +319 -0
  148. package/skills/create-campaign-brief/references/phase75-good-brief-and-messaging-examples.md +445 -0
  149. package/skills/create-campaign-brief/references/quick-research-protocol.md +39 -0
  150. package/skills/create-campaign-brief/references/reference-sheet-protocol.md +60 -0
  151. package/skills/create-campaign-brief/references/zero-shot-iteration-rules.md +66 -0
  152. package/skills/create-campaign-v2/SKILL.md +1619 -0
  153. package/skills/create-campaign-v2/core/auto-execute.README.md +219 -0
  154. package/skills/create-campaign-v2/core/auto-execute.yaml +121 -0
  155. package/skills/create-campaign-v2/core/flow.v2.json +1643 -0
  156. package/skills/create-campaign-v2/core/policy.md +82 -0
  157. package/skills/create-campaign-v2/references/ai-tells.md +253 -0
  158. package/skills/create-campaign-v2/references/approval-gate-framing.md +346 -0
  159. package/skills/create-campaign-v2/references/draft-lifecycle.md +110 -0
  160. package/skills/create-campaign-v2/references/escalation-ladder.md +119 -0
  161. package/skills/create-campaign-v2/references/filter-leads.md +495 -0
  162. package/skills/create-campaign-v2/references/final-handoff-contract.md +176 -0
  163. package/skills/create-campaign-v2/references/gold-standard-message-examples.md +394 -0
  164. package/skills/create-campaign-v2/references/gold-standard-message-patterns.md +314 -0
  165. package/skills/create-campaign-v2/references/gold-standard-message-validation-example.md +212 -0
  166. package/skills/create-campaign-v2/references/lead-validation-preview.md +172 -0
  167. package/skills/create-campaign-v2/references/parallel-critique-protocol.md +368 -0
  168. package/skills/create-campaign-v2/references/sample-validation-loop.md +289 -0
  169. package/skills/create-campaign-v2/references/step-13-import-leads.md +151 -0
  170. package/skills/create-campaign-v2/references/step-15-re-cascade.md +90 -0
  171. package/skills/create-campaign-v2/references/thomas-revision-filters.md +521 -0
  172. package/skills/create-campaign-v2/references/thomas-variant-selection.md +202 -0
  173. package/skills/create-campaign-v2/references/tier-routing-matrix.md +66 -0
  174. package/skills/create-campaign-v2/references/validation-criteria.md +367 -0
  175. package/skills/create-campaign-v2/references/watch-link-handoff.md +106 -0
  176. package/skills/create-campaign-v2-validation/SKILL.md +296 -0
  177. package/skills/create-post/SKILL.md +1308 -0
  178. package/skills/create-rubric/SKILL.md +251 -0
  179. package/skills/engage/SKILL.md +549 -0
  180. package/skills/engage/core/README.md +23 -0
  181. package/skills/engage/core/proven-searches.json +11 -0
  182. package/skills/engage/core/style-guide.template.md +47 -0
  183. package/skills/engage/core/tracked-people.json +10 -0
  184. package/skills/enrich-prospects/SKILL.md +97 -0
  185. package/skills/find-leads/SKILL.md +467 -0
  186. package/skills/generate-messages/SKILL.md +2361 -0
  187. package/skills/interview/SKILL.md +132 -0
  188. package/skills/interview/core/ENGAGE_STYLE_GUIDE.template.md +54 -0
  189. package/skills/interview/core/ICP.template.md +54 -0
  190. package/skills/interview/core/VOICE_PROFILE.template.md +101 -0
  191. package/skills/providers/apollo.md +520 -0
  192. package/skills/providers/prospeo.md +398 -0
  193. package/skills/providers/sales-nav.md +372 -0
  194. package/skills/providers/signal-discovery.md +495 -0
  195. package/skills/research/SKILL.md +258 -0
  196. package/skills/research/config.json +9 -0
  197. package/skills/research/override.md +13 -0
  198. package/skills/research-prospect/SKILL.md +99 -0
  199. package/skills/research-sender/SKILL.md +158 -0
  200. package/skills/workflow-sequences/SKILL.md +85 -0
@@ -0,0 +1,510 @@
1
+ import { getApi } from "../api.js";
2
+ import { hydrateCampaignContextFromNavigation } from "./context.js";
3
+ import { getCampaignNavigationState } from "./navigation.js";
4
+ const DEFAULT_TIMEOUT_MS = 90000;
5
+ const DEFAULT_INTERVAL_MS = 2000;
6
+ const TOOL_CALL_TIMEOUT_GUARD_MS = 55000;
7
+ const SIGNAL_PENDING_STALL_MS = 20000;
8
+ function normalizeLeadSourceProvider(provider) {
9
+ if (!provider)
10
+ return undefined;
11
+ if (provider === "apollo-ai" || provider === "apollo")
12
+ return "apollo";
13
+ if (provider === "sales-nav")
14
+ return "sales-nav";
15
+ if (provider === "prospeo")
16
+ return "prospeo";
17
+ if (provider === "signal-discovery")
18
+ return "signal-discovery";
19
+ if (provider === "csv-linkedin")
20
+ return "csv-linkedin";
21
+ return undefined;
22
+ }
23
+ function sleep(ms) {
24
+ return new Promise((resolve) => setTimeout(resolve, ms));
25
+ }
26
+ export function resolveWaitTimeout(timeoutMs) {
27
+ const requestedTimeoutMs = Math.max(5000, timeoutMs ?? DEFAULT_TIMEOUT_MS);
28
+ const effectiveTimeoutMs = Math.min(requestedTimeoutMs, TOOL_CALL_TIMEOUT_GUARD_MS);
29
+ return {
30
+ requestedTimeoutMs,
31
+ effectiveTimeoutMs,
32
+ guardApplied: requestedTimeoutMs > effectiveTimeoutMs,
33
+ };
34
+ }
35
+ function hasNonWaitableMissing(missing) {
36
+ const nonWaitable = new Set([
37
+ "clientProspectId",
38
+ "campaignBrief.content",
39
+ "leadSourceType",
40
+ 'leadSourceType="new"',
41
+ "leadSourceProvider",
42
+ ]);
43
+ return missing.some((m) => nonWaitable.has(m));
44
+ }
45
+ export const readinessToolDefinitions = [
46
+ {
47
+ name: "wait_for_campaign_table_ready",
48
+ description: "Poll backend state until the campaign workflow table exists and (by default) has rows. Use after confirm_lead_list before advancing steps.",
49
+ inputSchema: {
50
+ type: "object",
51
+ properties: {
52
+ campaignId: {
53
+ type: "string",
54
+ description: "Campaign offer ID",
55
+ },
56
+ timeoutMs: {
57
+ type: "number",
58
+ description: `Maximum time to wait in ms (default ${DEFAULT_TIMEOUT_MS})`,
59
+ },
60
+ intervalMs: {
61
+ type: "number",
62
+ description: `Polling interval in ms (default ${DEFAULT_INTERVAL_MS})`,
63
+ },
64
+ requireRows: {
65
+ type: "boolean",
66
+ description: "When true (default), wait for at least one row to appear.",
67
+ },
68
+ },
69
+ required: ["campaignId"],
70
+ additionalProperties: false,
71
+ },
72
+ },
73
+ {
74
+ name: "wait_for_lead_list_ready",
75
+ description: "Poll the lead list table until the import completes (default) and rows appear. Use right after import_leads before asking the user to confirm the list.",
76
+ inputSchema: {
77
+ type: "object",
78
+ properties: {
79
+ leadListId: {
80
+ type: "string",
81
+ description: "Lead list table ID. If omitted, uses campaignOfferId to read selectedLeadListId.",
82
+ },
83
+ campaignOfferId: {
84
+ type: "string",
85
+ description: "Campaign offer ID (used to resolve selectedLeadListId if leadListId is missing).",
86
+ },
87
+ provider: {
88
+ type: "string",
89
+ description: "Lead provider (apollo, sales-nav, prospeo, signal-discovery). If omitted, derived from campaignOfferId when possible.",
90
+ },
91
+ jobId: {
92
+ type: "string",
93
+ description: "Import job ID for providers that require it (sales-nav, prospeo).",
94
+ },
95
+ targetLeadCount: {
96
+ type: "number",
97
+ description: "Target number of leads requested. Used as a fallback completion check when status is unavailable.",
98
+ },
99
+ timeoutMs: {
100
+ type: "number",
101
+ description: `Maximum time to wait in ms (default ${DEFAULT_TIMEOUT_MS})`,
102
+ },
103
+ intervalMs: {
104
+ type: "number",
105
+ description: `Polling interval in ms (default ${DEFAULT_INTERVAL_MS})`,
106
+ },
107
+ requireRows: {
108
+ type: "boolean",
109
+ description: "When true (default), wait for at least one row to appear.",
110
+ },
111
+ requireComplete: {
112
+ type: "boolean",
113
+ description: "When true (default), wait until the import job completes (or hits targetLeadCount when status is unavailable).",
114
+ },
115
+ },
116
+ required: [],
117
+ additionalProperties: false,
118
+ },
119
+ },
120
+ ];
121
+ export async function waitForCampaignTableReady(input) {
122
+ const { effectiveTimeoutMs, guardApplied, requestedTimeoutMs } = resolveWaitTimeout(input.timeoutMs);
123
+ const intervalMs = Math.max(500, input.intervalMs ?? DEFAULT_INTERVAL_MS);
124
+ const requireRows = input.requireRows !== false;
125
+ const start = Date.now();
126
+ let attempts = 0;
127
+ let lastState = null;
128
+ while (Date.now() - start <= effectiveTimeoutMs) {
129
+ attempts += 1;
130
+ const navState = await getCampaignNavigationState({
131
+ campaignId: input.campaignId,
132
+ includeTableCheck: true,
133
+ });
134
+ lastState = navState;
135
+ if (hasNonWaitableMissing(navState.missing)) {
136
+ return {
137
+ ready: false,
138
+ reason: "blocked_by_prerequisites",
139
+ attempts,
140
+ elapsedMs: Date.now() - start,
141
+ navigation: navState,
142
+ };
143
+ }
144
+ const hasTable = Boolean(navState.table.workflowTableId);
145
+ const hasRows = navState.table.hasRows === true;
146
+ const rowsSatisfied = requireRows ? hasRows : hasTable;
147
+ const missingTableBits = navState.missing.filter((m) => m === "workflowTableId" || m === "workflowTableRows");
148
+ if (hasTable && rowsSatisfied && missingTableBits.length === 0) {
149
+ const entry = hydrateCampaignContextFromNavigation(navState, "wait_for_campaign_table_ready");
150
+ return {
151
+ ready: true,
152
+ attempts,
153
+ elapsedMs: Date.now() - start,
154
+ navigation: entry.navState,
155
+ };
156
+ }
157
+ await sleep(intervalMs);
158
+ }
159
+ return {
160
+ ready: false,
161
+ reason: guardApplied ? "tool_timeout_guard" : "timeout",
162
+ attempts,
163
+ elapsedMs: Date.now() - start,
164
+ navigation: lastState,
165
+ warning: guardApplied
166
+ ? `Stopped after ${effectiveTimeoutMs}ms to avoid host tool timeout (requested ${requestedTimeoutMs}ms).`
167
+ : undefined,
168
+ };
169
+ }
170
+ export async function waitForLeadListReady(input) {
171
+ const api = getApi();
172
+ const { effectiveTimeoutMs, guardApplied, requestedTimeoutMs } = resolveWaitTimeout(input.timeoutMs);
173
+ const intervalMs = Math.max(500, input.intervalMs ?? DEFAULT_INTERVAL_MS);
174
+ const requireRows = input.requireRows !== false;
175
+ const requireComplete = input.requireComplete !== false;
176
+ let leadListId = input.leadListId;
177
+ let provider = input.provider;
178
+ let targetLeadCount = input.targetLeadCount;
179
+ if (input.campaignOfferId && (!leadListId || !provider)) {
180
+ const campaign = await api.get(`/api/v2/campaign-offers/${input.campaignOfferId}`);
181
+ if (!leadListId) {
182
+ leadListId = campaign.selectedLeadListId ?? undefined;
183
+ }
184
+ if (!provider) {
185
+ provider = normalizeLeadSourceProvider(campaign.leadSourceProvider);
186
+ }
187
+ }
188
+ if (!leadListId) {
189
+ return {
190
+ ready: false,
191
+ reason: "missing_lead_list_id",
192
+ attempts: 0,
193
+ elapsedMs: 0,
194
+ leadListId: null,
195
+ };
196
+ }
197
+ const start = Date.now();
198
+ let attempts = 0;
199
+ let lastRowCount = null;
200
+ let lastError = null;
201
+ let lastStatus = null;
202
+ let missingJobId = false;
203
+ let signalPendingSince = null;
204
+ let signalPendingFingerprint = null;
205
+ while (Date.now() - start <= effectiveTimeoutMs) {
206
+ attempts += 1;
207
+ try {
208
+ const meta = await api.get(`/api/v3/workflow-tables/${leadListId}?mode=meta`);
209
+ const rowCount = meta?.rowCount ?? 0;
210
+ lastRowCount = rowCount;
211
+ const config = meta?.table?.config ?? null;
212
+ const configStatus = config?.importStatus ?? null;
213
+ const configError = config?.importError ?? null;
214
+ const importProgress = config?.importProgress ?? null;
215
+ const configTarget = typeof config?.targetLeadCount === "number"
216
+ ? config.targetLeadCount
217
+ : typeof importProgress?.total === "number"
218
+ ? (importProgress?.total ?? null)
219
+ : null;
220
+ if (!provider && config?.importProvider) {
221
+ provider =
222
+ normalizeLeadSourceProvider(config.importProvider) ?? provider;
223
+ }
224
+ if (typeof configTarget === "number" && !targetLeadCount) {
225
+ targetLeadCount = configTarget;
226
+ }
227
+ let jobId = input.jobId;
228
+ if (!jobId && typeof config?.importJobId === "string") {
229
+ jobId = config.importJobId;
230
+ }
231
+ let importComplete = !requireComplete;
232
+ let statusTarget = null;
233
+ if (requireComplete) {
234
+ if (configStatus === "error" && configError === "Import cancelled.") {
235
+ return {
236
+ ready: false,
237
+ reason: "cancelled",
238
+ leadListId,
239
+ provider: provider ?? null,
240
+ attempts,
241
+ elapsedMs: Date.now() - start,
242
+ rowCount,
243
+ status: "CANCELLED",
244
+ targetLeadCount,
245
+ };
246
+ }
247
+ if (configError && configStatus !== "complete") {
248
+ return {
249
+ ready: false,
250
+ reason: "import_failed",
251
+ leadListId,
252
+ provider: provider ?? null,
253
+ attempts,
254
+ elapsedMs: Date.now() - start,
255
+ rowCount,
256
+ status: configStatus,
257
+ targetLeadCount,
258
+ error: configError,
259
+ };
260
+ }
261
+ if (configStatus) {
262
+ lastStatus = configStatus;
263
+ if (configStatus === "error") {
264
+ return {
265
+ ready: false,
266
+ reason: "import_failed",
267
+ leadListId,
268
+ provider: provider ?? null,
269
+ attempts,
270
+ elapsedMs: Date.now() - start,
271
+ rowCount,
272
+ status: configStatus,
273
+ targetLeadCount,
274
+ error: configError,
275
+ };
276
+ }
277
+ if (configStatus === "complete") {
278
+ importComplete = true;
279
+ }
280
+ }
281
+ // If config didn't provide a status, fall back to provider-specific checks.
282
+ if (!configStatus) {
283
+ if (provider === "apollo") {
284
+ const statusResponse = await api.get(`/api/v3/lead-lists/${leadListId}/apollo-import/status`);
285
+ const status = statusResponse?.status ?? null;
286
+ lastStatus = status;
287
+ statusTarget = statusResponse?.progress?.targetLeadCount ?? null;
288
+ if (typeof statusTarget === "number" && !targetLeadCount) {
289
+ targetLeadCount = statusTarget;
290
+ }
291
+ if (status === "FAILED") {
292
+ return {
293
+ ready: false,
294
+ reason: "import_failed",
295
+ leadListId,
296
+ provider: provider ?? null,
297
+ attempts,
298
+ elapsedMs: Date.now() - start,
299
+ rowCount,
300
+ status,
301
+ targetLeadCount,
302
+ error: statusResponse?.lastError ?? null,
303
+ };
304
+ }
305
+ if (status === "CANCELLED") {
306
+ return {
307
+ ready: false,
308
+ reason: "cancelled",
309
+ leadListId,
310
+ provider: provider ?? null,
311
+ attempts,
312
+ elapsedMs: Date.now() - start,
313
+ rowCount,
314
+ status,
315
+ targetLeadCount,
316
+ };
317
+ }
318
+ if (status === "COMPLETED") {
319
+ importComplete = true;
320
+ }
321
+ else if (status === "NO_JOB" && rowCount > 0) {
322
+ importComplete = true;
323
+ }
324
+ }
325
+ else if (provider === "sales-nav") {
326
+ if (jobId) {
327
+ const statusResponse = await api.get(`/api/v3/sales-nav/export?jobId=${jobId}`);
328
+ const status = statusResponse?.job?.status ?? null;
329
+ lastStatus = status;
330
+ statusTarget = statusResponse?.job?.targetLeadCount ?? null;
331
+ if (typeof statusTarget === "number" && !targetLeadCount) {
332
+ targetLeadCount = statusTarget;
333
+ }
334
+ if (status === "FAILED") {
335
+ return {
336
+ ready: false,
337
+ reason: "import_failed",
338
+ leadListId,
339
+ provider: provider ?? null,
340
+ attempts,
341
+ elapsedMs: Date.now() - start,
342
+ rowCount,
343
+ status,
344
+ targetLeadCount,
345
+ };
346
+ }
347
+ if (status === "CANCELLED") {
348
+ return {
349
+ ready: false,
350
+ reason: "cancelled",
351
+ leadListId,
352
+ provider: provider ?? null,
353
+ attempts,
354
+ elapsedMs: Date.now() - start,
355
+ rowCount,
356
+ status,
357
+ targetLeadCount,
358
+ };
359
+ }
360
+ if (status === "COMPLETED") {
361
+ importComplete = true;
362
+ }
363
+ }
364
+ else if (typeof targetLeadCount === "number") {
365
+ importComplete = rowCount >= targetLeadCount;
366
+ }
367
+ else {
368
+ missingJobId = true;
369
+ }
370
+ }
371
+ else if (provider === "prospeo") {
372
+ if (jobId) {
373
+ const statusResponse = await api.get(`/api/v3/lead-lists/${leadListId}/prospeo-import/status?jobId=${jobId}`);
374
+ const status = statusResponse?.job?.status ?? null;
375
+ lastStatus = status;
376
+ statusTarget = statusResponse?.job?.targetLeadCount ?? null;
377
+ if (typeof statusTarget === "number" && !targetLeadCount) {
378
+ targetLeadCount = statusTarget;
379
+ }
380
+ if (status === "FAILED") {
381
+ return {
382
+ ready: false,
383
+ reason: "import_failed",
384
+ leadListId,
385
+ provider: provider ?? null,
386
+ attempts,
387
+ elapsedMs: Date.now() - start,
388
+ rowCount,
389
+ status,
390
+ targetLeadCount,
391
+ };
392
+ }
393
+ if (status === "CANCELLED") {
394
+ return {
395
+ ready: false,
396
+ reason: "cancelled",
397
+ leadListId,
398
+ provider: provider ?? null,
399
+ attempts,
400
+ elapsedMs: Date.now() - start,
401
+ rowCount,
402
+ status,
403
+ targetLeadCount,
404
+ };
405
+ }
406
+ if (status === "COMPLETED") {
407
+ importComplete = true;
408
+ }
409
+ }
410
+ else if (typeof targetLeadCount === "number") {
411
+ importComplete = rowCount >= targetLeadCount;
412
+ }
413
+ else {
414
+ missingJobId = true;
415
+ }
416
+ }
417
+ else if (provider === "signal-discovery") {
418
+ // Config-based check already handled above.
419
+ }
420
+ else if (typeof targetLeadCount === "number") {
421
+ importComplete = rowCount >= targetLeadCount;
422
+ }
423
+ }
424
+ }
425
+ if (provider === "signal-discovery" && requireComplete) {
426
+ const noRows = rowCount === 0;
427
+ const hasProgress = typeof importProgress?.processed === "number"
428
+ ? importProgress.processed > 0
429
+ : typeof importProgress?.leadsImported === "number"
430
+ ? importProgress.leadsImported > 0
431
+ : false;
432
+ const pendingNoProgress = configStatus === "pending" && noRows && !hasProgress && !configError;
433
+ if (pendingNoProgress) {
434
+ const nextFingerprint = JSON.stringify({
435
+ status: configStatus,
436
+ phase: importProgress?.phase ?? null,
437
+ processed: importProgress?.processed ?? null,
438
+ leadsImported: importProgress?.leadsImported ?? null,
439
+ rowCount,
440
+ });
441
+ if (signalPendingSince === null ||
442
+ signalPendingFingerprint !== nextFingerprint) {
443
+ signalPendingSince = Date.now();
444
+ signalPendingFingerprint = nextFingerprint;
445
+ }
446
+ else if (Date.now() - signalPendingSince >=
447
+ SIGNAL_PENDING_STALL_MS) {
448
+ return {
449
+ ready: false,
450
+ reason: "stalled_import",
451
+ leadListId,
452
+ provider: provider ?? null,
453
+ attempts,
454
+ elapsedMs: Date.now() - start,
455
+ rowCount,
456
+ status: configStatus,
457
+ targetLeadCount,
458
+ error: "Import stayed in pending state with no rows or progress. The job may not have started.",
459
+ };
460
+ }
461
+ }
462
+ else {
463
+ signalPendingSince = null;
464
+ signalPendingFingerprint = null;
465
+ }
466
+ }
467
+ if ((!requireRows || rowCount > 0) && importComplete) {
468
+ return {
469
+ ready: true,
470
+ leadListId,
471
+ provider: provider ?? null,
472
+ attempts,
473
+ elapsedMs: Date.now() - start,
474
+ rowCount,
475
+ status: lastStatus,
476
+ targetLeadCount: targetLeadCount ?? null,
477
+ };
478
+ }
479
+ }
480
+ catch (error) {
481
+ lastError = error instanceof Error ? error.message : String(error);
482
+ }
483
+ await sleep(intervalMs);
484
+ }
485
+ const timedOutWithRows = (lastRowCount ?? 0) > 0;
486
+ const timeoutReason = missingJobId
487
+ ? "missing_job_id"
488
+ : timedOutWithRows
489
+ ? "timeout_with_rows"
490
+ : guardApplied
491
+ ? "tool_timeout_guard"
492
+ : "timeout";
493
+ return {
494
+ ready: !missingJobId && timedOutWithRows,
495
+ reason: timeoutReason,
496
+ leadListId,
497
+ provider: provider ?? null,
498
+ attempts,
499
+ elapsedMs: Date.now() - start,
500
+ rowCount: lastRowCount,
501
+ status: lastStatus ?? undefined,
502
+ targetLeadCount: targetLeadCount ?? undefined,
503
+ warning: !missingJobId && timedOutWithRows
504
+ ? "Timed out waiting for import status, but rows exist."
505
+ : guardApplied
506
+ ? `Stopped after ${effectiveTimeoutMs}ms to avoid host tool timeout (requested ${requestedTimeoutMs}ms). Re-run wait_for_lead_list_ready to keep polling.`
507
+ : undefined,
508
+ error: lastError ?? undefined,
509
+ };
510
+ }
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Lightweight row for listing - minimal fields for lead selection.
3
+ */
4
+ export interface LightweightRow {
5
+ id: string;
6
+ name: string;
7
+ company: string;
8
+ title: string;
9
+ linkedinUrl: string | null;
10
+ message: string | null;
11
+ messageCellId: string | null;
12
+ subject?: string | null;
13
+ subjectCellId?: string | null;
14
+ enrichCellId?: string | null;
15
+ enrichStatus?: string | null;
16
+ icpCellId?: string | null;
17
+ icpStatus?: string | null;
18
+ icpPassed?: boolean | null;
19
+ carryData?: Record<string, string>;
20
+ }
21
+ /**
22
+ * Full row for crafting - includes enrichment essentials.
23
+ */
24
+ export interface FullRow {
25
+ id: string;
26
+ name: string;
27
+ company: string;
28
+ title: string;
29
+ linkedinUrl: string | null;
30
+ currentMessage: string | null;
31
+ messageCellId: string | null;
32
+ currentSubject?: string | null;
33
+ subjectCellId?: string | null;
34
+ messageSequenceCellId?: string | null;
35
+ isApproved: boolean;
36
+ approveCellId?: string | null;
37
+ exampleCellId?: string | null;
38
+ isExample?: boolean;
39
+ carryData?: Record<string, string>;
40
+ enrichment: {
41
+ summary: string | null;
42
+ location: string | null;
43
+ experience: Array<{
44
+ title: string;
45
+ company: string;
46
+ duration: string;
47
+ description: string;
48
+ }>;
49
+ education: Array<{
50
+ degree: string;
51
+ school: string;
52
+ }>;
53
+ skills: string[];
54
+ } | null;
55
+ }
56
+ export interface RowsResponse {
57
+ rows: LightweightRow[];
58
+ pagination: {
59
+ page: number;
60
+ totalPages: number;
61
+ totalCount: number;
62
+ };
63
+ }
64
+ export declare const rowToolDefinitions: ({
65
+ name: string;
66
+ description: string;
67
+ inputSchema: {
68
+ type: string;
69
+ properties: {
70
+ tableId: {
71
+ type: string;
72
+ description: string;
73
+ };
74
+ limit: {
75
+ type: string;
76
+ description: string;
77
+ default: number;
78
+ };
79
+ page: {
80
+ type: string;
81
+ description: string;
82
+ default: number;
83
+ };
84
+ rowIds?: undefined;
85
+ };
86
+ required: string[];
87
+ };
88
+ } | {
89
+ name: string;
90
+ description: string;
91
+ inputSchema: {
92
+ type: string;
93
+ properties: {
94
+ tableId: {
95
+ type: string;
96
+ description: string;
97
+ };
98
+ rowIds: {
99
+ oneOf: ({
100
+ type: string;
101
+ items?: undefined;
102
+ } | {
103
+ type: string;
104
+ items: {
105
+ type: string;
106
+ };
107
+ })[];
108
+ description: string;
109
+ };
110
+ limit?: undefined;
111
+ page?: undefined;
112
+ };
113
+ required: string[];
114
+ };
115
+ })[];
116
+ export declare function getTableRows(tableId: string, options?: {
117
+ limit?: number;
118
+ page?: number;
119
+ }): Promise<RowsResponse>;
120
+ export declare function getTableRowsMinimal(tableId: string, options?: {
121
+ limit?: number;
122
+ page?: number;
123
+ }): Promise<RowsResponse>;
124
+ export declare function getRows(tableId: string, rowIds: string | string[]): Promise<{
125
+ rows: FullRow[];
126
+ }>;