@entros/pulse-sdk 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/index.d.mts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +26 -12
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +26 -12
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -63,6 +63,12 @@ npm run build # ESM + CJS output
|
|
|
63
63
|
npm run typecheck # TypeScript strict mode
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
+
## Migration history
|
|
67
|
+
|
|
68
|
+
Originally published as `@iam-protocol/pulse-sdk` (deprecated). Renamed during
|
|
69
|
+
the IAM → Entros Protocol rebrand on 2026-04-25; full commit history preserved
|
|
70
|
+
on the current repository at `github.com/entros-protocol/pulse-sdk`.
|
|
71
|
+
|
|
66
72
|
## License
|
|
67
73
|
|
|
68
74
|
MIT
|
package/dist/index.d.mts
CHANGED
|
@@ -92,6 +92,19 @@ interface VerificationResult {
|
|
|
92
92
|
attestationTx?: string;
|
|
93
93
|
isFirstVerification: boolean;
|
|
94
94
|
error?: string;
|
|
95
|
+
/**
|
|
96
|
+
* Safe-to-reveal validator reason label when validation rejected — one of
|
|
97
|
+
* `variance_floor`, `entropy_bounds`, `temporal_coupling_low`,
|
|
98
|
+
* `phrase_content_mismatch`. Surfaced for the soft-reject + retry UX
|
|
99
|
+
* (master-list #94) so the UI can show a per-category hint.
|
|
100
|
+
*
|
|
101
|
+
* Absent on every other failure path (data-quality, on-chain submission,
|
|
102
|
+
* baseline missing, etc.) and on attack-signal rejections (TTS detection,
|
|
103
|
+
* Sybil match) and capture-shape bugs — the validator deliberately keeps
|
|
104
|
+
* those opaque to prevent adversarial probing. UI must not assume reason
|
|
105
|
+
* is present even when `success === false`.
|
|
106
|
+
*/
|
|
107
|
+
reason?: string;
|
|
95
108
|
}
|
|
96
109
|
|
|
97
110
|
type ResolvedConfig = Required<Pick<PulseConfig, "cluster" | "threshold">> & PulseConfig;
|
package/dist/index.d.ts
CHANGED
|
@@ -92,6 +92,19 @@ interface VerificationResult {
|
|
|
92
92
|
attestationTx?: string;
|
|
93
93
|
isFirstVerification: boolean;
|
|
94
94
|
error?: string;
|
|
95
|
+
/**
|
|
96
|
+
* Safe-to-reveal validator reason label when validation rejected — one of
|
|
97
|
+
* `variance_floor`, `entropy_bounds`, `temporal_coupling_low`,
|
|
98
|
+
* `phrase_content_mismatch`. Surfaced for the soft-reject + retry UX
|
|
99
|
+
* (master-list #94) so the UI can show a per-category hint.
|
|
100
|
+
*
|
|
101
|
+
* Absent on every other failure path (data-quality, on-chain submission,
|
|
102
|
+
* baseline missing, etc.) and on attack-signal rejections (TTS detection,
|
|
103
|
+
* Sybil match) and capture-shape bugs — the validator deliberately keeps
|
|
104
|
+
* those opaque to prevent adversarial probing. UI must not assume reason
|
|
105
|
+
* is present even when `success === false`.
|
|
106
|
+
*/
|
|
107
|
+
reason?: string;
|
|
95
108
|
}
|
|
96
109
|
|
|
97
110
|
type ResolvedConfig = Required<Pick<PulseConfig, "cluster" | "threshold">> & PulseConfig;
|
package/dist/index.js
CHANGED
|
@@ -568,7 +568,7 @@ function extractFormantRatios(samples, sampleRate, frameSize, hopSize) {
|
|
|
568
568
|
const numFrames = Math.floor((samples.length - frameSize) / hopSize) + 1;
|
|
569
569
|
for (let i = 0; i < numFrames; i++) {
|
|
570
570
|
const start = i * hopSize;
|
|
571
|
-
const frame = samples.
|
|
571
|
+
const frame = samples.subarray(start, start + frameSize);
|
|
572
572
|
const windowed = new Float32Array(frameSize);
|
|
573
573
|
for (let j = 0; j < frameSize; j++) {
|
|
574
574
|
windowed[j] = (frame[j] ?? 0) * (0.54 - 0.46 * Math.cos(2 * Math.PI * j / (frameSize - 1)));
|
|
@@ -629,7 +629,7 @@ async function detectF0Contour(samples, sampleRate) {
|
|
|
629
629
|
}
|
|
630
630
|
for (let i = 0; i < numFrames; i++) {
|
|
631
631
|
const start = i * hopSize;
|
|
632
|
-
const frame = samples.
|
|
632
|
+
const frame = samples.subarray(start, start + frameSize);
|
|
633
633
|
const pitch = detect(frame);
|
|
634
634
|
if (pitch && pitch > 50 && pitch < 600) {
|
|
635
635
|
f0.push(pitch);
|
|
@@ -720,7 +720,7 @@ function computeHNR(samples, sampleRate, f0Contour) {
|
|
|
720
720
|
const f0 = f0Contour[i];
|
|
721
721
|
if (f0 <= 0) continue;
|
|
722
722
|
const start = i * hopSize;
|
|
723
|
-
const frame = samples.
|
|
723
|
+
const frame = samples.subarray(start, start + frameSize);
|
|
724
724
|
const period = Math.round(sampleRate / f0);
|
|
725
725
|
if (period <= 0 || period >= frame.length) continue;
|
|
726
726
|
let num = 0;
|
|
@@ -747,11 +747,10 @@ async function computeLTAS(samples, sampleRate) {
|
|
|
747
747
|
const flatnesses = [];
|
|
748
748
|
const spreads = [];
|
|
749
749
|
const numFrames = Math.floor((samples.length - frameSize) / hopSize) + 1;
|
|
750
|
+
const paddedFrame = new Float32Array(frameSize);
|
|
750
751
|
for (let i = 0; i < numFrames; i++) {
|
|
751
752
|
const start = i * hopSize;
|
|
752
|
-
|
|
753
|
-
const paddedFrame = new Float32Array(frameSize);
|
|
754
|
-
paddedFrame.set(frame);
|
|
753
|
+
paddedFrame.set(samples.subarray(start, start + frameSize), 0);
|
|
755
754
|
const features = Meyda.extract(
|
|
756
755
|
["spectralCentroid", "spectralRolloff", "spectralFlatness", "spectralSpread"],
|
|
757
756
|
paddedFrame,
|
|
@@ -806,7 +805,15 @@ async function extractSpeakerFeaturesDetailed(audio) {
|
|
|
806
805
|
const abs = Math.abs(samples[i] ?? 0);
|
|
807
806
|
if (abs > peakAmp) peakAmp = abs;
|
|
808
807
|
}
|
|
809
|
-
|
|
808
|
+
let normalizedSamples;
|
|
809
|
+
if (peakAmp > 1e-6) {
|
|
810
|
+
normalizedSamples = new Float32Array(samples.length);
|
|
811
|
+
for (let i = 0; i < samples.length; i++) {
|
|
812
|
+
normalizedSamples[i] = samples[i] / peakAmp * 0.9;
|
|
813
|
+
}
|
|
814
|
+
} else {
|
|
815
|
+
normalizedSamples = samples;
|
|
816
|
+
}
|
|
810
817
|
const { f0, amplitudes: normalizedAmplitudes, periods } = await detectF0Contour(normalizedSamples, sampleRate);
|
|
811
818
|
const amplitudes = [];
|
|
812
819
|
for (let i = 0; i < numFrames; i++) {
|
|
@@ -1978,10 +1985,15 @@ async function extractFingerprintAndValidate(sensorData, config, walletAddress,
|
|
|
1978
1985
|
clearTimeout(validateTimer);
|
|
1979
1986
|
if (!validateResponse.ok) {
|
|
1980
1987
|
const errorBody = await validateResponse.json().catch(() => ({}));
|
|
1981
|
-
|
|
1988
|
+
const body = errorBody;
|
|
1989
|
+
const reason = typeof body.reason === "string" ? body.reason : void 0;
|
|
1990
|
+
sdkWarn(
|
|
1991
|
+
`[Entros SDK] Feature validation rejected by server${reason ? ` (reason: ${reason})` : ""}`
|
|
1992
|
+
);
|
|
1982
1993
|
return {
|
|
1983
1994
|
ok: false,
|
|
1984
|
-
error:
|
|
1995
|
+
error: body.error || "Feature validation failed",
|
|
1996
|
+
reason
|
|
1985
1997
|
};
|
|
1986
1998
|
}
|
|
1987
1999
|
} catch (err) {
|
|
@@ -2058,7 +2070,8 @@ async function processSensorData(sensorData, config, wallet, connection, onProgr
|
|
|
2058
2070
|
success: false,
|
|
2059
2071
|
commitment: new Uint8Array(32),
|
|
2060
2072
|
isFirstVerification: false,
|
|
2061
|
-
error: extraction.error
|
|
2073
|
+
error: extraction.error,
|
|
2074
|
+
reason: extraction.reason
|
|
2062
2075
|
};
|
|
2063
2076
|
}
|
|
2064
2077
|
const { fingerprint, tbh, features } = extraction;
|
|
@@ -2236,7 +2249,8 @@ async function processResetSensorData(sensorData, config, wallet, connection, on
|
|
|
2236
2249
|
success: false,
|
|
2237
2250
|
commitment: new Uint8Array(32),
|
|
2238
2251
|
isFirstVerification: true,
|
|
2239
|
-
error: extraction.error
|
|
2252
|
+
error: extraction.error,
|
|
2253
|
+
reason: extraction.reason
|
|
2240
2254
|
};
|
|
2241
2255
|
}
|
|
2242
2256
|
const { tbh } = extraction;
|
|
@@ -2512,7 +2526,7 @@ var PulseSession = class {
|
|
|
2512
2526
|
walletAddress
|
|
2513
2527
|
);
|
|
2514
2528
|
if (!extraction.ok) {
|
|
2515
|
-
return { validated: false, error: extraction.error };
|
|
2529
|
+
return { validated: false, error: extraction.error, reason: extraction.reason };
|
|
2516
2530
|
}
|
|
2517
2531
|
return { validated: true };
|
|
2518
2532
|
}
|