@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 +11 -0
- package/dist/{chunk-A7B63XMZ.mjs → chunk-B4OMGBD5.mjs} +33 -10
- package/dist/index.d.mts +8 -4
- package/dist/index.d.ts +8 -4
- package/dist/index.js +33 -10
- package/dist/index.mjs +1 -1
- package/dist/mcp-config.js +33 -10
- package/dist/mcp-config.mjs +1 -1
- package/package.json +1 -1
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
|
-
|
|
532
|
-
|
|
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
|
|
171
|
-
personName
|
|
172
|
-
title
|
|
173
|
-
campaignOffer
|
|
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
|
|
171
|
-
personName
|
|
172
|
-
title
|
|
173
|
-
campaignOffer
|
|
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
|
-
|
|
559
|
-
|
|
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
package/dist/mcp-config.js
CHANGED
|
@@ -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
|
-
|
|
563
|
-
|
|
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 ?? "",
|
package/dist/mcp-config.mjs
CHANGED
package/package.json
CHANGED