@signaliz/sdk 1.0.27 → 1.0.29

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.
package/README.md CHANGED
@@ -77,6 +77,8 @@ resumes long-running research through the same `/company-signals` endpoint;
77
77
  callers never need an internal Trigger.dev status URL. The SDK follows the
78
78
  server's adaptive polling hints by default and still honors `pollIntervalMs`
79
79
  when a caller explicitly overrides the cadence.
80
+ Signal to Copy batches are transported in 25-row chunks; exact duplicates and
81
+ shared company research are single-flighted without changing input order.
80
82
 
81
83
  Company Signal Enrichment defaults `online` and `enableDeepSearch` to `true`
82
84
  unless the caller explicitly disables them. Signal to Copy AI returns three
@@ -365,7 +365,12 @@ var Signaliz = class {
365
365
  const email = typeof data.email === "string" && data.email.trim() ? data.email.trim() : null;
366
366
  const found = Boolean(email) && data.found !== false;
367
367
  const verificationStatus = data.verification_status ?? data.status ?? "unknown";
368
- const verified = data.email_verified === true || data.verified_for_sending === true || ["valid", "deliverable"].includes(String(verificationStatus).toLowerCase());
368
+ const rawFreshness = String(data.verification_freshness ?? "").toLowerCase();
369
+ const verificationFreshness = rawFreshness === "fresh" || rawFreshness === "stale" ? rawFreshness : "unknown";
370
+ const needsReverification = data.needs_reverification === true || verificationFreshness === "stale";
371
+ const explicitlyNotSendSafe = data.verified_for_sending === false || needsReverification;
372
+ const verified = !explicitlyNotSendSafe && (data.email_verified === true || data.verified_for_sending === true || ["valid", "deliverable"].includes(String(verificationStatus).toLowerCase()));
373
+ const verificationAgeDays = typeof data.verification_age_days === "number" && Number.isFinite(data.verification_age_days) ? data.verification_age_days : null;
369
374
  return {
370
375
  success: data.success !== false && found,
371
376
  found,
@@ -374,6 +379,13 @@ var Signaliz = class {
374
379
  lastName: data.last_name,
375
380
  confidence: data.confidence ?? data.confidence_score ?? (verified ? 1 : 0),
376
381
  verificationStatus,
382
+ isVerified: verified,
383
+ isVerifiedForSending: !needsReverification && data.verified_for_sending === true,
384
+ isCatchAll: data.email_is_catch_all === true || data.is_catch_all === true,
385
+ verificationFreshness,
386
+ verificationAgeDays,
387
+ verifiedAt: typeof data.verified_at === "string" ? data.verified_at : null,
388
+ needsReverification,
377
389
  providerUsed: data.provider_used ?? data.provider ?? data.email_source ?? data.source ?? "unknown",
378
390
  error: found ? void 0 : data.error ?? "No verified email found",
379
391
  errorCode: found ? void 0 : data.error_code ?? "EMAIL_NOT_FOUND",
@@ -494,16 +506,7 @@ var Signaliz = class {
494
506
  return runBatch(companies, (company) => this.enrichCompanySignals(company), options);
495
507
  }
496
508
  async signalToCopy(params) {
497
- const request = compact({
498
- company_domain: params.companyDomain,
499
- person_name: params.personName,
500
- title: params.title,
501
- campaign_offer: params.campaignOffer,
502
- research_prompt: params.researchPrompt,
503
- signal_run_id: params.signalRunId,
504
- skip_cache: params.skipCache,
505
- enable_deep_search: params.enableDeepSearch
506
- });
509
+ const request = signalToCopyRequestBody(params);
507
510
  let data = await this.client.post("api/v1/signal-to-copy", request);
508
511
  if (params.waitForResult !== false && data.run_id && isPendingSignalResponse(data)) {
509
512
  const maxWaitMs = Math.max(1e3, params.maxWaitMs ?? 20 * 6e4);
@@ -520,30 +523,113 @@ var Signaliz = class {
520
523
  throw new Error(`Signal Copy ${data.run_id} did not complete within ${maxWaitMs}ms`);
521
524
  }
522
525
  }
526
+ return normalizeSignalToCopyResult(data);
527
+ }
528
+ async createSignalCopyBatch(requests, options) {
529
+ if (!Array.isArray(requests) || requests.length === 0) {
530
+ throw new Error("Signaliz batch requests require at least one item");
531
+ }
532
+ if (requests.length > 5e3) {
533
+ throw new Error(`Signaliz batch requests support at most 5,000 items; received ${requests.length}`);
534
+ }
535
+ const startedAt = Date.now();
536
+ const chunks = [];
537
+ for (let offset = 0; offset < requests.length; offset += 25) {
538
+ chunks.push({ offset, requests: requests.slice(offset, offset + 25) });
539
+ }
540
+ const serverConcurrency = Math.min(10, options?.concurrency ?? 10);
541
+ const chunkBatch = await runBatch(
542
+ chunks,
543
+ (chunk) => this.runSignalCopyBatchChunk(chunk.requests, serverConcurrency),
544
+ options
545
+ );
546
+ const results = new Array(requests.length);
547
+ for (const chunkResult of chunkBatch.results) {
548
+ const chunk = chunks[chunkResult.index];
549
+ if (!chunkResult.success || !chunkResult.data) {
550
+ for (let index = 0; index < chunk.requests.length; index += 1) {
551
+ results[chunk.offset + index] = {
552
+ index: chunk.offset + index,
553
+ success: false,
554
+ error: chunkResult.error || "Signal to Copy batch request failed"
555
+ };
556
+ }
557
+ continue;
558
+ }
559
+ for (const item of chunkResult.data) {
560
+ results[chunk.offset + item.index] = { ...item, index: chunk.offset + item.index };
561
+ }
562
+ }
563
+ const succeeded = results.filter((item) => item.success).length;
523
564
  return {
524
- success: data.success ?? true,
525
- status: data.status,
526
- runId: data.run_id,
527
- strongestSignal: data.strongest_signal ?? "",
528
- whyNow: data.why_now ?? "",
529
- subjectLine: data.subject_line ?? "",
530
- openingLine: data.opening_line ?? "",
531
- confidence: data.confidence ?? 0,
532
- evidenceUrl: data.evidence_url ?? "",
533
- researchPrompt: data.research_prompt,
534
- variations: (data.variations ?? []).map((variation) => ({
535
- style: variation.style,
536
- subjectLine: variation.subject_line ?? "",
537
- openingLine: variation.opening_line ?? "",
538
- cta: variation.cta ?? "",
539
- prediction: variation.prediction,
540
- email: variation.email
541
- })),
542
- raw: data
565
+ total: requests.length,
566
+ succeeded,
567
+ failed: requests.length - succeeded,
568
+ durationMs: Date.now() - startedAt,
569
+ results
543
570
  };
544
571
  }
545
- async createSignalCopyBatch(requests, options) {
546
- return runBatch(requests, (request) => this.signalToCopy(request), options);
572
+ async runSignalCopyBatchChunk(requests, concurrency) {
573
+ const startedAt = Date.now();
574
+ const results = new Array(requests.length);
575
+ let pending = requests.map((params, index) => ({
576
+ index,
577
+ params,
578
+ request: signalToCopyRequestBody(params)
579
+ }));
580
+ while (pending.length > 0) {
581
+ const data = await this.client.post("api/v1/signal-to-copy", {
582
+ requests: pending.map((item) => item.request),
583
+ concurrency
584
+ });
585
+ if (!Array.isArray(data.results) || data.results.length !== pending.length) {
586
+ throw new Error("Signal to Copy batch returned an invalid result count");
587
+ }
588
+ const nextPending = [];
589
+ let nextDelayMs = 6e4;
590
+ for (let index = 0; index < pending.length; index += 1) {
591
+ const task = pending[index];
592
+ const item = data.results[index];
593
+ if (item.success === false) {
594
+ results[task.index] = { index: task.index, success: false, error: item.error || item.message || "Signal to Copy failed" };
595
+ continue;
596
+ }
597
+ if (!isPendingSignalResponse(item)) {
598
+ results[task.index] = { index: task.index, success: true, data: normalizeSignalToCopyResult(item) };
599
+ continue;
600
+ }
601
+ if (task.params.waitForResult === false) {
602
+ results[task.index] = { index: task.index, success: true, data: normalizeSignalToCopyResult(item) };
603
+ continue;
604
+ }
605
+ const maxWaitMs = Math.max(1e3, task.params.maxWaitMs ?? 20 * 6e4);
606
+ if (Date.now() - startedAt >= maxWaitMs) {
607
+ results[task.index] = {
608
+ index: task.index,
609
+ success: false,
610
+ error: `Signal Copy ${item.signal_run_id || item.run_id || ""} did not complete within ${maxWaitMs}ms`
611
+ };
612
+ continue;
613
+ }
614
+ const signalRunId = item.signal_run_id || item.run_id;
615
+ if (!signalRunId) {
616
+ results[task.index] = { index: task.index, success: false, error: "Signal Copy is processing without a signal_run_id" };
617
+ continue;
618
+ }
619
+ nextDelayMs = Math.min(
620
+ nextDelayMs,
621
+ signalPollDelayMs(item, task.params.pollIntervalMs),
622
+ maxWaitMs - (Date.now() - startedAt)
623
+ );
624
+ nextPending.push({
625
+ ...task,
626
+ request: { ...task.request, signal_run_id: signalRunId }
627
+ });
628
+ }
629
+ pending = nextPending;
630
+ if (pending.length > 0) await sleep2(nextDelayMs);
631
+ }
632
+ return results;
547
633
  }
548
634
  async listTools() {
549
635
  const result = await this.client.mcp("tools/list");
@@ -560,6 +646,41 @@ var Signaliz = class {
560
646
  function compact(input) {
561
647
  return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== void 0));
562
648
  }
649
+ function signalToCopyRequestBody(params) {
650
+ return compact({
651
+ company_domain: params.companyDomain,
652
+ person_name: params.personName,
653
+ title: params.title,
654
+ campaign_offer: params.campaignOffer,
655
+ research_prompt: params.researchPrompt,
656
+ signal_run_id: params.signalRunId,
657
+ skip_cache: params.skipCache,
658
+ enable_deep_search: params.enableDeepSearch
659
+ });
660
+ }
661
+ function normalizeSignalToCopyResult(data) {
662
+ return {
663
+ success: data.success ?? true,
664
+ status: data.status,
665
+ runId: data.signal_run_id ?? data.run_id,
666
+ strongestSignal: data.strongest_signal ?? "",
667
+ whyNow: data.why_now ?? "",
668
+ subjectLine: data.subject_line ?? "",
669
+ openingLine: data.opening_line ?? "",
670
+ confidence: data.confidence ?? 0,
671
+ evidenceUrl: data.evidence_url ?? "",
672
+ researchPrompt: data.research_prompt,
673
+ variations: (data.variations ?? []).map((variation) => ({
674
+ style: variation.style,
675
+ subjectLine: variation.subject_line ?? "",
676
+ openingLine: variation.opening_line ?? "",
677
+ cta: variation.cta ?? "",
678
+ prediction: variation.prediction,
679
+ email: variation.email
680
+ })),
681
+ raw: data
682
+ };
683
+ }
563
684
  function isPendingSignalResponse(data) {
564
685
  return ["triggered", "queued", "processing", "pending", "pending_version", "waiting_for_deploy"].includes(String(data.status || "").toLowerCase());
565
686
  }
package/dist/index.d.mts CHANGED
@@ -49,6 +49,13 @@ interface FindEmailResult {
49
49
  lastName?: string;
50
50
  confidence: number;
51
51
  verificationStatus: string;
52
+ isVerified: boolean;
53
+ isVerifiedForSending: boolean;
54
+ isCatchAll: boolean;
55
+ verificationFreshness: 'fresh' | 'stale' | 'unknown';
56
+ verificationAgeDays: number | null;
57
+ verifiedAt: string | null;
58
+ needsReverification: boolean;
52
59
  providerUsed: string;
53
60
  error?: string;
54
61
  errorCode?: string;
@@ -196,6 +203,7 @@ declare class Signaliz {
196
203
  enrichCompanies(companies: CompanySignalEnrichmentParams[], options?: BatchOptions): Promise<BatchResult<CompanySignalEnrichmentResult>>;
197
204
  signalToCopy(params: SignalToCopyParams): Promise<SignalToCopyResult>;
198
205
  createSignalCopyBatch(requests: SignalToCopyParams[], options?: BatchOptions): Promise<BatchResult<SignalToCopyResult>>;
206
+ private runSignalCopyBatchChunk;
199
207
  listTools(): Promise<MCPToolInfo[]>;
200
208
  health(): Promise<PlatformHealth>;
201
209
  }
package/dist/index.d.ts CHANGED
@@ -49,6 +49,13 @@ interface FindEmailResult {
49
49
  lastName?: string;
50
50
  confidence: number;
51
51
  verificationStatus: string;
52
+ isVerified: boolean;
53
+ isVerifiedForSending: boolean;
54
+ isCatchAll: boolean;
55
+ verificationFreshness: 'fresh' | 'stale' | 'unknown';
56
+ verificationAgeDays: number | null;
57
+ verifiedAt: string | null;
58
+ needsReverification: boolean;
52
59
  providerUsed: string;
53
60
  error?: string;
54
61
  errorCode?: string;
@@ -196,6 +203,7 @@ declare class Signaliz {
196
203
  enrichCompanies(companies: CompanySignalEnrichmentParams[], options?: BatchOptions): Promise<BatchResult<CompanySignalEnrichmentResult>>;
197
204
  signalToCopy(params: SignalToCopyParams): Promise<SignalToCopyResult>;
198
205
  createSignalCopyBatch(requests: SignalToCopyParams[], options?: BatchOptions): Promise<BatchResult<SignalToCopyResult>>;
206
+ private runSignalCopyBatchChunk;
199
207
  listTools(): Promise<MCPToolInfo[]>;
200
208
  health(): Promise<PlatformHealth>;
201
209
  }
package/dist/index.js CHANGED
@@ -392,7 +392,12 @@ var Signaliz = class {
392
392
  const email = typeof data.email === "string" && data.email.trim() ? data.email.trim() : null;
393
393
  const found = Boolean(email) && data.found !== false;
394
394
  const verificationStatus = data.verification_status ?? data.status ?? "unknown";
395
- const verified = data.email_verified === true || data.verified_for_sending === true || ["valid", "deliverable"].includes(String(verificationStatus).toLowerCase());
395
+ const rawFreshness = String(data.verification_freshness ?? "").toLowerCase();
396
+ const verificationFreshness = rawFreshness === "fresh" || rawFreshness === "stale" ? rawFreshness : "unknown";
397
+ const needsReverification = data.needs_reverification === true || verificationFreshness === "stale";
398
+ const explicitlyNotSendSafe = data.verified_for_sending === false || needsReverification;
399
+ const verified = !explicitlyNotSendSafe && (data.email_verified === true || data.verified_for_sending === true || ["valid", "deliverable"].includes(String(verificationStatus).toLowerCase()));
400
+ const verificationAgeDays = typeof data.verification_age_days === "number" && Number.isFinite(data.verification_age_days) ? data.verification_age_days : null;
396
401
  return {
397
402
  success: data.success !== false && found,
398
403
  found,
@@ -401,6 +406,13 @@ var Signaliz = class {
401
406
  lastName: data.last_name,
402
407
  confidence: data.confidence ?? data.confidence_score ?? (verified ? 1 : 0),
403
408
  verificationStatus,
409
+ isVerified: verified,
410
+ isVerifiedForSending: !needsReverification && data.verified_for_sending === true,
411
+ isCatchAll: data.email_is_catch_all === true || data.is_catch_all === true,
412
+ verificationFreshness,
413
+ verificationAgeDays,
414
+ verifiedAt: typeof data.verified_at === "string" ? data.verified_at : null,
415
+ needsReverification,
404
416
  providerUsed: data.provider_used ?? data.provider ?? data.email_source ?? data.source ?? "unknown",
405
417
  error: found ? void 0 : data.error ?? "No verified email found",
406
418
  errorCode: found ? void 0 : data.error_code ?? "EMAIL_NOT_FOUND",
@@ -521,16 +533,7 @@ var Signaliz = class {
521
533
  return runBatch(companies, (company) => this.enrichCompanySignals(company), options);
522
534
  }
523
535
  async signalToCopy(params) {
524
- const request = compact({
525
- company_domain: params.companyDomain,
526
- person_name: params.personName,
527
- title: params.title,
528
- campaign_offer: params.campaignOffer,
529
- research_prompt: params.researchPrompt,
530
- signal_run_id: params.signalRunId,
531
- skip_cache: params.skipCache,
532
- enable_deep_search: params.enableDeepSearch
533
- });
536
+ const request = signalToCopyRequestBody(params);
534
537
  let data = await this.client.post("api/v1/signal-to-copy", request);
535
538
  if (params.waitForResult !== false && data.run_id && isPendingSignalResponse(data)) {
536
539
  const maxWaitMs = Math.max(1e3, params.maxWaitMs ?? 20 * 6e4);
@@ -547,30 +550,113 @@ var Signaliz = class {
547
550
  throw new Error(`Signal Copy ${data.run_id} did not complete within ${maxWaitMs}ms`);
548
551
  }
549
552
  }
553
+ return normalizeSignalToCopyResult(data);
554
+ }
555
+ async createSignalCopyBatch(requests, options) {
556
+ if (!Array.isArray(requests) || requests.length === 0) {
557
+ throw new Error("Signaliz batch requests require at least one item");
558
+ }
559
+ if (requests.length > 5e3) {
560
+ throw new Error(`Signaliz batch requests support at most 5,000 items; received ${requests.length}`);
561
+ }
562
+ const startedAt = Date.now();
563
+ const chunks = [];
564
+ for (let offset = 0; offset < requests.length; offset += 25) {
565
+ chunks.push({ offset, requests: requests.slice(offset, offset + 25) });
566
+ }
567
+ const serverConcurrency = Math.min(10, options?.concurrency ?? 10);
568
+ const chunkBatch = await runBatch(
569
+ chunks,
570
+ (chunk) => this.runSignalCopyBatchChunk(chunk.requests, serverConcurrency),
571
+ options
572
+ );
573
+ const results = new Array(requests.length);
574
+ for (const chunkResult of chunkBatch.results) {
575
+ const chunk = chunks[chunkResult.index];
576
+ if (!chunkResult.success || !chunkResult.data) {
577
+ for (let index = 0; index < chunk.requests.length; index += 1) {
578
+ results[chunk.offset + index] = {
579
+ index: chunk.offset + index,
580
+ success: false,
581
+ error: chunkResult.error || "Signal to Copy batch request failed"
582
+ };
583
+ }
584
+ continue;
585
+ }
586
+ for (const item of chunkResult.data) {
587
+ results[chunk.offset + item.index] = { ...item, index: chunk.offset + item.index };
588
+ }
589
+ }
590
+ const succeeded = results.filter((item) => item.success).length;
550
591
  return {
551
- success: data.success ?? true,
552
- status: data.status,
553
- runId: data.run_id,
554
- strongestSignal: data.strongest_signal ?? "",
555
- whyNow: data.why_now ?? "",
556
- subjectLine: data.subject_line ?? "",
557
- openingLine: data.opening_line ?? "",
558
- confidence: data.confidence ?? 0,
559
- evidenceUrl: data.evidence_url ?? "",
560
- researchPrompt: data.research_prompt,
561
- variations: (data.variations ?? []).map((variation) => ({
562
- style: variation.style,
563
- subjectLine: variation.subject_line ?? "",
564
- openingLine: variation.opening_line ?? "",
565
- cta: variation.cta ?? "",
566
- prediction: variation.prediction,
567
- email: variation.email
568
- })),
569
- raw: data
592
+ total: requests.length,
593
+ succeeded,
594
+ failed: requests.length - succeeded,
595
+ durationMs: Date.now() - startedAt,
596
+ results
570
597
  };
571
598
  }
572
- async createSignalCopyBatch(requests, options) {
573
- return runBatch(requests, (request) => this.signalToCopy(request), options);
599
+ async runSignalCopyBatchChunk(requests, concurrency) {
600
+ const startedAt = Date.now();
601
+ const results = new Array(requests.length);
602
+ let pending = requests.map((params, index) => ({
603
+ index,
604
+ params,
605
+ request: signalToCopyRequestBody(params)
606
+ }));
607
+ while (pending.length > 0) {
608
+ const data = await this.client.post("api/v1/signal-to-copy", {
609
+ requests: pending.map((item) => item.request),
610
+ concurrency
611
+ });
612
+ if (!Array.isArray(data.results) || data.results.length !== pending.length) {
613
+ throw new Error("Signal to Copy batch returned an invalid result count");
614
+ }
615
+ const nextPending = [];
616
+ let nextDelayMs = 6e4;
617
+ for (let index = 0; index < pending.length; index += 1) {
618
+ const task = pending[index];
619
+ const item = data.results[index];
620
+ if (item.success === false) {
621
+ results[task.index] = { index: task.index, success: false, error: item.error || item.message || "Signal to Copy failed" };
622
+ continue;
623
+ }
624
+ if (!isPendingSignalResponse(item)) {
625
+ results[task.index] = { index: task.index, success: true, data: normalizeSignalToCopyResult(item) };
626
+ continue;
627
+ }
628
+ if (task.params.waitForResult === false) {
629
+ results[task.index] = { index: task.index, success: true, data: normalizeSignalToCopyResult(item) };
630
+ continue;
631
+ }
632
+ const maxWaitMs = Math.max(1e3, task.params.maxWaitMs ?? 20 * 6e4);
633
+ if (Date.now() - startedAt >= maxWaitMs) {
634
+ results[task.index] = {
635
+ index: task.index,
636
+ success: false,
637
+ error: `Signal Copy ${item.signal_run_id || item.run_id || ""} did not complete within ${maxWaitMs}ms`
638
+ };
639
+ continue;
640
+ }
641
+ const signalRunId = item.signal_run_id || item.run_id;
642
+ if (!signalRunId) {
643
+ results[task.index] = { index: task.index, success: false, error: "Signal Copy is processing without a signal_run_id" };
644
+ continue;
645
+ }
646
+ nextDelayMs = Math.min(
647
+ nextDelayMs,
648
+ signalPollDelayMs(item, task.params.pollIntervalMs),
649
+ maxWaitMs - (Date.now() - startedAt)
650
+ );
651
+ nextPending.push({
652
+ ...task,
653
+ request: { ...task.request, signal_run_id: signalRunId }
654
+ });
655
+ }
656
+ pending = nextPending;
657
+ if (pending.length > 0) await sleep2(nextDelayMs);
658
+ }
659
+ return results;
574
660
  }
575
661
  async listTools() {
576
662
  const result = await this.client.mcp("tools/list");
@@ -587,6 +673,41 @@ var Signaliz = class {
587
673
  function compact(input) {
588
674
  return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== void 0));
589
675
  }
676
+ function signalToCopyRequestBody(params) {
677
+ return compact({
678
+ company_domain: params.companyDomain,
679
+ person_name: params.personName,
680
+ title: params.title,
681
+ campaign_offer: params.campaignOffer,
682
+ research_prompt: params.researchPrompt,
683
+ signal_run_id: params.signalRunId,
684
+ skip_cache: params.skipCache,
685
+ enable_deep_search: params.enableDeepSearch
686
+ });
687
+ }
688
+ function normalizeSignalToCopyResult(data) {
689
+ return {
690
+ success: data.success ?? true,
691
+ status: data.status,
692
+ runId: data.signal_run_id ?? data.run_id,
693
+ strongestSignal: data.strongest_signal ?? "",
694
+ whyNow: data.why_now ?? "",
695
+ subjectLine: data.subject_line ?? "",
696
+ openingLine: data.opening_line ?? "",
697
+ confidence: data.confidence ?? 0,
698
+ evidenceUrl: data.evidence_url ?? "",
699
+ researchPrompt: data.research_prompt,
700
+ variations: (data.variations ?? []).map((variation) => ({
701
+ style: variation.style,
702
+ subjectLine: variation.subject_line ?? "",
703
+ openingLine: variation.opening_line ?? "",
704
+ cta: variation.cta ?? "",
705
+ prediction: variation.prediction,
706
+ email: variation.email
707
+ })),
708
+ raw: data
709
+ };
710
+ }
590
711
  function isPendingSignalResponse(data) {
591
712
  return ["triggered", "queued", "processing", "pending", "pending_version", "waiting_for_deploy"].includes(String(data.status || "").toLowerCase());
592
713
  }
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  Signaliz,
3
3
  SignalizError
4
- } from "./chunk-TQZFKFPI.mjs";
4
+ } from "./chunk-R4DIAP7P.mjs";
5
5
  export {
6
6
  Signaliz,
7
7
  SignalizError
@@ -396,7 +396,12 @@ var Signaliz = class {
396
396
  const email = typeof data.email === "string" && data.email.trim() ? data.email.trim() : null;
397
397
  const found = Boolean(email) && data.found !== false;
398
398
  const verificationStatus = data.verification_status ?? data.status ?? "unknown";
399
- const verified = data.email_verified === true || data.verified_for_sending === true || ["valid", "deliverable"].includes(String(verificationStatus).toLowerCase());
399
+ const rawFreshness = String(data.verification_freshness ?? "").toLowerCase();
400
+ const verificationFreshness = rawFreshness === "fresh" || rawFreshness === "stale" ? rawFreshness : "unknown";
401
+ const needsReverification = data.needs_reverification === true || verificationFreshness === "stale";
402
+ const explicitlyNotSendSafe = data.verified_for_sending === false || needsReverification;
403
+ const verified = !explicitlyNotSendSafe && (data.email_verified === true || data.verified_for_sending === true || ["valid", "deliverable"].includes(String(verificationStatus).toLowerCase()));
404
+ const verificationAgeDays = typeof data.verification_age_days === "number" && Number.isFinite(data.verification_age_days) ? data.verification_age_days : null;
400
405
  return {
401
406
  success: data.success !== false && found,
402
407
  found,
@@ -405,6 +410,13 @@ var Signaliz = class {
405
410
  lastName: data.last_name,
406
411
  confidence: data.confidence ?? data.confidence_score ?? (verified ? 1 : 0),
407
412
  verificationStatus,
413
+ isVerified: verified,
414
+ isVerifiedForSending: !needsReverification && data.verified_for_sending === true,
415
+ isCatchAll: data.email_is_catch_all === true || data.is_catch_all === true,
416
+ verificationFreshness,
417
+ verificationAgeDays,
418
+ verifiedAt: typeof data.verified_at === "string" ? data.verified_at : null,
419
+ needsReverification,
408
420
  providerUsed: data.provider_used ?? data.provider ?? data.email_source ?? data.source ?? "unknown",
409
421
  error: found ? void 0 : data.error ?? "No verified email found",
410
422
  errorCode: found ? void 0 : data.error_code ?? "EMAIL_NOT_FOUND",
@@ -525,16 +537,7 @@ var Signaliz = class {
525
537
  return runBatch(companies, (company) => this.enrichCompanySignals(company), options);
526
538
  }
527
539
  async signalToCopy(params) {
528
- const request = compact({
529
- company_domain: params.companyDomain,
530
- person_name: params.personName,
531
- title: params.title,
532
- campaign_offer: params.campaignOffer,
533
- research_prompt: params.researchPrompt,
534
- signal_run_id: params.signalRunId,
535
- skip_cache: params.skipCache,
536
- enable_deep_search: params.enableDeepSearch
537
- });
540
+ const request = signalToCopyRequestBody(params);
538
541
  let data = await this.client.post("api/v1/signal-to-copy", request);
539
542
  if (params.waitForResult !== false && data.run_id && isPendingSignalResponse(data)) {
540
543
  const maxWaitMs = Math.max(1e3, params.maxWaitMs ?? 20 * 6e4);
@@ -551,30 +554,113 @@ var Signaliz = class {
551
554
  throw new Error(`Signal Copy ${data.run_id} did not complete within ${maxWaitMs}ms`);
552
555
  }
553
556
  }
557
+ return normalizeSignalToCopyResult(data);
558
+ }
559
+ async createSignalCopyBatch(requests, options) {
560
+ if (!Array.isArray(requests) || requests.length === 0) {
561
+ throw new Error("Signaliz batch requests require at least one item");
562
+ }
563
+ if (requests.length > 5e3) {
564
+ throw new Error(`Signaliz batch requests support at most 5,000 items; received ${requests.length}`);
565
+ }
566
+ const startedAt = Date.now();
567
+ const chunks = [];
568
+ for (let offset = 0; offset < requests.length; offset += 25) {
569
+ chunks.push({ offset, requests: requests.slice(offset, offset + 25) });
570
+ }
571
+ const serverConcurrency = Math.min(10, options?.concurrency ?? 10);
572
+ const chunkBatch = await runBatch(
573
+ chunks,
574
+ (chunk) => this.runSignalCopyBatchChunk(chunk.requests, serverConcurrency),
575
+ options
576
+ );
577
+ const results = new Array(requests.length);
578
+ for (const chunkResult of chunkBatch.results) {
579
+ const chunk = chunks[chunkResult.index];
580
+ if (!chunkResult.success || !chunkResult.data) {
581
+ for (let index = 0; index < chunk.requests.length; index += 1) {
582
+ results[chunk.offset + index] = {
583
+ index: chunk.offset + index,
584
+ success: false,
585
+ error: chunkResult.error || "Signal to Copy batch request failed"
586
+ };
587
+ }
588
+ continue;
589
+ }
590
+ for (const item of chunkResult.data) {
591
+ results[chunk.offset + item.index] = { ...item, index: chunk.offset + item.index };
592
+ }
593
+ }
594
+ const succeeded = results.filter((item) => item.success).length;
554
595
  return {
555
- success: data.success ?? true,
556
- status: data.status,
557
- runId: data.run_id,
558
- strongestSignal: data.strongest_signal ?? "",
559
- whyNow: data.why_now ?? "",
560
- subjectLine: data.subject_line ?? "",
561
- openingLine: data.opening_line ?? "",
562
- confidence: data.confidence ?? 0,
563
- evidenceUrl: data.evidence_url ?? "",
564
- researchPrompt: data.research_prompt,
565
- variations: (data.variations ?? []).map((variation) => ({
566
- style: variation.style,
567
- subjectLine: variation.subject_line ?? "",
568
- openingLine: variation.opening_line ?? "",
569
- cta: variation.cta ?? "",
570
- prediction: variation.prediction,
571
- email: variation.email
572
- })),
573
- raw: data
596
+ total: requests.length,
597
+ succeeded,
598
+ failed: requests.length - succeeded,
599
+ durationMs: Date.now() - startedAt,
600
+ results
574
601
  };
575
602
  }
576
- async createSignalCopyBatch(requests, options) {
577
- return runBatch(requests, (request) => this.signalToCopy(request), options);
603
+ async runSignalCopyBatchChunk(requests, concurrency) {
604
+ const startedAt = Date.now();
605
+ const results = new Array(requests.length);
606
+ let pending = requests.map((params, index) => ({
607
+ index,
608
+ params,
609
+ request: signalToCopyRequestBody(params)
610
+ }));
611
+ while (pending.length > 0) {
612
+ const data = await this.client.post("api/v1/signal-to-copy", {
613
+ requests: pending.map((item) => item.request),
614
+ concurrency
615
+ });
616
+ if (!Array.isArray(data.results) || data.results.length !== pending.length) {
617
+ throw new Error("Signal to Copy batch returned an invalid result count");
618
+ }
619
+ const nextPending = [];
620
+ let nextDelayMs = 6e4;
621
+ for (let index = 0; index < pending.length; index += 1) {
622
+ const task = pending[index];
623
+ const item = data.results[index];
624
+ if (item.success === false) {
625
+ results[task.index] = { index: task.index, success: false, error: item.error || item.message || "Signal to Copy failed" };
626
+ continue;
627
+ }
628
+ if (!isPendingSignalResponse(item)) {
629
+ results[task.index] = { index: task.index, success: true, data: normalizeSignalToCopyResult(item) };
630
+ continue;
631
+ }
632
+ if (task.params.waitForResult === false) {
633
+ results[task.index] = { index: task.index, success: true, data: normalizeSignalToCopyResult(item) };
634
+ continue;
635
+ }
636
+ const maxWaitMs = Math.max(1e3, task.params.maxWaitMs ?? 20 * 6e4);
637
+ if (Date.now() - startedAt >= maxWaitMs) {
638
+ results[task.index] = {
639
+ index: task.index,
640
+ success: false,
641
+ error: `Signal Copy ${item.signal_run_id || item.run_id || ""} did not complete within ${maxWaitMs}ms`
642
+ };
643
+ continue;
644
+ }
645
+ const signalRunId = item.signal_run_id || item.run_id;
646
+ if (!signalRunId) {
647
+ results[task.index] = { index: task.index, success: false, error: "Signal Copy is processing without a signal_run_id" };
648
+ continue;
649
+ }
650
+ nextDelayMs = Math.min(
651
+ nextDelayMs,
652
+ signalPollDelayMs(item, task.params.pollIntervalMs),
653
+ maxWaitMs - (Date.now() - startedAt)
654
+ );
655
+ nextPending.push({
656
+ ...task,
657
+ request: { ...task.request, signal_run_id: signalRunId }
658
+ });
659
+ }
660
+ pending = nextPending;
661
+ if (pending.length > 0) await sleep2(nextDelayMs);
662
+ }
663
+ return results;
578
664
  }
579
665
  async listTools() {
580
666
  const result = await this.client.mcp("tools/list");
@@ -591,6 +677,41 @@ var Signaliz = class {
591
677
  function compact(input) {
592
678
  return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== void 0));
593
679
  }
680
+ function signalToCopyRequestBody(params) {
681
+ return compact({
682
+ company_domain: params.companyDomain,
683
+ person_name: params.personName,
684
+ title: params.title,
685
+ campaign_offer: params.campaignOffer,
686
+ research_prompt: params.researchPrompt,
687
+ signal_run_id: params.signalRunId,
688
+ skip_cache: params.skipCache,
689
+ enable_deep_search: params.enableDeepSearch
690
+ });
691
+ }
692
+ function normalizeSignalToCopyResult(data) {
693
+ return {
694
+ success: data.success ?? true,
695
+ status: data.status,
696
+ runId: data.signal_run_id ?? data.run_id,
697
+ strongestSignal: data.strongest_signal ?? "",
698
+ whyNow: data.why_now ?? "",
699
+ subjectLine: data.subject_line ?? "",
700
+ openingLine: data.opening_line ?? "",
701
+ confidence: data.confidence ?? 0,
702
+ evidenceUrl: data.evidence_url ?? "",
703
+ researchPrompt: data.research_prompt,
704
+ variations: (data.variations ?? []).map((variation) => ({
705
+ style: variation.style,
706
+ subjectLine: variation.subject_line ?? "",
707
+ openingLine: variation.opening_line ?? "",
708
+ cta: variation.cta ?? "",
709
+ prediction: variation.prediction,
710
+ email: variation.email
711
+ })),
712
+ raw: data
713
+ };
714
+ }
594
715
  function isPendingSignalResponse(data) {
595
716
  return ["triggered", "queued", "processing", "pending", "pending_version", "waiting_for_deploy"].includes(String(data.status || "").toLowerCase());
596
717
  }
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  Signaliz
4
- } from "./chunk-TQZFKFPI.mjs";
4
+ } from "./chunk-R4DIAP7P.mjs";
5
5
 
6
6
  // src/mcp-config.ts
7
7
  import * as fs from "fs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signaliz/sdk",
3
- "version": "1.0.27",
3
+ "version": "1.0.29",
4
4
  "description": "Signaliz SDK for Find Email, Verify Email, Company Signal Enrichment, and Signal to Copy AI.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",