@signaliz/sdk 1.0.59 → 1.0.61
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 +22 -2
- package/dist/{chunk-VKBUQTDS.mjs → chunk-ENA7AVFB.mjs} +96 -9
- package/dist/index.d.mts +46 -4
- package/dist/index.d.ts +46 -4
- package/dist/index.js +96 -9
- package/dist/index.mjs +1 -1
- package/dist/mcp-config.js +96 -9
- package/dist/mcp-config.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -44,6 +44,10 @@ const verified = await signaliz.verifyEmail(found.email!, {
|
|
|
44
44
|
skipCache: true,
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
+
// Syntax failures are terminal local results, with no HTTP or provider call.
|
|
48
|
+
const malformed = await signaliz.verifyEmail('badformat@@gmail..com');
|
|
49
|
+
console.log(malformed.success, malformed.isMalformed, malformed.verificationVerdict);
|
|
50
|
+
|
|
47
51
|
// The SDK waits for long checks by default. For an agent handoff, return early
|
|
48
52
|
// and resume the exact provider run later without duplicate spend.
|
|
49
53
|
const queuedVerification = await signaliz.verifyEmail('jane@example.com', {
|
|
@@ -69,6 +73,12 @@ const signalDiscovery = await signaliz.discoverSignals({
|
|
|
69
73
|
limit: 100,
|
|
70
74
|
sources: ['web', 'news'],
|
|
71
75
|
});
|
|
76
|
+
const painSignalDiscovery = await signaliz.discoverSignals({
|
|
77
|
+
query: 'companies showing signals they need account-signal automation',
|
|
78
|
+
icpContext: 'B2B SaaS companies with outbound sales teams and manual account research',
|
|
79
|
+
limit: 100,
|
|
80
|
+
sources: ['web', 'news'],
|
|
81
|
+
});
|
|
72
82
|
const completedDiscovery = signalDiscovery.signalSearchRunId
|
|
73
83
|
? await signaliz.discoverSignals({ signalSearchRunId: signalDiscovery.signalSearchRunId })
|
|
74
84
|
: signalDiscovery;
|
|
@@ -150,12 +160,15 @@ Signal to Copy also shares company research across copy recipients.
|
|
|
150
160
|
Signals Everything is query-first rather than a row batch. It defaults to 100
|
|
151
161
|
distinct companies and supports up to 1,000. Every returned signal contains a
|
|
152
162
|
company name, company domain, dated evidence, and a source URL. The typed
|
|
153
|
-
`costGuardrail` reports
|
|
163
|
+
`costGuardrail` reports projected and observed source acquisition cost plus
|
|
164
|
+
identity-resolution cost, receipt source, and fallback-provider request count;
|
|
154
165
|
Signaliz returns signals only when cost stays strictly below $0.01 per qualified
|
|
155
166
|
company. A request is a ceiling, not a promise: it can return fewer results
|
|
156
167
|
when public evidence cannot verify both a date and company identity. It is
|
|
157
168
|
included on Team and Agency plans and can be resumed with
|
|
158
|
-
`signalSearchRunId` without duplicate source acquisition.
|
|
169
|
+
`signalSearchRunId` without duplicate source acquisition. Pass `icpContext` to
|
|
170
|
+
ground an open-ended request such as “companies showing signals they need our
|
|
171
|
+
product” in the user's target market.
|
|
159
172
|
|
|
160
173
|
Company Signal Enrichment defaults `online` and `enableDeepSearch` to `true`
|
|
161
174
|
unless the caller explicitly disables them. Its `searchMode` defaults to
|
|
@@ -166,6 +179,13 @@ complete, evidence-backed email variations when supported signals are available.
|
|
|
166
179
|
Signal to Copy is cache-first and defaults `enableDeepSearch` to `false` so it
|
|
167
180
|
normally finishes within an agent turn; set `skipCache` or `enableDeepSearch`
|
|
168
181
|
only when fresh or deeper research is required.
|
|
182
|
+
Every core request uses the authenticated workspace. Find Email and Verify Email
|
|
183
|
+
honor that workspace's configured cache window; eligible hits cost 0 credits,
|
|
184
|
+
while fresh discovery or verification is charged on every plan. Team, Agency,
|
|
185
|
+
and Pay-As-You-Go include fresh Company Signals and Signal to Copy. Those plans
|
|
186
|
+
still check eligible workspace signal data first even when Company Signals is
|
|
187
|
+
set to No cache. `lookbackDays` remains a hard evidence-age bound for both
|
|
188
|
+
cached and fresh Company Signals and Signal to Copy results.
|
|
169
189
|
Unclassified evidence is returned by signal enrichment but is not used to
|
|
170
190
|
generate outreach copy.
|
|
171
191
|
Each normalized signal keeps `date` separate from crawl-time `detectedAt` and
|
|
@@ -9,17 +9,21 @@ var SignalizError = class extends Error {
|
|
|
9
9
|
this.details = data.details;
|
|
10
10
|
}
|
|
11
11
|
get isRetryable() {
|
|
12
|
+
if (typeof this.details?.retry_eligible === "boolean") {
|
|
13
|
+
return this.details.retry_eligible;
|
|
14
|
+
}
|
|
12
15
|
return this.errorType === "rate_limited" || this.errorType === "timeout" || this.errorType === "provider_error";
|
|
13
16
|
}
|
|
14
17
|
};
|
|
15
18
|
function parseError(status, body) {
|
|
16
19
|
if (body?.error && typeof body.error === "object") {
|
|
20
|
+
const nestedDetails = body.error.details && typeof body.error.details === "object" ? body.error.details : {};
|
|
17
21
|
return new SignalizError({
|
|
18
22
|
code: body.error.code || `HTTP_${status}`,
|
|
19
23
|
message: body.error.message || `Request failed with status ${status}`,
|
|
20
24
|
errorType: mapErrorType(body.error.code, status),
|
|
21
25
|
retryAfter: retryAfterSeconds(body.error),
|
|
22
|
-
details: body.error
|
|
26
|
+
details: mergePublicErrorDetails({ ...body, ...body.error }, nestedDetails)
|
|
23
27
|
});
|
|
24
28
|
}
|
|
25
29
|
const code = typeof body?.error_code === "string" && body.error_code.trim() ? body.error_code.trim() : `HTTP_${status}`;
|
|
@@ -28,7 +32,7 @@ function parseError(status, body) {
|
|
|
28
32
|
message: body?.message || body?.error || `Request failed with status ${status}`,
|
|
29
33
|
errorType: mapErrorType(code, status),
|
|
30
34
|
retryAfter: retryAfterSeconds(body),
|
|
31
|
-
details: body?.details
|
|
35
|
+
details: mergePublicErrorDetails(body, body?.details)
|
|
32
36
|
});
|
|
33
37
|
}
|
|
34
38
|
function retryAfterSeconds(body) {
|
|
@@ -40,13 +44,26 @@ function publicRetryDetails(body) {
|
|
|
40
44
|
...body?.retry_eligible !== void 0 ? { retry_eligible: body.retry_eligible } : {},
|
|
41
45
|
...body?.retry_after !== void 0 ? { retry_after: body.retry_after } : {},
|
|
42
46
|
...body?.retry_after_seconds !== void 0 ? { retry_after_seconds: body.retry_after_seconds } : {},
|
|
47
|
+
...body?.retry_strategy !== void 0 ? { retry_strategy: body.retry_strategy } : {},
|
|
48
|
+
...body?.credits_used !== void 0 ? { credits_used: body.credits_used } : {},
|
|
49
|
+
...body?.credits_charged !== void 0 ? { credits_charged: body.credits_charged } : {},
|
|
50
|
+
...body?.job_id !== void 0 ? { job_id: body.job_id } : {},
|
|
51
|
+
...body?.verification_run_id !== void 0 ? { verification_run_id: body.verification_run_id } : {},
|
|
43
52
|
...body?.run_id !== void 0 ? { run_id: body.run_id } : {},
|
|
53
|
+
...body?.suggested_action !== void 0 ? { suggested_action: body.suggested_action } : {},
|
|
44
54
|
...body?.signal_run_id !== void 0 ? { signal_run_id: body.signal_run_id } : {},
|
|
45
55
|
...body?.company_signal_run_id !== void 0 ? { company_signal_run_id: body.company_signal_run_id } : {},
|
|
46
56
|
...body?.resume_context_persisted !== void 0 ? { resume_context_persisted: body.resume_context_persisted } : {}
|
|
47
57
|
};
|
|
48
58
|
return Object.keys(details).length > 0 ? details : void 0;
|
|
49
59
|
}
|
|
60
|
+
function mergePublicErrorDetails(body, explicitDetails) {
|
|
61
|
+
const merged = { ...publicRetryDetails(body) || {} };
|
|
62
|
+
if (explicitDetails && typeof explicitDetails === "object" && !Array.isArray(explicitDetails)) {
|
|
63
|
+
Object.assign(merged, explicitDetails);
|
|
64
|
+
}
|
|
65
|
+
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
66
|
+
}
|
|
50
67
|
function mapErrorType(code, status) {
|
|
51
68
|
if (code === "RATE_LIMITED" || status === 429) return "rate_limited";
|
|
52
69
|
if (code === "VALIDATION_ERROR" || code === "SIGNAL_RUN_COMPANY_MISMATCH" || status === 400) return "validation";
|
|
@@ -108,7 +125,9 @@ var HttpClient = class {
|
|
|
108
125
|
const errBody = await res.json().catch(() => ({}));
|
|
109
126
|
const err = parseError(res.status, errBody);
|
|
110
127
|
if (err.isRetryable && attempt < this.maxRetries) {
|
|
111
|
-
const
|
|
128
|
+
const retryWithNewKey = err.details?.retry_strategy === "new_idempotency_key" && err.details?.credits_used === 0 && err.details?.credits_charged === 0;
|
|
129
|
+
if (retryWithNewKey) throw err;
|
|
130
|
+
const wait = err.retryAfter !== void 0 ? err.retryAfter * 1e3 : Math.min(1e3 * Math.pow(2, attempt), 1e4);
|
|
112
131
|
await sleep(wait);
|
|
113
132
|
lastError = err;
|
|
114
133
|
continue;
|
|
@@ -408,6 +427,29 @@ var Signaliz = class {
|
|
|
408
427
|
);
|
|
409
428
|
}
|
|
410
429
|
async verifyEmail(email, options) {
|
|
430
|
+
const normalizedEmail = typeof email === "string" ? email.trim() : "";
|
|
431
|
+
if (options?.dryRun !== true && !options?.verificationRunId && normalizedEmail && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(normalizedEmail)) {
|
|
432
|
+
const error = `Invalid email format: "${normalizedEmail}"`;
|
|
433
|
+
return normalizeVerifyEmailResult(normalizedEmail, {
|
|
434
|
+
success: false,
|
|
435
|
+
email: normalizedEmail,
|
|
436
|
+
status: "completed",
|
|
437
|
+
is_valid: false,
|
|
438
|
+
is_deliverable: false,
|
|
439
|
+
email_verified: false,
|
|
440
|
+
verified_for_sending: false,
|
|
441
|
+
is_malformed: true,
|
|
442
|
+
verification_status: "malformed",
|
|
443
|
+
verification_verdict: "malformed",
|
|
444
|
+
verification_source: "input_validation",
|
|
445
|
+
provider_status: "rejected_before_provider",
|
|
446
|
+
error,
|
|
447
|
+
error_code: "INPUT_001",
|
|
448
|
+
retry_eligible: false,
|
|
449
|
+
credits_used: 0,
|
|
450
|
+
live_provider_called: false
|
|
451
|
+
});
|
|
452
|
+
}
|
|
411
453
|
const request = compact({
|
|
412
454
|
email: email || void 0,
|
|
413
455
|
verification_run_id: options?.verificationRunId,
|
|
@@ -742,7 +784,10 @@ var Signaliz = class {
|
|
|
742
784
|
);
|
|
743
785
|
const uniqueTasks = deduplicated.uniqueItems;
|
|
744
786
|
const recoverableBatch = path === "api/v1/find-email" ? { label: "Find Email", pageSize: 500, maxWaitMs: 20 * 6e4 } : path === "api/v1/verify-email" ? { label: "Verify Email", pageSize: 500, maxWaitMs: 20 * 6e4 } : void 0;
|
|
745
|
-
|
|
787
|
+
const containsFindRecoveryReads = path === "api/v1/find-email" && uniqueTasks.some(
|
|
788
|
+
(task) => typeof task.request.run_id === "string" && task.request.run_id.trim()
|
|
789
|
+
);
|
|
790
|
+
if (path !== "api/v1/company-signals" && recoverableBatch && uniqueTasks.length > 25 && !containsFindRecoveryReads) {
|
|
746
791
|
const rows = await this.runRecoverableCoreBatchJob(
|
|
747
792
|
path,
|
|
748
793
|
uniqueTasks.map((task) => task.request),
|
|
@@ -868,13 +913,27 @@ function batchItemFailure(index, error, raw) {
|
|
|
868
913
|
raw?.retry_after_seconds ?? raw?.retry_after ?? raw?.retryAfterSeconds ?? (Number.isFinite(Number(raw?.retry_after_ms)) ? Number(raw?.retry_after_ms) / 1e3 : void 0)
|
|
869
914
|
);
|
|
870
915
|
const retryEligible = raw?.retry_eligible ?? raw?.retryEligible;
|
|
916
|
+
const retryStrategy = raw?.retry_strategy ?? raw?.retryStrategy;
|
|
917
|
+
const creditsUsed = raw?.credits_used ?? raw?.creditsUsed;
|
|
918
|
+
const creditsCharged = raw?.credits_charged ?? raw?.creditsCharged;
|
|
919
|
+
const runId = raw?.run_id ?? raw?.runId;
|
|
920
|
+
const verificationRunId = raw?.verification_run_id ?? raw?.verificationRunId;
|
|
921
|
+
const jobId = raw?.job_id ?? raw?.jobId;
|
|
922
|
+
const suggestedAction = raw?.suggested_action ?? raw?.suggestedAction;
|
|
871
923
|
return {
|
|
872
924
|
index,
|
|
873
925
|
success: false,
|
|
874
926
|
error,
|
|
875
927
|
...errorCode ? { errorCode } : {},
|
|
876
928
|
...typeof retryEligible === "boolean" ? { retryEligible } : {},
|
|
877
|
-
...Number.isFinite(retryAfterSeconds2) && retryAfterSeconds2 >= 0 ? { retryAfterSeconds: retryAfterSeconds2 } : {}
|
|
929
|
+
...Number.isFinite(retryAfterSeconds2) && retryAfterSeconds2 >= 0 ? { retryAfterSeconds: retryAfterSeconds2 } : {},
|
|
930
|
+
...retryStrategy === "new_idempotency_key" ? { retryStrategy } : {},
|
|
931
|
+
...creditsUsed === 0 ? { creditsUsed: 0 } : {},
|
|
932
|
+
...creditsCharged === 0 ? { creditsCharged: 0 } : {},
|
|
933
|
+
...typeof runId === "string" && runId.trim() ? { runId: runId.trim() } : {},
|
|
934
|
+
...typeof verificationRunId === "string" && verificationRunId.trim() ? { verificationRunId: verificationRunId.trim() } : {},
|
|
935
|
+
...typeof jobId === "string" && jobId.trim() ? { jobId: jobId.trim() } : {},
|
|
936
|
+
...typeof suggestedAction === "string" && suggestedAction.trim() ? { suggestedAction: suggestedAction.trim() } : {}
|
|
878
937
|
};
|
|
879
938
|
}
|
|
880
939
|
function recoverableJobRowFailed(row) {
|
|
@@ -889,6 +948,7 @@ function compact(input) {
|
|
|
889
948
|
}
|
|
890
949
|
function findEmailRequestBody(params) {
|
|
891
950
|
return compact({
|
|
951
|
+
run_id: params.runId,
|
|
892
952
|
company_domain: params.companyDomain,
|
|
893
953
|
full_name: params.fullName,
|
|
894
954
|
first_name: params.firstName,
|
|
@@ -928,6 +988,7 @@ function normalizeCoreProductDryRunResult(data) {
|
|
|
928
988
|
};
|
|
929
989
|
}
|
|
930
990
|
function normalizeFindEmailResult(data) {
|
|
991
|
+
const processing = String(data.status || "").toLowerCase() === "processing";
|
|
931
992
|
const failed = coreProductResponseFailed(data);
|
|
932
993
|
const rawEmail = typeof data.email === "string" && data.email.trim() ? data.email.trim() : null;
|
|
933
994
|
const email = failed ? null : rawEmail;
|
|
@@ -940,7 +1001,7 @@ function normalizeFindEmailResult(data) {
|
|
|
940
1001
|
const verified = !explicitlyNotSendSafe && (data.is_valid === true || data.isValid === true || data.email_verified === true || data.verified_for_sending === true || ["valid", "deliverable"].includes(String(verificationStatus).toLowerCase()));
|
|
941
1002
|
const verificationAgeDays = typeof data.verification_age_days === "number" && Number.isFinite(data.verification_age_days) ? data.verification_age_days : null;
|
|
942
1003
|
return {
|
|
943
|
-
success: !failed && found,
|
|
1004
|
+
success: processing || !failed && found,
|
|
944
1005
|
found,
|
|
945
1006
|
email,
|
|
946
1007
|
firstName: data.first_name,
|
|
@@ -967,8 +1028,12 @@ function normalizeFindEmailResult(data) {
|
|
|
967
1028
|
liveProviderCalled: data.live_provider_called,
|
|
968
1029
|
openrouterCalled: data.openrouter_called,
|
|
969
1030
|
byoOpenrouterUsed: data.byo_openrouter_used,
|
|
970
|
-
error: found ? void 0 : coreProductErrorMessage(data) ?? "No verified email found",
|
|
971
|
-
errorCode: found ? void 0 : coreProductErrorCode(data) ?? "EMAIL_NOT_FOUND",
|
|
1031
|
+
error: processing || found ? void 0 : coreProductErrorMessage(data) ?? "No verified email found",
|
|
1032
|
+
errorCode: processing || found ? void 0 : coreProductErrorCode(data) ?? "EMAIL_NOT_FOUND",
|
|
1033
|
+
status: processing ? "processing" : "completed",
|
|
1034
|
+
runId: data.run_id,
|
|
1035
|
+
nextPollAfterSeconds: data.next_poll_after_seconds,
|
|
1036
|
+
suggestedAction: data.suggested_action,
|
|
972
1037
|
raw: data
|
|
973
1038
|
};
|
|
974
1039
|
}
|
|
@@ -1104,6 +1169,12 @@ function validateSignalDiscoveryParams(params) {
|
|
|
1104
1169
|
if (query && (query.length < 2 || query.length > 2e3)) {
|
|
1105
1170
|
throw new RangeError("query must contain between 2 and 2000 characters");
|
|
1106
1171
|
}
|
|
1172
|
+
if (params.icpContext !== void 0) {
|
|
1173
|
+
const icpContext = typeof params.icpContext === "string" ? params.icpContext.trim() : "";
|
|
1174
|
+
if (icpContext.length < 2 || icpContext.length > 4e3) {
|
|
1175
|
+
throw new RangeError("icpContext must contain between 2 and 4000 characters");
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1107
1178
|
if (params.limit !== void 0 && (!Number.isInteger(params.limit) || params.limit < 1 || params.limit > 1e3)) {
|
|
1108
1179
|
throw new RangeError("limit must be an integer between 1 and 1000");
|
|
1109
1180
|
}
|
|
@@ -1128,6 +1199,7 @@ function signalDiscoveryRequestBody(params) {
|
|
|
1128
1199
|
}
|
|
1129
1200
|
return compact({
|
|
1130
1201
|
query: params.query?.trim(),
|
|
1202
|
+
icp_context: params.icpContext?.trim(),
|
|
1131
1203
|
limit: params.limit ?? 100,
|
|
1132
1204
|
sources: params.sources,
|
|
1133
1205
|
lookback_days: params.lookbackDays,
|
|
@@ -1153,6 +1225,8 @@ function normalizeSignalDiscoveryResult(params, data) {
|
|
|
1153
1225
|
availableCompanySignalCount: Number.isFinite(Number(data.available_company_signal_count)) ? Number(data.available_company_signal_count) : void 0,
|
|
1154
1226
|
sourceLanes: Array.isArray(data.source_lanes) ? data.source_lanes.filter((lane) => typeof lane === "string") : void 0,
|
|
1155
1227
|
evidenceValidation: data.evidence_validation && typeof data.evidence_validation === "object" ? data.evidence_validation : void 0,
|
|
1228
|
+
discoveryDiagnostics: data.discovery_diagnostics && typeof data.discovery_diagnostics === "object" ? data.discovery_diagnostics : void 0,
|
|
1229
|
+
identityResolution: data.identity_resolution && typeof data.identity_resolution === "object" ? data.identity_resolution : void 0,
|
|
1156
1230
|
costGuardrail: normalizeSignalDiscoveryCostGuardrail(data.cost_guardrail),
|
|
1157
1231
|
warnings: Array.isArray(data.warnings) ? data.warnings.filter((warning) => typeof warning === "string") : void 0,
|
|
1158
1232
|
raw: data
|
|
@@ -1181,7 +1255,15 @@ function normalizeSignalDiscoveryCostGuardrail(value) {
|
|
|
1181
1255
|
guardrail.projected_cost_per_requested_company_signal_usd
|
|
1182
1256
|
),
|
|
1183
1257
|
sourceCostUsd: numberOrNullable(guardrail.source_cost_usd),
|
|
1184
|
-
costSource: ["observed", "catalog_bounded", "mixed", "unavailable"].includes(String(guardrail.cost_source)) ? String(guardrail.cost_source) : void 0,
|
|
1258
|
+
costSource: ["observed", "catalog_preflight", "catalog_bounded", "mixed", "unavailable"].includes(String(guardrail.cost_source)) ? String(guardrail.cost_source) : void 0,
|
|
1259
|
+
identityResolutionCostUsd: numberOrNullable(guardrail.identity_resolution_cost_usd),
|
|
1260
|
+
identityCostSource: [
|
|
1261
|
+
"observed_zero_credit_receipts",
|
|
1262
|
+
"contractual_zero_cost",
|
|
1263
|
+
"not_used",
|
|
1264
|
+
"unavailable"
|
|
1265
|
+
].includes(String(guardrail.identity_cost_source)) ? String(guardrail.identity_cost_source) : void 0,
|
|
1266
|
+
identityProviderRequests: numberOrUndefined(guardrail.identity_provider_requests),
|
|
1185
1267
|
qualifiedCompanySignalCount: numberOrUndefined(guardrail.qualified_company_signal_count),
|
|
1186
1268
|
returnedCompanySignalCount: numberOrUndefined(guardrail.returned_company_signal_count),
|
|
1187
1269
|
availableCompanySignalCount: numberOrUndefined(guardrail.available_company_signal_count),
|
|
@@ -1285,6 +1367,7 @@ function normalizedFindEmailBatchName(request) {
|
|
|
1285
1367
|
function coreProductBatchRequestKey(path, request) {
|
|
1286
1368
|
if (path === "api/v1/find-email") {
|
|
1287
1369
|
return JSON.stringify([
|
|
1370
|
+
normalizedBatchText(request.run_id),
|
|
1288
1371
|
normalizedFindEmailBatchName(request),
|
|
1289
1372
|
normalizedBatchDomain(request.company_domain),
|
|
1290
1373
|
normalizedBatchLinkedIn(request.linkedin_url),
|
|
@@ -1390,6 +1473,7 @@ function signalToCopyRequestBody(params) {
|
|
|
1390
1473
|
title: params.title,
|
|
1391
1474
|
campaign_offer: params.campaignOffer,
|
|
1392
1475
|
research_prompt: params.researchPrompt,
|
|
1476
|
+
lookback_days: params.lookbackDays,
|
|
1393
1477
|
signal_run_id: params.signalRunId,
|
|
1394
1478
|
skip_cache: params.skipCache,
|
|
1395
1479
|
enable_deep_search: params.enableDeepSearch,
|
|
@@ -1400,6 +1484,9 @@ function signalToCopyRequestBody(params) {
|
|
|
1400
1484
|
}
|
|
1401
1485
|
function validateSignalToCopyParams(params) {
|
|
1402
1486
|
validateCoreProductMaxWaitMs(params.maxWaitMs);
|
|
1487
|
+
if (params.lookbackDays !== void 0 && (!Number.isInteger(params.lookbackDays) || params.lookbackDays < 1 || params.lookbackDays > 365)) {
|
|
1488
|
+
throw new RangeError("lookbackDays must be an integer between 1 and 365");
|
|
1489
|
+
}
|
|
1403
1490
|
if (typeof params.signalRunId === "string" && params.signalRunId.trim()) return;
|
|
1404
1491
|
const missing = [
|
|
1405
1492
|
["companyDomain", params.companyDomain],
|
package/dist/index.d.mts
CHANGED
|
@@ -27,6 +27,20 @@ interface BatchItemResult<T> {
|
|
|
27
27
|
retryEligible?: boolean;
|
|
28
28
|
/** Server-provided delay before retrying the failed row. */
|
|
29
29
|
retryAfterSeconds?: number;
|
|
30
|
+
/** Explicit recovery strategy. A fresh key can repeat provider work and is never automatic. */
|
|
31
|
+
retryStrategy?: 'new_idempotency_key';
|
|
32
|
+
/** Present only when the server proves the failed row consumed zero Signaliz credits. */
|
|
33
|
+
creditsUsed?: 0;
|
|
34
|
+
/** Present only when the server proves the failed row charged zero Signaliz credits. */
|
|
35
|
+
creditsCharged?: 0;
|
|
36
|
+
/** Existing provider run that can be resumed without dispatching new work. */
|
|
37
|
+
runId?: string;
|
|
38
|
+
/** Existing Verify Email run that can be resumed without dispatching new work. */
|
|
39
|
+
verificationRunId?: string;
|
|
40
|
+
/** Existing recoverable batch job. */
|
|
41
|
+
jobId?: string;
|
|
42
|
+
/** Machine-readable recovery action returned by Signaliz. */
|
|
43
|
+
suggestedAction?: string;
|
|
30
44
|
/** Zero-based input index containing the full result for this exact duplicate. */
|
|
31
45
|
duplicateOf?: number;
|
|
32
46
|
}
|
|
@@ -66,8 +80,7 @@ interface CoreProductDryRunResult {
|
|
|
66
80
|
sideEffects: unknown[];
|
|
67
81
|
raw: Record<string, unknown>;
|
|
68
82
|
}
|
|
69
|
-
interface
|
|
70
|
-
companyDomain: string;
|
|
83
|
+
interface FindEmailCommonParams {
|
|
71
84
|
fullName?: string;
|
|
72
85
|
firstName?: string;
|
|
73
86
|
lastName?: string;
|
|
@@ -80,6 +93,15 @@ interface FindEmailParams {
|
|
|
80
93
|
/** Stable retry key for recovering the same request after an ambiguous response. */
|
|
81
94
|
idempotencyKey?: string;
|
|
82
95
|
}
|
|
96
|
+
type FindEmailParams = FindEmailCommonParams & ({
|
|
97
|
+
companyDomain: string;
|
|
98
|
+
/** Omit for a new lookup. */
|
|
99
|
+
runId?: undefined;
|
|
100
|
+
} | {
|
|
101
|
+
/** Resume one existing Find Email run without dispatching a new finder. */
|
|
102
|
+
runId: string;
|
|
103
|
+
companyDomain?: string;
|
|
104
|
+
});
|
|
83
105
|
interface FindEmailResult {
|
|
84
106
|
success: boolean;
|
|
85
107
|
found: boolean;
|
|
@@ -112,6 +134,12 @@ interface FindEmailResult {
|
|
|
112
134
|
byoOpenrouterUsed?: boolean;
|
|
113
135
|
error?: string;
|
|
114
136
|
errorCode?: string;
|
|
137
|
+
/** Processing while an existing provider run is still active; otherwise completed. */
|
|
138
|
+
status?: 'processing' | 'completed';
|
|
139
|
+
/** Durable Find Email run ID for polling without duplicate spend. */
|
|
140
|
+
runId?: string;
|
|
141
|
+
nextPollAfterSeconds?: number;
|
|
142
|
+
suggestedAction?: string;
|
|
115
143
|
raw: Record<string, unknown>;
|
|
116
144
|
}
|
|
117
145
|
interface VerifyEmailResult {
|
|
@@ -250,8 +278,10 @@ interface CompanySignalEnrichmentResult {
|
|
|
250
278
|
type SignalDiscoverySource = 'web' | 'news';
|
|
251
279
|
type SignalDiscoveryStatus = 'planned' | 'queued' | 'processing' | 'completed' | 'completed_with_warnings' | 'failed';
|
|
252
280
|
interface SignalDiscoveryParams {
|
|
253
|
-
/**
|
|
281
|
+
/** Any natural-language signal question. Required for a new search. */
|
|
254
282
|
query?: string;
|
|
283
|
+
/** Optional natural-language ICP, product, or pain context that returned signals must support. */
|
|
284
|
+
icpContext?: string;
|
|
255
285
|
/** Number of qualified company signals requested. Defaults to 100; allowed range is 1-1,000. */
|
|
256
286
|
limit?: number;
|
|
257
287
|
/** Restrict discovery to selected source families. */
|
|
@@ -300,7 +330,13 @@ interface SignalDiscoveryCostGuardrail {
|
|
|
300
330
|
maxSourceCostUsd?: number | null;
|
|
301
331
|
projectedCostPerRequestedCompanySignalUsd?: number | null;
|
|
302
332
|
sourceCostUsd?: number | null;
|
|
303
|
-
costSource?: 'observed' | 'catalog_bounded' | 'mixed' | 'unavailable';
|
|
333
|
+
costSource?: 'observed' | 'catalog_preflight' | 'catalog_bounded' | 'mixed' | 'unavailable';
|
|
334
|
+
/** Separately reported company identity-resolution cost; null when receipts are incomplete. */
|
|
335
|
+
identityResolutionCostUsd?: number | null;
|
|
336
|
+
/** Evidence used to account for company identity-resolution cost. */
|
|
337
|
+
identityCostSource?: 'observed_zero_credit_receipts' | 'contractual_zero_cost' | 'not_used' | 'unavailable';
|
|
338
|
+
/** Zero-cost fallback identity-provider requests covered by identityResolutionCostUsd. */
|
|
339
|
+
identityProviderRequests?: number;
|
|
304
340
|
qualifiedCompanySignalCount?: number;
|
|
305
341
|
returnedCompanySignalCount?: number;
|
|
306
342
|
/** Qualified signals retained on the same search run for later expansion. */
|
|
@@ -326,6 +362,10 @@ interface SignalDiscoveryResult {
|
|
|
326
362
|
availableCompanySignalCount?: number;
|
|
327
363
|
sourceLanes?: string[];
|
|
328
364
|
evidenceValidation?: Record<string, unknown>;
|
|
365
|
+
/** Search, reachability, date, classifier, and rejection counts for diagnosis. */
|
|
366
|
+
discoveryDiagnostics?: Record<string, unknown>;
|
|
367
|
+
/** Company-domain verification counts for the retained cohort. */
|
|
368
|
+
identityResolution?: Record<string, unknown>;
|
|
329
369
|
costGuardrail?: SignalDiscoveryCostGuardrail;
|
|
330
370
|
warnings?: string[];
|
|
331
371
|
raw: Record<string, unknown>;
|
|
@@ -336,6 +376,8 @@ interface SignalToCopyParams {
|
|
|
336
376
|
title?: string;
|
|
337
377
|
campaignOffer?: string;
|
|
338
378
|
researchPrompt?: string;
|
|
379
|
+
/** Maximum signal age used for cache reuse and fresh research. Defaults to 90 days. */
|
|
380
|
+
lookbackDays?: number;
|
|
339
381
|
signalRunId?: string;
|
|
340
382
|
/** Bypass cached company signals and start fresh research. */
|
|
341
383
|
skipCache?: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -27,6 +27,20 @@ interface BatchItemResult<T> {
|
|
|
27
27
|
retryEligible?: boolean;
|
|
28
28
|
/** Server-provided delay before retrying the failed row. */
|
|
29
29
|
retryAfterSeconds?: number;
|
|
30
|
+
/** Explicit recovery strategy. A fresh key can repeat provider work and is never automatic. */
|
|
31
|
+
retryStrategy?: 'new_idempotency_key';
|
|
32
|
+
/** Present only when the server proves the failed row consumed zero Signaliz credits. */
|
|
33
|
+
creditsUsed?: 0;
|
|
34
|
+
/** Present only when the server proves the failed row charged zero Signaliz credits. */
|
|
35
|
+
creditsCharged?: 0;
|
|
36
|
+
/** Existing provider run that can be resumed without dispatching new work. */
|
|
37
|
+
runId?: string;
|
|
38
|
+
/** Existing Verify Email run that can be resumed without dispatching new work. */
|
|
39
|
+
verificationRunId?: string;
|
|
40
|
+
/** Existing recoverable batch job. */
|
|
41
|
+
jobId?: string;
|
|
42
|
+
/** Machine-readable recovery action returned by Signaliz. */
|
|
43
|
+
suggestedAction?: string;
|
|
30
44
|
/** Zero-based input index containing the full result for this exact duplicate. */
|
|
31
45
|
duplicateOf?: number;
|
|
32
46
|
}
|
|
@@ -66,8 +80,7 @@ interface CoreProductDryRunResult {
|
|
|
66
80
|
sideEffects: unknown[];
|
|
67
81
|
raw: Record<string, unknown>;
|
|
68
82
|
}
|
|
69
|
-
interface
|
|
70
|
-
companyDomain: string;
|
|
83
|
+
interface FindEmailCommonParams {
|
|
71
84
|
fullName?: string;
|
|
72
85
|
firstName?: string;
|
|
73
86
|
lastName?: string;
|
|
@@ -80,6 +93,15 @@ interface FindEmailParams {
|
|
|
80
93
|
/** Stable retry key for recovering the same request after an ambiguous response. */
|
|
81
94
|
idempotencyKey?: string;
|
|
82
95
|
}
|
|
96
|
+
type FindEmailParams = FindEmailCommonParams & ({
|
|
97
|
+
companyDomain: string;
|
|
98
|
+
/** Omit for a new lookup. */
|
|
99
|
+
runId?: undefined;
|
|
100
|
+
} | {
|
|
101
|
+
/** Resume one existing Find Email run without dispatching a new finder. */
|
|
102
|
+
runId: string;
|
|
103
|
+
companyDomain?: string;
|
|
104
|
+
});
|
|
83
105
|
interface FindEmailResult {
|
|
84
106
|
success: boolean;
|
|
85
107
|
found: boolean;
|
|
@@ -112,6 +134,12 @@ interface FindEmailResult {
|
|
|
112
134
|
byoOpenrouterUsed?: boolean;
|
|
113
135
|
error?: string;
|
|
114
136
|
errorCode?: string;
|
|
137
|
+
/** Processing while an existing provider run is still active; otherwise completed. */
|
|
138
|
+
status?: 'processing' | 'completed';
|
|
139
|
+
/** Durable Find Email run ID for polling without duplicate spend. */
|
|
140
|
+
runId?: string;
|
|
141
|
+
nextPollAfterSeconds?: number;
|
|
142
|
+
suggestedAction?: string;
|
|
115
143
|
raw: Record<string, unknown>;
|
|
116
144
|
}
|
|
117
145
|
interface VerifyEmailResult {
|
|
@@ -250,8 +278,10 @@ interface CompanySignalEnrichmentResult {
|
|
|
250
278
|
type SignalDiscoverySource = 'web' | 'news';
|
|
251
279
|
type SignalDiscoveryStatus = 'planned' | 'queued' | 'processing' | 'completed' | 'completed_with_warnings' | 'failed';
|
|
252
280
|
interface SignalDiscoveryParams {
|
|
253
|
-
/**
|
|
281
|
+
/** Any natural-language signal question. Required for a new search. */
|
|
254
282
|
query?: string;
|
|
283
|
+
/** Optional natural-language ICP, product, or pain context that returned signals must support. */
|
|
284
|
+
icpContext?: string;
|
|
255
285
|
/** Number of qualified company signals requested. Defaults to 100; allowed range is 1-1,000. */
|
|
256
286
|
limit?: number;
|
|
257
287
|
/** Restrict discovery to selected source families. */
|
|
@@ -300,7 +330,13 @@ interface SignalDiscoveryCostGuardrail {
|
|
|
300
330
|
maxSourceCostUsd?: number | null;
|
|
301
331
|
projectedCostPerRequestedCompanySignalUsd?: number | null;
|
|
302
332
|
sourceCostUsd?: number | null;
|
|
303
|
-
costSource?: 'observed' | 'catalog_bounded' | 'mixed' | 'unavailable';
|
|
333
|
+
costSource?: 'observed' | 'catalog_preflight' | 'catalog_bounded' | 'mixed' | 'unavailable';
|
|
334
|
+
/** Separately reported company identity-resolution cost; null when receipts are incomplete. */
|
|
335
|
+
identityResolutionCostUsd?: number | null;
|
|
336
|
+
/** Evidence used to account for company identity-resolution cost. */
|
|
337
|
+
identityCostSource?: 'observed_zero_credit_receipts' | 'contractual_zero_cost' | 'not_used' | 'unavailable';
|
|
338
|
+
/** Zero-cost fallback identity-provider requests covered by identityResolutionCostUsd. */
|
|
339
|
+
identityProviderRequests?: number;
|
|
304
340
|
qualifiedCompanySignalCount?: number;
|
|
305
341
|
returnedCompanySignalCount?: number;
|
|
306
342
|
/** Qualified signals retained on the same search run for later expansion. */
|
|
@@ -326,6 +362,10 @@ interface SignalDiscoveryResult {
|
|
|
326
362
|
availableCompanySignalCount?: number;
|
|
327
363
|
sourceLanes?: string[];
|
|
328
364
|
evidenceValidation?: Record<string, unknown>;
|
|
365
|
+
/** Search, reachability, date, classifier, and rejection counts for diagnosis. */
|
|
366
|
+
discoveryDiagnostics?: Record<string, unknown>;
|
|
367
|
+
/** Company-domain verification counts for the retained cohort. */
|
|
368
|
+
identityResolution?: Record<string, unknown>;
|
|
329
369
|
costGuardrail?: SignalDiscoveryCostGuardrail;
|
|
330
370
|
warnings?: string[];
|
|
331
371
|
raw: Record<string, unknown>;
|
|
@@ -336,6 +376,8 @@ interface SignalToCopyParams {
|
|
|
336
376
|
title?: string;
|
|
337
377
|
campaignOffer?: string;
|
|
338
378
|
researchPrompt?: string;
|
|
379
|
+
/** Maximum signal age used for cache reuse and fresh research. Defaults to 90 days. */
|
|
380
|
+
lookbackDays?: number;
|
|
339
381
|
signalRunId?: string;
|
|
340
382
|
/** Bypass cached company signals and start fresh research. */
|
|
341
383
|
skipCache?: boolean;
|
package/dist/index.js
CHANGED
|
@@ -36,17 +36,21 @@ var SignalizError = class extends Error {
|
|
|
36
36
|
this.details = data.details;
|
|
37
37
|
}
|
|
38
38
|
get isRetryable() {
|
|
39
|
+
if (typeof this.details?.retry_eligible === "boolean") {
|
|
40
|
+
return this.details.retry_eligible;
|
|
41
|
+
}
|
|
39
42
|
return this.errorType === "rate_limited" || this.errorType === "timeout" || this.errorType === "provider_error";
|
|
40
43
|
}
|
|
41
44
|
};
|
|
42
45
|
function parseError(status, body) {
|
|
43
46
|
if (body?.error && typeof body.error === "object") {
|
|
47
|
+
const nestedDetails = body.error.details && typeof body.error.details === "object" ? body.error.details : {};
|
|
44
48
|
return new SignalizError({
|
|
45
49
|
code: body.error.code || `HTTP_${status}`,
|
|
46
50
|
message: body.error.message || `Request failed with status ${status}`,
|
|
47
51
|
errorType: mapErrorType(body.error.code, status),
|
|
48
52
|
retryAfter: retryAfterSeconds(body.error),
|
|
49
|
-
details: body.error
|
|
53
|
+
details: mergePublicErrorDetails({ ...body, ...body.error }, nestedDetails)
|
|
50
54
|
});
|
|
51
55
|
}
|
|
52
56
|
const code = typeof body?.error_code === "string" && body.error_code.trim() ? body.error_code.trim() : `HTTP_${status}`;
|
|
@@ -55,7 +59,7 @@ function parseError(status, body) {
|
|
|
55
59
|
message: body?.message || body?.error || `Request failed with status ${status}`,
|
|
56
60
|
errorType: mapErrorType(code, status),
|
|
57
61
|
retryAfter: retryAfterSeconds(body),
|
|
58
|
-
details: body?.details
|
|
62
|
+
details: mergePublicErrorDetails(body, body?.details)
|
|
59
63
|
});
|
|
60
64
|
}
|
|
61
65
|
function retryAfterSeconds(body) {
|
|
@@ -67,13 +71,26 @@ function publicRetryDetails(body) {
|
|
|
67
71
|
...body?.retry_eligible !== void 0 ? { retry_eligible: body.retry_eligible } : {},
|
|
68
72
|
...body?.retry_after !== void 0 ? { retry_after: body.retry_after } : {},
|
|
69
73
|
...body?.retry_after_seconds !== void 0 ? { retry_after_seconds: body.retry_after_seconds } : {},
|
|
74
|
+
...body?.retry_strategy !== void 0 ? { retry_strategy: body.retry_strategy } : {},
|
|
75
|
+
...body?.credits_used !== void 0 ? { credits_used: body.credits_used } : {},
|
|
76
|
+
...body?.credits_charged !== void 0 ? { credits_charged: body.credits_charged } : {},
|
|
77
|
+
...body?.job_id !== void 0 ? { job_id: body.job_id } : {},
|
|
78
|
+
...body?.verification_run_id !== void 0 ? { verification_run_id: body.verification_run_id } : {},
|
|
70
79
|
...body?.run_id !== void 0 ? { run_id: body.run_id } : {},
|
|
80
|
+
...body?.suggested_action !== void 0 ? { suggested_action: body.suggested_action } : {},
|
|
71
81
|
...body?.signal_run_id !== void 0 ? { signal_run_id: body.signal_run_id } : {},
|
|
72
82
|
...body?.company_signal_run_id !== void 0 ? { company_signal_run_id: body.company_signal_run_id } : {},
|
|
73
83
|
...body?.resume_context_persisted !== void 0 ? { resume_context_persisted: body.resume_context_persisted } : {}
|
|
74
84
|
};
|
|
75
85
|
return Object.keys(details).length > 0 ? details : void 0;
|
|
76
86
|
}
|
|
87
|
+
function mergePublicErrorDetails(body, explicitDetails) {
|
|
88
|
+
const merged = { ...publicRetryDetails(body) || {} };
|
|
89
|
+
if (explicitDetails && typeof explicitDetails === "object" && !Array.isArray(explicitDetails)) {
|
|
90
|
+
Object.assign(merged, explicitDetails);
|
|
91
|
+
}
|
|
92
|
+
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
93
|
+
}
|
|
77
94
|
function mapErrorType(code, status) {
|
|
78
95
|
if (code === "RATE_LIMITED" || status === 429) return "rate_limited";
|
|
79
96
|
if (code === "VALIDATION_ERROR" || code === "SIGNAL_RUN_COMPANY_MISMATCH" || status === 400) return "validation";
|
|
@@ -135,7 +152,9 @@ var HttpClient = class {
|
|
|
135
152
|
const errBody = await res.json().catch(() => ({}));
|
|
136
153
|
const err = parseError(res.status, errBody);
|
|
137
154
|
if (err.isRetryable && attempt < this.maxRetries) {
|
|
138
|
-
const
|
|
155
|
+
const retryWithNewKey = err.details?.retry_strategy === "new_idempotency_key" && err.details?.credits_used === 0 && err.details?.credits_charged === 0;
|
|
156
|
+
if (retryWithNewKey) throw err;
|
|
157
|
+
const wait = err.retryAfter !== void 0 ? err.retryAfter * 1e3 : Math.min(1e3 * Math.pow(2, attempt), 1e4);
|
|
139
158
|
await sleep(wait);
|
|
140
159
|
lastError = err;
|
|
141
160
|
continue;
|
|
@@ -435,6 +454,29 @@ var Signaliz = class {
|
|
|
435
454
|
);
|
|
436
455
|
}
|
|
437
456
|
async verifyEmail(email, options) {
|
|
457
|
+
const normalizedEmail = typeof email === "string" ? email.trim() : "";
|
|
458
|
+
if (options?.dryRun !== true && !options?.verificationRunId && normalizedEmail && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(normalizedEmail)) {
|
|
459
|
+
const error = `Invalid email format: "${normalizedEmail}"`;
|
|
460
|
+
return normalizeVerifyEmailResult(normalizedEmail, {
|
|
461
|
+
success: false,
|
|
462
|
+
email: normalizedEmail,
|
|
463
|
+
status: "completed",
|
|
464
|
+
is_valid: false,
|
|
465
|
+
is_deliverable: false,
|
|
466
|
+
email_verified: false,
|
|
467
|
+
verified_for_sending: false,
|
|
468
|
+
is_malformed: true,
|
|
469
|
+
verification_status: "malformed",
|
|
470
|
+
verification_verdict: "malformed",
|
|
471
|
+
verification_source: "input_validation",
|
|
472
|
+
provider_status: "rejected_before_provider",
|
|
473
|
+
error,
|
|
474
|
+
error_code: "INPUT_001",
|
|
475
|
+
retry_eligible: false,
|
|
476
|
+
credits_used: 0,
|
|
477
|
+
live_provider_called: false
|
|
478
|
+
});
|
|
479
|
+
}
|
|
438
480
|
const request = compact({
|
|
439
481
|
email: email || void 0,
|
|
440
482
|
verification_run_id: options?.verificationRunId,
|
|
@@ -769,7 +811,10 @@ var Signaliz = class {
|
|
|
769
811
|
);
|
|
770
812
|
const uniqueTasks = deduplicated.uniqueItems;
|
|
771
813
|
const recoverableBatch = path === "api/v1/find-email" ? { label: "Find Email", pageSize: 500, maxWaitMs: 20 * 6e4 } : path === "api/v1/verify-email" ? { label: "Verify Email", pageSize: 500, maxWaitMs: 20 * 6e4 } : void 0;
|
|
772
|
-
|
|
814
|
+
const containsFindRecoveryReads = path === "api/v1/find-email" && uniqueTasks.some(
|
|
815
|
+
(task) => typeof task.request.run_id === "string" && task.request.run_id.trim()
|
|
816
|
+
);
|
|
817
|
+
if (path !== "api/v1/company-signals" && recoverableBatch && uniqueTasks.length > 25 && !containsFindRecoveryReads) {
|
|
773
818
|
const rows = await this.runRecoverableCoreBatchJob(
|
|
774
819
|
path,
|
|
775
820
|
uniqueTasks.map((task) => task.request),
|
|
@@ -895,13 +940,27 @@ function batchItemFailure(index, error, raw) {
|
|
|
895
940
|
raw?.retry_after_seconds ?? raw?.retry_after ?? raw?.retryAfterSeconds ?? (Number.isFinite(Number(raw?.retry_after_ms)) ? Number(raw?.retry_after_ms) / 1e3 : void 0)
|
|
896
941
|
);
|
|
897
942
|
const retryEligible = raw?.retry_eligible ?? raw?.retryEligible;
|
|
943
|
+
const retryStrategy = raw?.retry_strategy ?? raw?.retryStrategy;
|
|
944
|
+
const creditsUsed = raw?.credits_used ?? raw?.creditsUsed;
|
|
945
|
+
const creditsCharged = raw?.credits_charged ?? raw?.creditsCharged;
|
|
946
|
+
const runId = raw?.run_id ?? raw?.runId;
|
|
947
|
+
const verificationRunId = raw?.verification_run_id ?? raw?.verificationRunId;
|
|
948
|
+
const jobId = raw?.job_id ?? raw?.jobId;
|
|
949
|
+
const suggestedAction = raw?.suggested_action ?? raw?.suggestedAction;
|
|
898
950
|
return {
|
|
899
951
|
index,
|
|
900
952
|
success: false,
|
|
901
953
|
error,
|
|
902
954
|
...errorCode ? { errorCode } : {},
|
|
903
955
|
...typeof retryEligible === "boolean" ? { retryEligible } : {},
|
|
904
|
-
...Number.isFinite(retryAfterSeconds2) && retryAfterSeconds2 >= 0 ? { retryAfterSeconds: retryAfterSeconds2 } : {}
|
|
956
|
+
...Number.isFinite(retryAfterSeconds2) && retryAfterSeconds2 >= 0 ? { retryAfterSeconds: retryAfterSeconds2 } : {},
|
|
957
|
+
...retryStrategy === "new_idempotency_key" ? { retryStrategy } : {},
|
|
958
|
+
...creditsUsed === 0 ? { creditsUsed: 0 } : {},
|
|
959
|
+
...creditsCharged === 0 ? { creditsCharged: 0 } : {},
|
|
960
|
+
...typeof runId === "string" && runId.trim() ? { runId: runId.trim() } : {},
|
|
961
|
+
...typeof verificationRunId === "string" && verificationRunId.trim() ? { verificationRunId: verificationRunId.trim() } : {},
|
|
962
|
+
...typeof jobId === "string" && jobId.trim() ? { jobId: jobId.trim() } : {},
|
|
963
|
+
...typeof suggestedAction === "string" && suggestedAction.trim() ? { suggestedAction: suggestedAction.trim() } : {}
|
|
905
964
|
};
|
|
906
965
|
}
|
|
907
966
|
function recoverableJobRowFailed(row) {
|
|
@@ -916,6 +975,7 @@ function compact(input) {
|
|
|
916
975
|
}
|
|
917
976
|
function findEmailRequestBody(params) {
|
|
918
977
|
return compact({
|
|
978
|
+
run_id: params.runId,
|
|
919
979
|
company_domain: params.companyDomain,
|
|
920
980
|
full_name: params.fullName,
|
|
921
981
|
first_name: params.firstName,
|
|
@@ -955,6 +1015,7 @@ function normalizeCoreProductDryRunResult(data) {
|
|
|
955
1015
|
};
|
|
956
1016
|
}
|
|
957
1017
|
function normalizeFindEmailResult(data) {
|
|
1018
|
+
const processing = String(data.status || "").toLowerCase() === "processing";
|
|
958
1019
|
const failed = coreProductResponseFailed(data);
|
|
959
1020
|
const rawEmail = typeof data.email === "string" && data.email.trim() ? data.email.trim() : null;
|
|
960
1021
|
const email = failed ? null : rawEmail;
|
|
@@ -967,7 +1028,7 @@ function normalizeFindEmailResult(data) {
|
|
|
967
1028
|
const verified = !explicitlyNotSendSafe && (data.is_valid === true || data.isValid === true || data.email_verified === true || data.verified_for_sending === true || ["valid", "deliverable"].includes(String(verificationStatus).toLowerCase()));
|
|
968
1029
|
const verificationAgeDays = typeof data.verification_age_days === "number" && Number.isFinite(data.verification_age_days) ? data.verification_age_days : null;
|
|
969
1030
|
return {
|
|
970
|
-
success: !failed && found,
|
|
1031
|
+
success: processing || !failed && found,
|
|
971
1032
|
found,
|
|
972
1033
|
email,
|
|
973
1034
|
firstName: data.first_name,
|
|
@@ -994,8 +1055,12 @@ function normalizeFindEmailResult(data) {
|
|
|
994
1055
|
liveProviderCalled: data.live_provider_called,
|
|
995
1056
|
openrouterCalled: data.openrouter_called,
|
|
996
1057
|
byoOpenrouterUsed: data.byo_openrouter_used,
|
|
997
|
-
error: found ? void 0 : coreProductErrorMessage(data) ?? "No verified email found",
|
|
998
|
-
errorCode: found ? void 0 : coreProductErrorCode(data) ?? "EMAIL_NOT_FOUND",
|
|
1058
|
+
error: processing || found ? void 0 : coreProductErrorMessage(data) ?? "No verified email found",
|
|
1059
|
+
errorCode: processing || found ? void 0 : coreProductErrorCode(data) ?? "EMAIL_NOT_FOUND",
|
|
1060
|
+
status: processing ? "processing" : "completed",
|
|
1061
|
+
runId: data.run_id,
|
|
1062
|
+
nextPollAfterSeconds: data.next_poll_after_seconds,
|
|
1063
|
+
suggestedAction: data.suggested_action,
|
|
999
1064
|
raw: data
|
|
1000
1065
|
};
|
|
1001
1066
|
}
|
|
@@ -1131,6 +1196,12 @@ function validateSignalDiscoveryParams(params) {
|
|
|
1131
1196
|
if (query && (query.length < 2 || query.length > 2e3)) {
|
|
1132
1197
|
throw new RangeError("query must contain between 2 and 2000 characters");
|
|
1133
1198
|
}
|
|
1199
|
+
if (params.icpContext !== void 0) {
|
|
1200
|
+
const icpContext = typeof params.icpContext === "string" ? params.icpContext.trim() : "";
|
|
1201
|
+
if (icpContext.length < 2 || icpContext.length > 4e3) {
|
|
1202
|
+
throw new RangeError("icpContext must contain between 2 and 4000 characters");
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1134
1205
|
if (params.limit !== void 0 && (!Number.isInteger(params.limit) || params.limit < 1 || params.limit > 1e3)) {
|
|
1135
1206
|
throw new RangeError("limit must be an integer between 1 and 1000");
|
|
1136
1207
|
}
|
|
@@ -1155,6 +1226,7 @@ function signalDiscoveryRequestBody(params) {
|
|
|
1155
1226
|
}
|
|
1156
1227
|
return compact({
|
|
1157
1228
|
query: params.query?.trim(),
|
|
1229
|
+
icp_context: params.icpContext?.trim(),
|
|
1158
1230
|
limit: params.limit ?? 100,
|
|
1159
1231
|
sources: params.sources,
|
|
1160
1232
|
lookback_days: params.lookbackDays,
|
|
@@ -1180,6 +1252,8 @@ function normalizeSignalDiscoveryResult(params, data) {
|
|
|
1180
1252
|
availableCompanySignalCount: Number.isFinite(Number(data.available_company_signal_count)) ? Number(data.available_company_signal_count) : void 0,
|
|
1181
1253
|
sourceLanes: Array.isArray(data.source_lanes) ? data.source_lanes.filter((lane) => typeof lane === "string") : void 0,
|
|
1182
1254
|
evidenceValidation: data.evidence_validation && typeof data.evidence_validation === "object" ? data.evidence_validation : void 0,
|
|
1255
|
+
discoveryDiagnostics: data.discovery_diagnostics && typeof data.discovery_diagnostics === "object" ? data.discovery_diagnostics : void 0,
|
|
1256
|
+
identityResolution: data.identity_resolution && typeof data.identity_resolution === "object" ? data.identity_resolution : void 0,
|
|
1183
1257
|
costGuardrail: normalizeSignalDiscoveryCostGuardrail(data.cost_guardrail),
|
|
1184
1258
|
warnings: Array.isArray(data.warnings) ? data.warnings.filter((warning) => typeof warning === "string") : void 0,
|
|
1185
1259
|
raw: data
|
|
@@ -1208,7 +1282,15 @@ function normalizeSignalDiscoveryCostGuardrail(value) {
|
|
|
1208
1282
|
guardrail.projected_cost_per_requested_company_signal_usd
|
|
1209
1283
|
),
|
|
1210
1284
|
sourceCostUsd: numberOrNullable(guardrail.source_cost_usd),
|
|
1211
|
-
costSource: ["observed", "catalog_bounded", "mixed", "unavailable"].includes(String(guardrail.cost_source)) ? String(guardrail.cost_source) : void 0,
|
|
1285
|
+
costSource: ["observed", "catalog_preflight", "catalog_bounded", "mixed", "unavailable"].includes(String(guardrail.cost_source)) ? String(guardrail.cost_source) : void 0,
|
|
1286
|
+
identityResolutionCostUsd: numberOrNullable(guardrail.identity_resolution_cost_usd),
|
|
1287
|
+
identityCostSource: [
|
|
1288
|
+
"observed_zero_credit_receipts",
|
|
1289
|
+
"contractual_zero_cost",
|
|
1290
|
+
"not_used",
|
|
1291
|
+
"unavailable"
|
|
1292
|
+
].includes(String(guardrail.identity_cost_source)) ? String(guardrail.identity_cost_source) : void 0,
|
|
1293
|
+
identityProviderRequests: numberOrUndefined(guardrail.identity_provider_requests),
|
|
1212
1294
|
qualifiedCompanySignalCount: numberOrUndefined(guardrail.qualified_company_signal_count),
|
|
1213
1295
|
returnedCompanySignalCount: numberOrUndefined(guardrail.returned_company_signal_count),
|
|
1214
1296
|
availableCompanySignalCount: numberOrUndefined(guardrail.available_company_signal_count),
|
|
@@ -1312,6 +1394,7 @@ function normalizedFindEmailBatchName(request) {
|
|
|
1312
1394
|
function coreProductBatchRequestKey(path, request) {
|
|
1313
1395
|
if (path === "api/v1/find-email") {
|
|
1314
1396
|
return JSON.stringify([
|
|
1397
|
+
normalizedBatchText(request.run_id),
|
|
1315
1398
|
normalizedFindEmailBatchName(request),
|
|
1316
1399
|
normalizedBatchDomain(request.company_domain),
|
|
1317
1400
|
normalizedBatchLinkedIn(request.linkedin_url),
|
|
@@ -1417,6 +1500,7 @@ function signalToCopyRequestBody(params) {
|
|
|
1417
1500
|
title: params.title,
|
|
1418
1501
|
campaign_offer: params.campaignOffer,
|
|
1419
1502
|
research_prompt: params.researchPrompt,
|
|
1503
|
+
lookback_days: params.lookbackDays,
|
|
1420
1504
|
signal_run_id: params.signalRunId,
|
|
1421
1505
|
skip_cache: params.skipCache,
|
|
1422
1506
|
enable_deep_search: params.enableDeepSearch,
|
|
@@ -1427,6 +1511,9 @@ function signalToCopyRequestBody(params) {
|
|
|
1427
1511
|
}
|
|
1428
1512
|
function validateSignalToCopyParams(params) {
|
|
1429
1513
|
validateCoreProductMaxWaitMs(params.maxWaitMs);
|
|
1514
|
+
if (params.lookbackDays !== void 0 && (!Number.isInteger(params.lookbackDays) || params.lookbackDays < 1 || params.lookbackDays > 365)) {
|
|
1515
|
+
throw new RangeError("lookbackDays must be an integer between 1 and 365");
|
|
1516
|
+
}
|
|
1430
1517
|
if (typeof params.signalRunId === "string" && params.signalRunId.trim()) return;
|
|
1431
1518
|
const missing = [
|
|
1432
1519
|
["companyDomain", params.companyDomain],
|
package/dist/index.mjs
CHANGED
package/dist/mcp-config.js
CHANGED
|
@@ -40,17 +40,21 @@ var SignalizError = class extends Error {
|
|
|
40
40
|
this.details = data.details;
|
|
41
41
|
}
|
|
42
42
|
get isRetryable() {
|
|
43
|
+
if (typeof this.details?.retry_eligible === "boolean") {
|
|
44
|
+
return this.details.retry_eligible;
|
|
45
|
+
}
|
|
43
46
|
return this.errorType === "rate_limited" || this.errorType === "timeout" || this.errorType === "provider_error";
|
|
44
47
|
}
|
|
45
48
|
};
|
|
46
49
|
function parseError(status, body) {
|
|
47
50
|
if (body?.error && typeof body.error === "object") {
|
|
51
|
+
const nestedDetails = body.error.details && typeof body.error.details === "object" ? body.error.details : {};
|
|
48
52
|
return new SignalizError({
|
|
49
53
|
code: body.error.code || `HTTP_${status}`,
|
|
50
54
|
message: body.error.message || `Request failed with status ${status}`,
|
|
51
55
|
errorType: mapErrorType(body.error.code, status),
|
|
52
56
|
retryAfter: retryAfterSeconds(body.error),
|
|
53
|
-
details: body.error
|
|
57
|
+
details: mergePublicErrorDetails({ ...body, ...body.error }, nestedDetails)
|
|
54
58
|
});
|
|
55
59
|
}
|
|
56
60
|
const code = typeof body?.error_code === "string" && body.error_code.trim() ? body.error_code.trim() : `HTTP_${status}`;
|
|
@@ -59,7 +63,7 @@ function parseError(status, body) {
|
|
|
59
63
|
message: body?.message || body?.error || `Request failed with status ${status}`,
|
|
60
64
|
errorType: mapErrorType(code, status),
|
|
61
65
|
retryAfter: retryAfterSeconds(body),
|
|
62
|
-
details: body?.details
|
|
66
|
+
details: mergePublicErrorDetails(body, body?.details)
|
|
63
67
|
});
|
|
64
68
|
}
|
|
65
69
|
function retryAfterSeconds(body) {
|
|
@@ -71,13 +75,26 @@ function publicRetryDetails(body) {
|
|
|
71
75
|
...body?.retry_eligible !== void 0 ? { retry_eligible: body.retry_eligible } : {},
|
|
72
76
|
...body?.retry_after !== void 0 ? { retry_after: body.retry_after } : {},
|
|
73
77
|
...body?.retry_after_seconds !== void 0 ? { retry_after_seconds: body.retry_after_seconds } : {},
|
|
78
|
+
...body?.retry_strategy !== void 0 ? { retry_strategy: body.retry_strategy } : {},
|
|
79
|
+
...body?.credits_used !== void 0 ? { credits_used: body.credits_used } : {},
|
|
80
|
+
...body?.credits_charged !== void 0 ? { credits_charged: body.credits_charged } : {},
|
|
81
|
+
...body?.job_id !== void 0 ? { job_id: body.job_id } : {},
|
|
82
|
+
...body?.verification_run_id !== void 0 ? { verification_run_id: body.verification_run_id } : {},
|
|
74
83
|
...body?.run_id !== void 0 ? { run_id: body.run_id } : {},
|
|
84
|
+
...body?.suggested_action !== void 0 ? { suggested_action: body.suggested_action } : {},
|
|
75
85
|
...body?.signal_run_id !== void 0 ? { signal_run_id: body.signal_run_id } : {},
|
|
76
86
|
...body?.company_signal_run_id !== void 0 ? { company_signal_run_id: body.company_signal_run_id } : {},
|
|
77
87
|
...body?.resume_context_persisted !== void 0 ? { resume_context_persisted: body.resume_context_persisted } : {}
|
|
78
88
|
};
|
|
79
89
|
return Object.keys(details).length > 0 ? details : void 0;
|
|
80
90
|
}
|
|
91
|
+
function mergePublicErrorDetails(body, explicitDetails) {
|
|
92
|
+
const merged = { ...publicRetryDetails(body) || {} };
|
|
93
|
+
if (explicitDetails && typeof explicitDetails === "object" && !Array.isArray(explicitDetails)) {
|
|
94
|
+
Object.assign(merged, explicitDetails);
|
|
95
|
+
}
|
|
96
|
+
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
97
|
+
}
|
|
81
98
|
function mapErrorType(code, status) {
|
|
82
99
|
if (code === "RATE_LIMITED" || status === 429) return "rate_limited";
|
|
83
100
|
if (code === "VALIDATION_ERROR" || code === "SIGNAL_RUN_COMPANY_MISMATCH" || status === 400) return "validation";
|
|
@@ -139,7 +156,9 @@ var HttpClient = class {
|
|
|
139
156
|
const errBody = await res.json().catch(() => ({}));
|
|
140
157
|
const err = parseError(res.status, errBody);
|
|
141
158
|
if (err.isRetryable && attempt < this.maxRetries) {
|
|
142
|
-
const
|
|
159
|
+
const retryWithNewKey = err.details?.retry_strategy === "new_idempotency_key" && err.details?.credits_used === 0 && err.details?.credits_charged === 0;
|
|
160
|
+
if (retryWithNewKey) throw err;
|
|
161
|
+
const wait = err.retryAfter !== void 0 ? err.retryAfter * 1e3 : Math.min(1e3 * Math.pow(2, attempt), 1e4);
|
|
143
162
|
await sleep(wait);
|
|
144
163
|
lastError = err;
|
|
145
164
|
continue;
|
|
@@ -439,6 +458,29 @@ var Signaliz = class {
|
|
|
439
458
|
);
|
|
440
459
|
}
|
|
441
460
|
async verifyEmail(email, options) {
|
|
461
|
+
const normalizedEmail = typeof email === "string" ? email.trim() : "";
|
|
462
|
+
if (options?.dryRun !== true && !options?.verificationRunId && normalizedEmail && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(normalizedEmail)) {
|
|
463
|
+
const error = `Invalid email format: "${normalizedEmail}"`;
|
|
464
|
+
return normalizeVerifyEmailResult(normalizedEmail, {
|
|
465
|
+
success: false,
|
|
466
|
+
email: normalizedEmail,
|
|
467
|
+
status: "completed",
|
|
468
|
+
is_valid: false,
|
|
469
|
+
is_deliverable: false,
|
|
470
|
+
email_verified: false,
|
|
471
|
+
verified_for_sending: false,
|
|
472
|
+
is_malformed: true,
|
|
473
|
+
verification_status: "malformed",
|
|
474
|
+
verification_verdict: "malformed",
|
|
475
|
+
verification_source: "input_validation",
|
|
476
|
+
provider_status: "rejected_before_provider",
|
|
477
|
+
error,
|
|
478
|
+
error_code: "INPUT_001",
|
|
479
|
+
retry_eligible: false,
|
|
480
|
+
credits_used: 0,
|
|
481
|
+
live_provider_called: false
|
|
482
|
+
});
|
|
483
|
+
}
|
|
442
484
|
const request = compact({
|
|
443
485
|
email: email || void 0,
|
|
444
486
|
verification_run_id: options?.verificationRunId,
|
|
@@ -773,7 +815,10 @@ var Signaliz = class {
|
|
|
773
815
|
);
|
|
774
816
|
const uniqueTasks = deduplicated.uniqueItems;
|
|
775
817
|
const recoverableBatch = path2 === "api/v1/find-email" ? { label: "Find Email", pageSize: 500, maxWaitMs: 20 * 6e4 } : path2 === "api/v1/verify-email" ? { label: "Verify Email", pageSize: 500, maxWaitMs: 20 * 6e4 } : void 0;
|
|
776
|
-
|
|
818
|
+
const containsFindRecoveryReads = path2 === "api/v1/find-email" && uniqueTasks.some(
|
|
819
|
+
(task) => typeof task.request.run_id === "string" && task.request.run_id.trim()
|
|
820
|
+
);
|
|
821
|
+
if (path2 !== "api/v1/company-signals" && recoverableBatch && uniqueTasks.length > 25 && !containsFindRecoveryReads) {
|
|
777
822
|
const rows = await this.runRecoverableCoreBatchJob(
|
|
778
823
|
path2,
|
|
779
824
|
uniqueTasks.map((task) => task.request),
|
|
@@ -899,13 +944,27 @@ function batchItemFailure(index, error, raw) {
|
|
|
899
944
|
raw?.retry_after_seconds ?? raw?.retry_after ?? raw?.retryAfterSeconds ?? (Number.isFinite(Number(raw?.retry_after_ms)) ? Number(raw?.retry_after_ms) / 1e3 : void 0)
|
|
900
945
|
);
|
|
901
946
|
const retryEligible = raw?.retry_eligible ?? raw?.retryEligible;
|
|
947
|
+
const retryStrategy = raw?.retry_strategy ?? raw?.retryStrategy;
|
|
948
|
+
const creditsUsed = raw?.credits_used ?? raw?.creditsUsed;
|
|
949
|
+
const creditsCharged = raw?.credits_charged ?? raw?.creditsCharged;
|
|
950
|
+
const runId = raw?.run_id ?? raw?.runId;
|
|
951
|
+
const verificationRunId = raw?.verification_run_id ?? raw?.verificationRunId;
|
|
952
|
+
const jobId = raw?.job_id ?? raw?.jobId;
|
|
953
|
+
const suggestedAction = raw?.suggested_action ?? raw?.suggestedAction;
|
|
902
954
|
return {
|
|
903
955
|
index,
|
|
904
956
|
success: false,
|
|
905
957
|
error,
|
|
906
958
|
...errorCode ? { errorCode } : {},
|
|
907
959
|
...typeof retryEligible === "boolean" ? { retryEligible } : {},
|
|
908
|
-
...Number.isFinite(retryAfterSeconds2) && retryAfterSeconds2 >= 0 ? { retryAfterSeconds: retryAfterSeconds2 } : {}
|
|
960
|
+
...Number.isFinite(retryAfterSeconds2) && retryAfterSeconds2 >= 0 ? { retryAfterSeconds: retryAfterSeconds2 } : {},
|
|
961
|
+
...retryStrategy === "new_idempotency_key" ? { retryStrategy } : {},
|
|
962
|
+
...creditsUsed === 0 ? { creditsUsed: 0 } : {},
|
|
963
|
+
...creditsCharged === 0 ? { creditsCharged: 0 } : {},
|
|
964
|
+
...typeof runId === "string" && runId.trim() ? { runId: runId.trim() } : {},
|
|
965
|
+
...typeof verificationRunId === "string" && verificationRunId.trim() ? { verificationRunId: verificationRunId.trim() } : {},
|
|
966
|
+
...typeof jobId === "string" && jobId.trim() ? { jobId: jobId.trim() } : {},
|
|
967
|
+
...typeof suggestedAction === "string" && suggestedAction.trim() ? { suggestedAction: suggestedAction.trim() } : {}
|
|
909
968
|
};
|
|
910
969
|
}
|
|
911
970
|
function recoverableJobRowFailed(row) {
|
|
@@ -920,6 +979,7 @@ function compact(input) {
|
|
|
920
979
|
}
|
|
921
980
|
function findEmailRequestBody(params) {
|
|
922
981
|
return compact({
|
|
982
|
+
run_id: params.runId,
|
|
923
983
|
company_domain: params.companyDomain,
|
|
924
984
|
full_name: params.fullName,
|
|
925
985
|
first_name: params.firstName,
|
|
@@ -959,6 +1019,7 @@ function normalizeCoreProductDryRunResult(data) {
|
|
|
959
1019
|
};
|
|
960
1020
|
}
|
|
961
1021
|
function normalizeFindEmailResult(data) {
|
|
1022
|
+
const processing = String(data.status || "").toLowerCase() === "processing";
|
|
962
1023
|
const failed = coreProductResponseFailed(data);
|
|
963
1024
|
const rawEmail = typeof data.email === "string" && data.email.trim() ? data.email.trim() : null;
|
|
964
1025
|
const email = failed ? null : rawEmail;
|
|
@@ -971,7 +1032,7 @@ function normalizeFindEmailResult(data) {
|
|
|
971
1032
|
const verified = !explicitlyNotSendSafe && (data.is_valid === true || data.isValid === true || data.email_verified === true || data.verified_for_sending === true || ["valid", "deliverable"].includes(String(verificationStatus).toLowerCase()));
|
|
972
1033
|
const verificationAgeDays = typeof data.verification_age_days === "number" && Number.isFinite(data.verification_age_days) ? data.verification_age_days : null;
|
|
973
1034
|
return {
|
|
974
|
-
success: !failed && found,
|
|
1035
|
+
success: processing || !failed && found,
|
|
975
1036
|
found,
|
|
976
1037
|
email,
|
|
977
1038
|
firstName: data.first_name,
|
|
@@ -998,8 +1059,12 @@ function normalizeFindEmailResult(data) {
|
|
|
998
1059
|
liveProviderCalled: data.live_provider_called,
|
|
999
1060
|
openrouterCalled: data.openrouter_called,
|
|
1000
1061
|
byoOpenrouterUsed: data.byo_openrouter_used,
|
|
1001
|
-
error: found ? void 0 : coreProductErrorMessage(data) ?? "No verified email found",
|
|
1002
|
-
errorCode: found ? void 0 : coreProductErrorCode(data) ?? "EMAIL_NOT_FOUND",
|
|
1062
|
+
error: processing || found ? void 0 : coreProductErrorMessage(data) ?? "No verified email found",
|
|
1063
|
+
errorCode: processing || found ? void 0 : coreProductErrorCode(data) ?? "EMAIL_NOT_FOUND",
|
|
1064
|
+
status: processing ? "processing" : "completed",
|
|
1065
|
+
runId: data.run_id,
|
|
1066
|
+
nextPollAfterSeconds: data.next_poll_after_seconds,
|
|
1067
|
+
suggestedAction: data.suggested_action,
|
|
1003
1068
|
raw: data
|
|
1004
1069
|
};
|
|
1005
1070
|
}
|
|
@@ -1135,6 +1200,12 @@ function validateSignalDiscoveryParams(params) {
|
|
|
1135
1200
|
if (query && (query.length < 2 || query.length > 2e3)) {
|
|
1136
1201
|
throw new RangeError("query must contain between 2 and 2000 characters");
|
|
1137
1202
|
}
|
|
1203
|
+
if (params.icpContext !== void 0) {
|
|
1204
|
+
const icpContext = typeof params.icpContext === "string" ? params.icpContext.trim() : "";
|
|
1205
|
+
if (icpContext.length < 2 || icpContext.length > 4e3) {
|
|
1206
|
+
throw new RangeError("icpContext must contain between 2 and 4000 characters");
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1138
1209
|
if (params.limit !== void 0 && (!Number.isInteger(params.limit) || params.limit < 1 || params.limit > 1e3)) {
|
|
1139
1210
|
throw new RangeError("limit must be an integer between 1 and 1000");
|
|
1140
1211
|
}
|
|
@@ -1159,6 +1230,7 @@ function signalDiscoveryRequestBody(params) {
|
|
|
1159
1230
|
}
|
|
1160
1231
|
return compact({
|
|
1161
1232
|
query: params.query?.trim(),
|
|
1233
|
+
icp_context: params.icpContext?.trim(),
|
|
1162
1234
|
limit: params.limit ?? 100,
|
|
1163
1235
|
sources: params.sources,
|
|
1164
1236
|
lookback_days: params.lookbackDays,
|
|
@@ -1184,6 +1256,8 @@ function normalizeSignalDiscoveryResult(params, data) {
|
|
|
1184
1256
|
availableCompanySignalCount: Number.isFinite(Number(data.available_company_signal_count)) ? Number(data.available_company_signal_count) : void 0,
|
|
1185
1257
|
sourceLanes: Array.isArray(data.source_lanes) ? data.source_lanes.filter((lane) => typeof lane === "string") : void 0,
|
|
1186
1258
|
evidenceValidation: data.evidence_validation && typeof data.evidence_validation === "object" ? data.evidence_validation : void 0,
|
|
1259
|
+
discoveryDiagnostics: data.discovery_diagnostics && typeof data.discovery_diagnostics === "object" ? data.discovery_diagnostics : void 0,
|
|
1260
|
+
identityResolution: data.identity_resolution && typeof data.identity_resolution === "object" ? data.identity_resolution : void 0,
|
|
1187
1261
|
costGuardrail: normalizeSignalDiscoveryCostGuardrail(data.cost_guardrail),
|
|
1188
1262
|
warnings: Array.isArray(data.warnings) ? data.warnings.filter((warning) => typeof warning === "string") : void 0,
|
|
1189
1263
|
raw: data
|
|
@@ -1212,7 +1286,15 @@ function normalizeSignalDiscoveryCostGuardrail(value) {
|
|
|
1212
1286
|
guardrail.projected_cost_per_requested_company_signal_usd
|
|
1213
1287
|
),
|
|
1214
1288
|
sourceCostUsd: numberOrNullable(guardrail.source_cost_usd),
|
|
1215
|
-
costSource: ["observed", "catalog_bounded", "mixed", "unavailable"].includes(String(guardrail.cost_source)) ? String(guardrail.cost_source) : void 0,
|
|
1289
|
+
costSource: ["observed", "catalog_preflight", "catalog_bounded", "mixed", "unavailable"].includes(String(guardrail.cost_source)) ? String(guardrail.cost_source) : void 0,
|
|
1290
|
+
identityResolutionCostUsd: numberOrNullable(guardrail.identity_resolution_cost_usd),
|
|
1291
|
+
identityCostSource: [
|
|
1292
|
+
"observed_zero_credit_receipts",
|
|
1293
|
+
"contractual_zero_cost",
|
|
1294
|
+
"not_used",
|
|
1295
|
+
"unavailable"
|
|
1296
|
+
].includes(String(guardrail.identity_cost_source)) ? String(guardrail.identity_cost_source) : void 0,
|
|
1297
|
+
identityProviderRequests: numberOrUndefined(guardrail.identity_provider_requests),
|
|
1216
1298
|
qualifiedCompanySignalCount: numberOrUndefined(guardrail.qualified_company_signal_count),
|
|
1217
1299
|
returnedCompanySignalCount: numberOrUndefined(guardrail.returned_company_signal_count),
|
|
1218
1300
|
availableCompanySignalCount: numberOrUndefined(guardrail.available_company_signal_count),
|
|
@@ -1316,6 +1398,7 @@ function normalizedFindEmailBatchName(request) {
|
|
|
1316
1398
|
function coreProductBatchRequestKey(path2, request) {
|
|
1317
1399
|
if (path2 === "api/v1/find-email") {
|
|
1318
1400
|
return JSON.stringify([
|
|
1401
|
+
normalizedBatchText(request.run_id),
|
|
1319
1402
|
normalizedFindEmailBatchName(request),
|
|
1320
1403
|
normalizedBatchDomain(request.company_domain),
|
|
1321
1404
|
normalizedBatchLinkedIn(request.linkedin_url),
|
|
@@ -1421,6 +1504,7 @@ function signalToCopyRequestBody(params) {
|
|
|
1421
1504
|
title: params.title,
|
|
1422
1505
|
campaign_offer: params.campaignOffer,
|
|
1423
1506
|
research_prompt: params.researchPrompt,
|
|
1507
|
+
lookback_days: params.lookbackDays,
|
|
1424
1508
|
signal_run_id: params.signalRunId,
|
|
1425
1509
|
skip_cache: params.skipCache,
|
|
1426
1510
|
enable_deep_search: params.enableDeepSearch,
|
|
@@ -1431,6 +1515,9 @@ function signalToCopyRequestBody(params) {
|
|
|
1431
1515
|
}
|
|
1432
1516
|
function validateSignalToCopyParams(params) {
|
|
1433
1517
|
validateCoreProductMaxWaitMs(params.maxWaitMs);
|
|
1518
|
+
if (params.lookbackDays !== void 0 && (!Number.isInteger(params.lookbackDays) || params.lookbackDays < 1 || params.lookbackDays > 365)) {
|
|
1519
|
+
throw new RangeError("lookbackDays must be an integer between 1 and 365");
|
|
1520
|
+
}
|
|
1434
1521
|
if (typeof params.signalRunId === "string" && params.signalRunId.trim()) return;
|
|
1435
1522
|
const missing = [
|
|
1436
1523
|
["companyDomain", params.companyDomain],
|
package/dist/mcp-config.mjs
CHANGED
package/package.json
CHANGED