@signaliz/sdk 1.0.40 → 1.0.42

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
@@ -69,6 +69,17 @@ const copy = await signaliz.signalToCopy({
69
69
  skipCache: false,
70
70
  enableDeepSearch: false,
71
71
  });
72
+
73
+ // Persist a queued copy ID and resume with that ID alone. Signaliz restores
74
+ // the recipient, title, offer, prompt, and underlying research run.
75
+ const queuedCopy = await signaliz.signalToCopy({
76
+ companyDomain: 'example.com',
77
+ personName: 'Jane Doe',
78
+ title: 'VP Sales',
79
+ campaignOffer: 'pipeline intelligence',
80
+ waitForResult: false,
81
+ });
82
+ const resumedCopy = await signaliz.signalToCopy({ signalRunId: queuedCopy.runId });
72
83
  ```
73
84
 
74
85
  All four products support bounded batch execution over the same REST endpoints.
@@ -32,7 +32,7 @@ function parseError(status, body) {
32
32
  }
33
33
  function mapErrorType(code, status) {
34
34
  if (code === "RATE_LIMITED" || status === 429) return "rate_limited";
35
- if (code === "VALIDATION_ERROR" || status === 400) return "validation";
35
+ if (code === "VALIDATION_ERROR" || code === "SIGNAL_RUN_COMPANY_MISMATCH" || status === 400) return "validation";
36
36
  if (code === "NOT_FOUND" || status === 404) return "not_found";
37
37
  if (code === "NO_SUPPORTED_SIGNAL") return "not_found";
38
38
  if (code === "AUTH_EXPIRED" || status === 401) return "auth_expired";
@@ -60,6 +60,7 @@ var HttpClient = class {
60
60
  }
61
61
  async request(functionName, body, method = "POST") {
62
62
  let lastError;
63
+ const idempotencyKey = method === "POST" ? requestIdempotencyKey(body) : void 0;
63
64
  for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
64
65
  let timer;
65
66
  try {
@@ -77,7 +78,8 @@ var HttpClient = class {
77
78
  method,
78
79
  headers: {
79
80
  "Content-Type": "application/json",
80
- Authorization: `Bearer ${token}`
81
+ Authorization: `Bearer ${token}`,
82
+ ...idempotencyKey ? { "Idempotency-Key": idempotencyKey } : {}
81
83
  },
82
84
  signal: controller.signal
83
85
  };
@@ -242,6 +244,11 @@ var HttpClient = class {
242
244
  return this.accessTokenPromise;
243
245
  }
244
246
  };
247
+ function requestIdempotencyKey(body) {
248
+ const requested = typeof body.idempotency_key === "string" ? body.idempotency_key.trim() : "";
249
+ const nonce = globalThis.crypto?.randomUUID?.() || `${Date.now()}-${Math.random().toString(16).slice(2)}`;
250
+ return requested || `sdk_${nonce}`;
251
+ }
245
252
  function sleep(ms) {
246
253
  return new Promise((r) => setTimeout(r, ms));
247
254
  }
@@ -342,7 +349,7 @@ function mapMcpErrorType(error) {
342
349
  function mapErrorTypeFromCode(code) {
343
350
  if (!code) return "unknown";
344
351
  if (code === "RATE_LIMITED") return "rate_limited";
345
- if (code === "INVALID_ARGUMENT" || code === "VALIDATION_ERROR" || code === "VALIDATION_001" || code === "APPROVAL_REQUIRED" || code === "WEBHOOK_APPROVAL_REQUIRED" || code === "TARGET_LIMIT_EXCEEDED") return "validation";
352
+ if (code === "INVALID_ARGUMENT" || code === "VALIDATION_ERROR" || code === "VALIDATION_001" || code === "SIGNAL_RUN_COMPANY_MISMATCH" || code === "APPROVAL_REQUIRED" || code === "WEBHOOK_APPROVAL_REQUIRED" || code === "TARGET_LIMIT_EXCEEDED") return "validation";
346
353
  if (code === "AUTH_REQUIRED" || code === "AUTH_001") return "auth_expired";
347
354
  if (code === "QUOTA_EXCEEDED") return "insufficient_credits";
348
355
  if (code === "BUILD_NOT_READY" || code === "ARTIFACT_NOT_READY") return "not_found";
@@ -521,17 +528,16 @@ var Signaliz = class {
521
528
  };
522
529
  }
523
530
  async signalToCopy(params) {
531
+ validateSignalToCopyParams(params);
524
532
  const request = signalToCopyRequestBody(params);
525
533
  let data = await this.client.post("api/v1/signal-to-copy", request);
526
- if (params.waitForResult !== false && data.run_id && isPendingSignalResponse(data)) {
534
+ if (params.waitForResult !== false && (data.signal_run_id || data.run_id) && isPendingSignalResponse(data)) {
527
535
  const maxWaitMs = Math.max(1e3, params.maxWaitMs ?? 20 * 6e4);
528
536
  const startedAt = Date.now();
529
537
  while (Date.now() - startedAt < maxWaitMs) {
530
538
  if (!await waitForNextSignalPoll(data, params.pollIntervalMs, startedAt, maxWaitMs)) break;
531
- data = await this.client.post("api/v1/signal-to-copy", {
532
- ...request,
533
- signal_run_id: data.run_id
534
- });
539
+ const signalRunId = data.signal_run_id || data.run_id;
540
+ data = await this.client.post("api/v1/signal-to-copy", data.resume_context_persisted === true ? { signal_run_id: signalRunId } : { ...request, signal_run_id: signalRunId });
535
541
  if (!isPendingSignalResponse(data)) break;
536
542
  }
537
543
  if (isPendingSignalResponse(data)) {
@@ -542,6 +548,7 @@ var Signaliz = class {
542
548
  }
543
549
  async createSignalCopyBatch(requests, options) {
544
550
  validateBatchSize(requests);
551
+ requests.forEach(validateSignalToCopyParams);
545
552
  const startedAt = Date.now();
546
553
  const deduplicated = dedupeExactBatchItems(requests, (params) => JSON.stringify({
547
554
  request: signalToCopyRequestBody(params),
@@ -551,7 +558,7 @@ var Signaliz = class {
551
558
  }));
552
559
  const uniqueRequests = deduplicated.uniqueItems;
553
560
  const canUseRecoverableAvailableDataJob = uniqueRequests.length > 25 && uniqueRequests.every(
554
- (params) => params.waitForResult !== false && params.skipCache !== true && params.enableDeepSearch !== true && params.maxWaitMs === void 0 && params.pollIntervalMs === void 0
561
+ (params) => (!params.signalRunId || !params.signalRunId.startsWith("copy_") && [params.companyDomain, params.personName, params.title, params.campaignOffer].every((value) => typeof value === "string" && Boolean(value.trim()))) && params.waitForResult !== false && params.skipCache !== true && params.enableDeepSearch !== true && params.maxWaitMs === void 0 && params.pollIntervalMs === void 0
555
562
  );
556
563
  const uniqueResults = canUseRecoverableAvailableDataJob ? await this.runAvailableSignalCopyBatchJob(uniqueRequests, options) : await this.runSignalCopyBatchChunks(uniqueRequests, options);
557
564
  const results = requests.map((_, index) => ({
@@ -738,7 +745,7 @@ var Signaliz = class {
738
745
  );
739
746
  nextPending.push({
740
747
  ...task,
741
- request: { ...task.request, signal_run_id: signalRunId }
748
+ request: item.resume_context_persisted === true ? { signal_run_id: signalRunId } : { ...task.request, signal_run_id: signalRunId }
742
749
  });
743
750
  }
744
751
  pending = nextPending;
@@ -1088,11 +1095,24 @@ function signalToCopyRequestBody(params) {
1088
1095
  enable_deep_search: params.enableDeepSearch
1089
1096
  });
1090
1097
  }
1098
+ function validateSignalToCopyParams(params) {
1099
+ if (typeof params.signalRunId === "string" && params.signalRunId.trim()) return;
1100
+ const missing = [
1101
+ ["companyDomain", params.companyDomain],
1102
+ ["personName", params.personName],
1103
+ ["title", params.title],
1104
+ ["campaignOffer", params.campaignOffer]
1105
+ ].filter(([, value]) => typeof value !== "string" || !value.trim()).map(([field]) => field);
1106
+ if (missing.length > 0) {
1107
+ throw new TypeError(`Signal to Copy requires signalRunId or: ${missing.join(", ")}`);
1108
+ }
1109
+ }
1091
1110
  function normalizeSignalToCopyResult(data) {
1092
1111
  return {
1093
1112
  success: data.success ?? true,
1094
1113
  status: data.status,
1095
1114
  runId: data.signal_run_id ?? data.run_id,
1115
+ resumeContextPersisted: data.resume_context_persisted,
1096
1116
  strongestSignal: data.strongest_signal ?? "",
1097
1117
  whyNow: data.why_now ?? "",
1098
1118
  subjectLine: data.subject_line ?? "",
@@ -1100,6 +1120,9 @@ function normalizeSignalToCopyResult(data) {
1100
1120
  confidence: data.confidence ?? 0,
1101
1121
  evidenceUrl: data.evidence_url ?? "",
1102
1122
  researchPrompt: data.research_prompt,
1123
+ empty: data.empty,
1124
+ emptyReason: data.empty_reason,
1125
+ researchMetadata: data.research_metadata,
1103
1126
  variations: (data.variations ?? []).map((variation) => ({
1104
1127
  style: variation.style,
1105
1128
  subjectLine: variation.subject_line ?? "",
package/dist/index.d.mts CHANGED
@@ -167,10 +167,10 @@ interface CompanySignalEnrichmentResult {
167
167
  raw: Record<string, unknown>;
168
168
  }
169
169
  interface SignalToCopyParams {
170
- companyDomain: string;
171
- personName: string;
172
- title: string;
173
- campaignOffer: string;
170
+ companyDomain?: string;
171
+ personName?: string;
172
+ title?: string;
173
+ campaignOffer?: string;
174
174
  researchPrompt?: string;
175
175
  signalRunId?: string;
176
176
  /** Bypass cached company signals and start fresh research. */
@@ -193,6 +193,7 @@ interface SignalToCopyResult {
193
193
  success: boolean;
194
194
  status?: string;
195
195
  runId?: string;
196
+ resumeContextPersisted?: boolean;
196
197
  strongestSignal: string;
197
198
  whyNow: string;
198
199
  subjectLine: string;
@@ -200,6 +201,9 @@ interface SignalToCopyResult {
200
201
  confidence: number;
201
202
  evidenceUrl: string;
202
203
  researchPrompt?: string;
204
+ empty?: boolean;
205
+ emptyReason?: string;
206
+ researchMetadata?: Record<string, unknown>;
203
207
  variations: SignalToCopyVariation[];
204
208
  raw: Record<string, unknown>;
205
209
  }
package/dist/index.d.ts CHANGED
@@ -167,10 +167,10 @@ interface CompanySignalEnrichmentResult {
167
167
  raw: Record<string, unknown>;
168
168
  }
169
169
  interface SignalToCopyParams {
170
- companyDomain: string;
171
- personName: string;
172
- title: string;
173
- campaignOffer: string;
170
+ companyDomain?: string;
171
+ personName?: string;
172
+ title?: string;
173
+ campaignOffer?: string;
174
174
  researchPrompt?: string;
175
175
  signalRunId?: string;
176
176
  /** Bypass cached company signals and start fresh research. */
@@ -193,6 +193,7 @@ interface SignalToCopyResult {
193
193
  success: boolean;
194
194
  status?: string;
195
195
  runId?: string;
196
+ resumeContextPersisted?: boolean;
196
197
  strongestSignal: string;
197
198
  whyNow: string;
198
199
  subjectLine: string;
@@ -200,6 +201,9 @@ interface SignalToCopyResult {
200
201
  confidence: number;
201
202
  evidenceUrl: string;
202
203
  researchPrompt?: string;
204
+ empty?: boolean;
205
+ emptyReason?: string;
206
+ researchMetadata?: Record<string, unknown>;
203
207
  variations: SignalToCopyVariation[];
204
208
  raw: Record<string, unknown>;
205
209
  }
package/dist/index.js CHANGED
@@ -59,7 +59,7 @@ function parseError(status, body) {
59
59
  }
60
60
  function mapErrorType(code, status) {
61
61
  if (code === "RATE_LIMITED" || status === 429) return "rate_limited";
62
- if (code === "VALIDATION_ERROR" || status === 400) return "validation";
62
+ if (code === "VALIDATION_ERROR" || code === "SIGNAL_RUN_COMPANY_MISMATCH" || status === 400) return "validation";
63
63
  if (code === "NOT_FOUND" || status === 404) return "not_found";
64
64
  if (code === "NO_SUPPORTED_SIGNAL") return "not_found";
65
65
  if (code === "AUTH_EXPIRED" || status === 401) return "auth_expired";
@@ -87,6 +87,7 @@ var HttpClient = class {
87
87
  }
88
88
  async request(functionName, body, method = "POST") {
89
89
  let lastError;
90
+ const idempotencyKey = method === "POST" ? requestIdempotencyKey(body) : void 0;
90
91
  for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
91
92
  let timer;
92
93
  try {
@@ -104,7 +105,8 @@ var HttpClient = class {
104
105
  method,
105
106
  headers: {
106
107
  "Content-Type": "application/json",
107
- Authorization: `Bearer ${token}`
108
+ Authorization: `Bearer ${token}`,
109
+ ...idempotencyKey ? { "Idempotency-Key": idempotencyKey } : {}
108
110
  },
109
111
  signal: controller.signal
110
112
  };
@@ -269,6 +271,11 @@ var HttpClient = class {
269
271
  return this.accessTokenPromise;
270
272
  }
271
273
  };
274
+ function requestIdempotencyKey(body) {
275
+ const requested = typeof body.idempotency_key === "string" ? body.idempotency_key.trim() : "";
276
+ const nonce = globalThis.crypto?.randomUUID?.() || `${Date.now()}-${Math.random().toString(16).slice(2)}`;
277
+ return requested || `sdk_${nonce}`;
278
+ }
272
279
  function sleep(ms) {
273
280
  return new Promise((r) => setTimeout(r, ms));
274
281
  }
@@ -369,7 +376,7 @@ function mapMcpErrorType(error) {
369
376
  function mapErrorTypeFromCode(code) {
370
377
  if (!code) return "unknown";
371
378
  if (code === "RATE_LIMITED") return "rate_limited";
372
- if (code === "INVALID_ARGUMENT" || code === "VALIDATION_ERROR" || code === "VALIDATION_001" || code === "APPROVAL_REQUIRED" || code === "WEBHOOK_APPROVAL_REQUIRED" || code === "TARGET_LIMIT_EXCEEDED") return "validation";
379
+ if (code === "INVALID_ARGUMENT" || code === "VALIDATION_ERROR" || code === "VALIDATION_001" || code === "SIGNAL_RUN_COMPANY_MISMATCH" || code === "APPROVAL_REQUIRED" || code === "WEBHOOK_APPROVAL_REQUIRED" || code === "TARGET_LIMIT_EXCEEDED") return "validation";
373
380
  if (code === "AUTH_REQUIRED" || code === "AUTH_001") return "auth_expired";
374
381
  if (code === "QUOTA_EXCEEDED") return "insufficient_credits";
375
382
  if (code === "BUILD_NOT_READY" || code === "ARTIFACT_NOT_READY") return "not_found";
@@ -548,17 +555,16 @@ var Signaliz = class {
548
555
  };
549
556
  }
550
557
  async signalToCopy(params) {
558
+ validateSignalToCopyParams(params);
551
559
  const request = signalToCopyRequestBody(params);
552
560
  let data = await this.client.post("api/v1/signal-to-copy", request);
553
- if (params.waitForResult !== false && data.run_id && isPendingSignalResponse(data)) {
561
+ if (params.waitForResult !== false && (data.signal_run_id || data.run_id) && isPendingSignalResponse(data)) {
554
562
  const maxWaitMs = Math.max(1e3, params.maxWaitMs ?? 20 * 6e4);
555
563
  const startedAt = Date.now();
556
564
  while (Date.now() - startedAt < maxWaitMs) {
557
565
  if (!await waitForNextSignalPoll(data, params.pollIntervalMs, startedAt, maxWaitMs)) break;
558
- data = await this.client.post("api/v1/signal-to-copy", {
559
- ...request,
560
- signal_run_id: data.run_id
561
- });
566
+ const signalRunId = data.signal_run_id || data.run_id;
567
+ data = await this.client.post("api/v1/signal-to-copy", data.resume_context_persisted === true ? { signal_run_id: signalRunId } : { ...request, signal_run_id: signalRunId });
562
568
  if (!isPendingSignalResponse(data)) break;
563
569
  }
564
570
  if (isPendingSignalResponse(data)) {
@@ -569,6 +575,7 @@ var Signaliz = class {
569
575
  }
570
576
  async createSignalCopyBatch(requests, options) {
571
577
  validateBatchSize(requests);
578
+ requests.forEach(validateSignalToCopyParams);
572
579
  const startedAt = Date.now();
573
580
  const deduplicated = dedupeExactBatchItems(requests, (params) => JSON.stringify({
574
581
  request: signalToCopyRequestBody(params),
@@ -578,7 +585,7 @@ var Signaliz = class {
578
585
  }));
579
586
  const uniqueRequests = deduplicated.uniqueItems;
580
587
  const canUseRecoverableAvailableDataJob = uniqueRequests.length > 25 && uniqueRequests.every(
581
- (params) => params.waitForResult !== false && params.skipCache !== true && params.enableDeepSearch !== true && params.maxWaitMs === void 0 && params.pollIntervalMs === void 0
588
+ (params) => (!params.signalRunId || !params.signalRunId.startsWith("copy_") && [params.companyDomain, params.personName, params.title, params.campaignOffer].every((value) => typeof value === "string" && Boolean(value.trim()))) && params.waitForResult !== false && params.skipCache !== true && params.enableDeepSearch !== true && params.maxWaitMs === void 0 && params.pollIntervalMs === void 0
582
589
  );
583
590
  const uniqueResults = canUseRecoverableAvailableDataJob ? await this.runAvailableSignalCopyBatchJob(uniqueRequests, options) : await this.runSignalCopyBatchChunks(uniqueRequests, options);
584
591
  const results = requests.map((_, index) => ({
@@ -765,7 +772,7 @@ var Signaliz = class {
765
772
  );
766
773
  nextPending.push({
767
774
  ...task,
768
- request: { ...task.request, signal_run_id: signalRunId }
775
+ request: item.resume_context_persisted === true ? { signal_run_id: signalRunId } : { ...task.request, signal_run_id: signalRunId }
769
776
  });
770
777
  }
771
778
  pending = nextPending;
@@ -1115,11 +1122,24 @@ function signalToCopyRequestBody(params) {
1115
1122
  enable_deep_search: params.enableDeepSearch
1116
1123
  });
1117
1124
  }
1125
+ function validateSignalToCopyParams(params) {
1126
+ if (typeof params.signalRunId === "string" && params.signalRunId.trim()) return;
1127
+ const missing = [
1128
+ ["companyDomain", params.companyDomain],
1129
+ ["personName", params.personName],
1130
+ ["title", params.title],
1131
+ ["campaignOffer", params.campaignOffer]
1132
+ ].filter(([, value]) => typeof value !== "string" || !value.trim()).map(([field]) => field);
1133
+ if (missing.length > 0) {
1134
+ throw new TypeError(`Signal to Copy requires signalRunId or: ${missing.join(", ")}`);
1135
+ }
1136
+ }
1118
1137
  function normalizeSignalToCopyResult(data) {
1119
1138
  return {
1120
1139
  success: data.success ?? true,
1121
1140
  status: data.status,
1122
1141
  runId: data.signal_run_id ?? data.run_id,
1142
+ resumeContextPersisted: data.resume_context_persisted,
1123
1143
  strongestSignal: data.strongest_signal ?? "",
1124
1144
  whyNow: data.why_now ?? "",
1125
1145
  subjectLine: data.subject_line ?? "",
@@ -1127,6 +1147,9 @@ function normalizeSignalToCopyResult(data) {
1127
1147
  confidence: data.confidence ?? 0,
1128
1148
  evidenceUrl: data.evidence_url ?? "",
1129
1149
  researchPrompt: data.research_prompt,
1150
+ empty: data.empty,
1151
+ emptyReason: data.empty_reason,
1152
+ researchMetadata: data.research_metadata,
1130
1153
  variations: (data.variations ?? []).map((variation) => ({
1131
1154
  style: variation.style,
1132
1155
  subjectLine: variation.subject_line ?? "",
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  Signaliz,
3
3
  SignalizError
4
- } from "./chunk-A7B63XMZ.mjs";
4
+ } from "./chunk-B4OMGBD5.mjs";
5
5
  export {
6
6
  Signaliz,
7
7
  SignalizError
@@ -63,7 +63,7 @@ function parseError(status, body) {
63
63
  }
64
64
  function mapErrorType(code, status) {
65
65
  if (code === "RATE_LIMITED" || status === 429) return "rate_limited";
66
- if (code === "VALIDATION_ERROR" || status === 400) return "validation";
66
+ if (code === "VALIDATION_ERROR" || code === "SIGNAL_RUN_COMPANY_MISMATCH" || status === 400) return "validation";
67
67
  if (code === "NOT_FOUND" || status === 404) return "not_found";
68
68
  if (code === "NO_SUPPORTED_SIGNAL") return "not_found";
69
69
  if (code === "AUTH_EXPIRED" || status === 401) return "auth_expired";
@@ -91,6 +91,7 @@ var HttpClient = class {
91
91
  }
92
92
  async request(functionName, body, method = "POST") {
93
93
  let lastError;
94
+ const idempotencyKey = method === "POST" ? requestIdempotencyKey(body) : void 0;
94
95
  for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
95
96
  let timer;
96
97
  try {
@@ -108,7 +109,8 @@ var HttpClient = class {
108
109
  method,
109
110
  headers: {
110
111
  "Content-Type": "application/json",
111
- Authorization: `Bearer ${token}`
112
+ Authorization: `Bearer ${token}`,
113
+ ...idempotencyKey ? { "Idempotency-Key": idempotencyKey } : {}
112
114
  },
113
115
  signal: controller.signal
114
116
  };
@@ -273,6 +275,11 @@ var HttpClient = class {
273
275
  return this.accessTokenPromise;
274
276
  }
275
277
  };
278
+ function requestIdempotencyKey(body) {
279
+ const requested = typeof body.idempotency_key === "string" ? body.idempotency_key.trim() : "";
280
+ const nonce = globalThis.crypto?.randomUUID?.() || `${Date.now()}-${Math.random().toString(16).slice(2)}`;
281
+ return requested || `sdk_${nonce}`;
282
+ }
276
283
  function sleep(ms) {
277
284
  return new Promise((r) => setTimeout(r, ms));
278
285
  }
@@ -373,7 +380,7 @@ function mapMcpErrorType(error) {
373
380
  function mapErrorTypeFromCode(code) {
374
381
  if (!code) return "unknown";
375
382
  if (code === "RATE_LIMITED") return "rate_limited";
376
- if (code === "INVALID_ARGUMENT" || code === "VALIDATION_ERROR" || code === "VALIDATION_001" || code === "APPROVAL_REQUIRED" || code === "WEBHOOK_APPROVAL_REQUIRED" || code === "TARGET_LIMIT_EXCEEDED") return "validation";
383
+ if (code === "INVALID_ARGUMENT" || code === "VALIDATION_ERROR" || code === "VALIDATION_001" || code === "SIGNAL_RUN_COMPANY_MISMATCH" || code === "APPROVAL_REQUIRED" || code === "WEBHOOK_APPROVAL_REQUIRED" || code === "TARGET_LIMIT_EXCEEDED") return "validation";
377
384
  if (code === "AUTH_REQUIRED" || code === "AUTH_001") return "auth_expired";
378
385
  if (code === "QUOTA_EXCEEDED") return "insufficient_credits";
379
386
  if (code === "BUILD_NOT_READY" || code === "ARTIFACT_NOT_READY") return "not_found";
@@ -552,17 +559,16 @@ var Signaliz = class {
552
559
  };
553
560
  }
554
561
  async signalToCopy(params) {
562
+ validateSignalToCopyParams(params);
555
563
  const request = signalToCopyRequestBody(params);
556
564
  let data = await this.client.post("api/v1/signal-to-copy", request);
557
- if (params.waitForResult !== false && data.run_id && isPendingSignalResponse(data)) {
565
+ if (params.waitForResult !== false && (data.signal_run_id || data.run_id) && isPendingSignalResponse(data)) {
558
566
  const maxWaitMs = Math.max(1e3, params.maxWaitMs ?? 20 * 6e4);
559
567
  const startedAt = Date.now();
560
568
  while (Date.now() - startedAt < maxWaitMs) {
561
569
  if (!await waitForNextSignalPoll(data, params.pollIntervalMs, startedAt, maxWaitMs)) break;
562
- data = await this.client.post("api/v1/signal-to-copy", {
563
- ...request,
564
- signal_run_id: data.run_id
565
- });
570
+ const signalRunId = data.signal_run_id || data.run_id;
571
+ data = await this.client.post("api/v1/signal-to-copy", data.resume_context_persisted === true ? { signal_run_id: signalRunId } : { ...request, signal_run_id: signalRunId });
566
572
  if (!isPendingSignalResponse(data)) break;
567
573
  }
568
574
  if (isPendingSignalResponse(data)) {
@@ -573,6 +579,7 @@ var Signaliz = class {
573
579
  }
574
580
  async createSignalCopyBatch(requests, options) {
575
581
  validateBatchSize(requests);
582
+ requests.forEach(validateSignalToCopyParams);
576
583
  const startedAt = Date.now();
577
584
  const deduplicated = dedupeExactBatchItems(requests, (params) => JSON.stringify({
578
585
  request: signalToCopyRequestBody(params),
@@ -582,7 +589,7 @@ var Signaliz = class {
582
589
  }));
583
590
  const uniqueRequests = deduplicated.uniqueItems;
584
591
  const canUseRecoverableAvailableDataJob = uniqueRequests.length > 25 && uniqueRequests.every(
585
- (params) => params.waitForResult !== false && params.skipCache !== true && params.enableDeepSearch !== true && params.maxWaitMs === void 0 && params.pollIntervalMs === void 0
592
+ (params) => (!params.signalRunId || !params.signalRunId.startsWith("copy_") && [params.companyDomain, params.personName, params.title, params.campaignOffer].every((value) => typeof value === "string" && Boolean(value.trim()))) && params.waitForResult !== false && params.skipCache !== true && params.enableDeepSearch !== true && params.maxWaitMs === void 0 && params.pollIntervalMs === void 0
586
593
  );
587
594
  const uniqueResults = canUseRecoverableAvailableDataJob ? await this.runAvailableSignalCopyBatchJob(uniqueRequests, options) : await this.runSignalCopyBatchChunks(uniqueRequests, options);
588
595
  const results = requests.map((_, index) => ({
@@ -769,7 +776,7 @@ var Signaliz = class {
769
776
  );
770
777
  nextPending.push({
771
778
  ...task,
772
- request: { ...task.request, signal_run_id: signalRunId }
779
+ request: item.resume_context_persisted === true ? { signal_run_id: signalRunId } : { ...task.request, signal_run_id: signalRunId }
773
780
  });
774
781
  }
775
782
  pending = nextPending;
@@ -1119,11 +1126,24 @@ function signalToCopyRequestBody(params) {
1119
1126
  enable_deep_search: params.enableDeepSearch
1120
1127
  });
1121
1128
  }
1129
+ function validateSignalToCopyParams(params) {
1130
+ if (typeof params.signalRunId === "string" && params.signalRunId.trim()) return;
1131
+ const missing = [
1132
+ ["companyDomain", params.companyDomain],
1133
+ ["personName", params.personName],
1134
+ ["title", params.title],
1135
+ ["campaignOffer", params.campaignOffer]
1136
+ ].filter(([, value]) => typeof value !== "string" || !value.trim()).map(([field]) => field);
1137
+ if (missing.length > 0) {
1138
+ throw new TypeError(`Signal to Copy requires signalRunId or: ${missing.join(", ")}`);
1139
+ }
1140
+ }
1122
1141
  function normalizeSignalToCopyResult(data) {
1123
1142
  return {
1124
1143
  success: data.success ?? true,
1125
1144
  status: data.status,
1126
1145
  runId: data.signal_run_id ?? data.run_id,
1146
+ resumeContextPersisted: data.resume_context_persisted,
1127
1147
  strongestSignal: data.strongest_signal ?? "",
1128
1148
  whyNow: data.why_now ?? "",
1129
1149
  subjectLine: data.subject_line ?? "",
@@ -1131,6 +1151,9 @@ function normalizeSignalToCopyResult(data) {
1131
1151
  confidence: data.confidence ?? 0,
1132
1152
  evidenceUrl: data.evidence_url ?? "",
1133
1153
  researchPrompt: data.research_prompt,
1154
+ empty: data.empty,
1155
+ emptyReason: data.empty_reason,
1156
+ researchMetadata: data.research_metadata,
1134
1157
  variations: (data.variations ?? []).map((variation) => ({
1135
1158
  style: variation.style,
1136
1159
  subjectLine: variation.subject_line ?? "",
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  Signaliz
4
- } from "./chunk-A7B63XMZ.mjs";
4
+ } from "./chunk-B4OMGBD5.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.40",
3
+ "version": "1.0.42",
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",