@iam-protocol/pulse-sdk 0.1.1 → 0.2.1

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/dist/index.d.mts CHANGED
@@ -1,5 +1,6 @@
1
1
  declare const FINGERPRINT_BITS = 256;
2
2
  declare const DEFAULT_THRESHOLD = 30;
3
+ declare const DEFAULT_MIN_DISTANCE = 3;
3
4
  declare const MIN_CAPTURE_MS = 2000;
4
5
  declare const MAX_CAPTURE_MS = 60000;
5
6
  declare const DEFAULT_CAPTURE_MS = 7000;
@@ -50,8 +51,6 @@ interface CaptureOptions {
50
51
  minDurationMs?: number;
51
52
  /** Maximum capture duration in ms. Auto-stops if signal hasn't fired. Default: 60000 */
52
53
  maxDurationMs?: number;
53
- /** Called with RMS audio level (0-1) on each buffer during audio capture (~4x per second). */
54
- onAudioLevel?: (rms: number) => void;
55
54
  }
56
55
  /** Stage of a capture session */
57
56
  type CaptureStage = "audio" | "motion" | "touch";
@@ -120,7 +119,7 @@ declare class PulseSession {
120
119
  private motionData;
121
120
  private touchData;
122
121
  constructor(config: ResolvedConfig, touchElement?: HTMLElement);
123
- startAudio(onAudioLevel?: (rms: number) => void): Promise<void>;
122
+ startAudio(): Promise<void>;
124
123
  stopAudio(): Promise<AudioCapture | null>;
125
124
  skipAudio(): void;
126
125
  startMotion(): Promise<void>;
@@ -224,6 +223,18 @@ declare function variance(values: number[], mu?: number): number;
224
223
  declare function skewness(values: number[]): number;
225
224
  declare function kurtosis(values: number[]): number;
226
225
  declare function condense(values: number[]): StatsSummary;
226
+ /**
227
+ * Shannon entropy over histogram bins. Measures information density.
228
+ * Real human data has moderate entropy (varied but structured).
229
+ * Synthetic data is either too uniform (high entropy) or too structured (low entropy).
230
+ */
231
+ declare function entropy(values: number[], bins?: number): number;
232
+ /**
233
+ * Autocorrelation at a given lag. Detects periodic synthetic patterns.
234
+ * Real human data has low autocorrelation at most lags (chaotic/noisy).
235
+ * Synthetic data often has high autocorrelation (periodic/smooth).
236
+ */
237
+ declare function autocorrelation(values: number[], lag?: number): number;
227
238
  declare function fuseFeatures(audio: number[], motion: number[], touch: number[]): number[];
228
239
 
229
240
  /** Serialized proof ready for on-chain submission */
@@ -248,6 +259,7 @@ interface CircuitInput {
248
259
  commitment_new: string;
249
260
  commitment_prev: string;
250
261
  threshold: string;
262
+ min_distance: string;
251
263
  }
252
264
  /** Proof generation result */
253
265
  interface ProofResult {
@@ -271,7 +283,7 @@ declare function serializeProof(proof: RawProof, publicSignals: string[]): Solan
271
283
  /**
272
284
  * Prepare circuit input from current and previous TBH data.
273
285
  */
274
- declare function prepareCircuitInput(current: TBH, previous: TBH, threshold?: number): CircuitInput;
286
+ declare function prepareCircuitInput(current: TBH, previous: TBH, threshold?: number, minDistance?: number): CircuitInput;
275
287
  /**
276
288
  * Generate a Groth16 proof for the Hamming distance circuit.
277
289
  *
@@ -348,6 +360,11 @@ declare function loadVerificationData(): StoredVerificationData | null;
348
360
  * Each phrase is 5-6 syllable pairs, forming nonsensical but speakable words.
349
361
  */
350
362
  declare function generatePhrase(wordCount?: number): string;
363
+ /**
364
+ * Generate a sequence of phrases for dynamic mid-session switching.
365
+ * Each phrase uses a different syllable subset to prevent pre-computation.
366
+ */
367
+ declare function generatePhraseSequence(count?: number, wordCount?: number): string[];
351
368
 
352
369
  /**
353
370
  * Generate Lissajous curve points for the touch tracing challenge.
@@ -374,5 +391,13 @@ declare function randomLissajousParams(): LissajousParams;
374
391
  * Generate Lissajous curve points normalized to [0, 1] range.
375
392
  */
376
393
  declare function generateLissajousPoints(params: LissajousParams): Point2D[];
394
+ /**
395
+ * Generate a sequence of Lissajous curves for dynamic mid-session switching.
396
+ * Each curve uses different parameters, preventing pre-computation.
397
+ */
398
+ declare function generateLissajousSequence(count?: number): {
399
+ params: LissajousParams;
400
+ points: Point2D[];
401
+ }[];
377
402
 
378
- export { type AudioCapture, type CaptureOptions, type CaptureStage, type CircuitInput, DEFAULT_CAPTURE_MS, DEFAULT_THRESHOLD, FINGERPRINT_BITS, type FeatureVector, type FusedFeatureVector, type IdentityState, type LissajousParams, MAX_CAPTURE_MS, MIN_CAPTURE_MS, type MotionSample, PROGRAM_IDS, type PackedFingerprint, type Point2D, type ProofResult, type PulseConfig, PulseSDK, PulseSession, type SensorData, type SolanaProof, type StageState, type StatsSummary, type StoredVerificationData, type SubmissionResult, type TBH, type TemporalFingerprint, type TouchSample, type VerificationResult, bigintToBytes32, computeCommitment, condense, fetchIdentityState, fuseFeatures, generateLissajousPoints, generatePhrase, generateProof, generateSalt, generateSolanaProof, generateTBH, hammingDistance, kurtosis, loadVerificationData, mean, packBits, prepareCircuitInput, randomLissajousParams, serializeProof, simhash, skewness, storeVerificationData, submitViaRelayer, submitViaWallet, toBigEndian32, variance };
403
+ export { type AudioCapture, type CaptureOptions, type CaptureStage, type CircuitInput, DEFAULT_CAPTURE_MS, DEFAULT_MIN_DISTANCE, DEFAULT_THRESHOLD, FINGERPRINT_BITS, type FeatureVector, type FusedFeatureVector, type IdentityState, type LissajousParams, MAX_CAPTURE_MS, MIN_CAPTURE_MS, type MotionSample, PROGRAM_IDS, type PackedFingerprint, type Point2D, type ProofResult, type PulseConfig, PulseSDK, PulseSession, type SensorData, type SolanaProof, type StageState, type StatsSummary, type StoredVerificationData, type SubmissionResult, type TBH, type TemporalFingerprint, type TouchSample, type VerificationResult, autocorrelation, bigintToBytes32, computeCommitment, condense, entropy, fetchIdentityState, fuseFeatures, generateLissajousPoints, generateLissajousSequence, generatePhrase, generatePhraseSequence, generateProof, generateSalt, generateSolanaProof, generateTBH, hammingDistance, kurtosis, loadVerificationData, mean, packBits, prepareCircuitInput, randomLissajousParams, serializeProof, simhash, skewness, storeVerificationData, submitViaRelayer, submitViaWallet, toBigEndian32, variance };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  declare const FINGERPRINT_BITS = 256;
2
2
  declare const DEFAULT_THRESHOLD = 30;
3
+ declare const DEFAULT_MIN_DISTANCE = 3;
3
4
  declare const MIN_CAPTURE_MS = 2000;
4
5
  declare const MAX_CAPTURE_MS = 60000;
5
6
  declare const DEFAULT_CAPTURE_MS = 7000;
@@ -50,8 +51,6 @@ interface CaptureOptions {
50
51
  minDurationMs?: number;
51
52
  /** Maximum capture duration in ms. Auto-stops if signal hasn't fired. Default: 60000 */
52
53
  maxDurationMs?: number;
53
- /** Called with RMS audio level (0-1) on each buffer during audio capture (~4x per second). */
54
- onAudioLevel?: (rms: number) => void;
55
54
  }
56
55
  /** Stage of a capture session */
57
56
  type CaptureStage = "audio" | "motion" | "touch";
@@ -120,7 +119,7 @@ declare class PulseSession {
120
119
  private motionData;
121
120
  private touchData;
122
121
  constructor(config: ResolvedConfig, touchElement?: HTMLElement);
123
- startAudio(onAudioLevel?: (rms: number) => void): Promise<void>;
122
+ startAudio(): Promise<void>;
124
123
  stopAudio(): Promise<AudioCapture | null>;
125
124
  skipAudio(): void;
126
125
  startMotion(): Promise<void>;
@@ -224,6 +223,18 @@ declare function variance(values: number[], mu?: number): number;
224
223
  declare function skewness(values: number[]): number;
225
224
  declare function kurtosis(values: number[]): number;
226
225
  declare function condense(values: number[]): StatsSummary;
226
+ /**
227
+ * Shannon entropy over histogram bins. Measures information density.
228
+ * Real human data has moderate entropy (varied but structured).
229
+ * Synthetic data is either too uniform (high entropy) or too structured (low entropy).
230
+ */
231
+ declare function entropy(values: number[], bins?: number): number;
232
+ /**
233
+ * Autocorrelation at a given lag. Detects periodic synthetic patterns.
234
+ * Real human data has low autocorrelation at most lags (chaotic/noisy).
235
+ * Synthetic data often has high autocorrelation (periodic/smooth).
236
+ */
237
+ declare function autocorrelation(values: number[], lag?: number): number;
227
238
  declare function fuseFeatures(audio: number[], motion: number[], touch: number[]): number[];
228
239
 
229
240
  /** Serialized proof ready for on-chain submission */
@@ -248,6 +259,7 @@ interface CircuitInput {
248
259
  commitment_new: string;
249
260
  commitment_prev: string;
250
261
  threshold: string;
262
+ min_distance: string;
251
263
  }
252
264
  /** Proof generation result */
253
265
  interface ProofResult {
@@ -271,7 +283,7 @@ declare function serializeProof(proof: RawProof, publicSignals: string[]): Solan
271
283
  /**
272
284
  * Prepare circuit input from current and previous TBH data.
273
285
  */
274
- declare function prepareCircuitInput(current: TBH, previous: TBH, threshold?: number): CircuitInput;
286
+ declare function prepareCircuitInput(current: TBH, previous: TBH, threshold?: number, minDistance?: number): CircuitInput;
275
287
  /**
276
288
  * Generate a Groth16 proof for the Hamming distance circuit.
277
289
  *
@@ -348,6 +360,11 @@ declare function loadVerificationData(): StoredVerificationData | null;
348
360
  * Each phrase is 5-6 syllable pairs, forming nonsensical but speakable words.
349
361
  */
350
362
  declare function generatePhrase(wordCount?: number): string;
363
+ /**
364
+ * Generate a sequence of phrases for dynamic mid-session switching.
365
+ * Each phrase uses a different syllable subset to prevent pre-computation.
366
+ */
367
+ declare function generatePhraseSequence(count?: number, wordCount?: number): string[];
351
368
 
352
369
  /**
353
370
  * Generate Lissajous curve points for the touch tracing challenge.
@@ -374,5 +391,13 @@ declare function randomLissajousParams(): LissajousParams;
374
391
  * Generate Lissajous curve points normalized to [0, 1] range.
375
392
  */
376
393
  declare function generateLissajousPoints(params: LissajousParams): Point2D[];
394
+ /**
395
+ * Generate a sequence of Lissajous curves for dynamic mid-session switching.
396
+ * Each curve uses different parameters, preventing pre-computation.
397
+ */
398
+ declare function generateLissajousSequence(count?: number): {
399
+ params: LissajousParams;
400
+ points: Point2D[];
401
+ }[];
377
402
 
378
- export { type AudioCapture, type CaptureOptions, type CaptureStage, type CircuitInput, DEFAULT_CAPTURE_MS, DEFAULT_THRESHOLD, FINGERPRINT_BITS, type FeatureVector, type FusedFeatureVector, type IdentityState, type LissajousParams, MAX_CAPTURE_MS, MIN_CAPTURE_MS, type MotionSample, PROGRAM_IDS, type PackedFingerprint, type Point2D, type ProofResult, type PulseConfig, PulseSDK, PulseSession, type SensorData, type SolanaProof, type StageState, type StatsSummary, type StoredVerificationData, type SubmissionResult, type TBH, type TemporalFingerprint, type TouchSample, type VerificationResult, bigintToBytes32, computeCommitment, condense, fetchIdentityState, fuseFeatures, generateLissajousPoints, generatePhrase, generateProof, generateSalt, generateSolanaProof, generateTBH, hammingDistance, kurtosis, loadVerificationData, mean, packBits, prepareCircuitInput, randomLissajousParams, serializeProof, simhash, skewness, storeVerificationData, submitViaRelayer, submitViaWallet, toBigEndian32, variance };
403
+ export { type AudioCapture, type CaptureOptions, type CaptureStage, type CircuitInput, DEFAULT_CAPTURE_MS, DEFAULT_MIN_DISTANCE, DEFAULT_THRESHOLD, FINGERPRINT_BITS, type FeatureVector, type FusedFeatureVector, type IdentityState, type LissajousParams, MAX_CAPTURE_MS, MIN_CAPTURE_MS, type MotionSample, PROGRAM_IDS, type PackedFingerprint, type Point2D, type ProofResult, type PulseConfig, PulseSDK, PulseSession, type SensorData, type SolanaProof, type StageState, type StatsSummary, type StoredVerificationData, type SubmissionResult, type TBH, type TemporalFingerprint, type TouchSample, type VerificationResult, autocorrelation, bigintToBytes32, computeCommitment, condense, entropy, fetchIdentityState, fuseFeatures, generateLissajousPoints, generateLissajousSequence, generatePhrase, generatePhraseSequence, generateProof, generateSalt, generateSolanaProof, generateTBH, hammingDistance, kurtosis, loadVerificationData, mean, packBits, prepareCircuitInput, randomLissajousParams, serializeProof, simhash, skewness, storeVerificationData, submitViaRelayer, submitViaWallet, toBigEndian32, variance };
package/dist/index.js CHANGED
@@ -12978,6 +12978,7 @@ var init_esm4 = __esm({
12978
12978
  var index_exports = {};
12979
12979
  __export(index_exports, {
12980
12980
  DEFAULT_CAPTURE_MS: () => DEFAULT_CAPTURE_MS,
12981
+ DEFAULT_MIN_DISTANCE: () => DEFAULT_MIN_DISTANCE,
12981
12982
  DEFAULT_THRESHOLD: () => DEFAULT_THRESHOLD,
12982
12983
  FINGERPRINT_BITS: () => FINGERPRINT_BITS,
12983
12984
  MAX_CAPTURE_MS: () => MAX_CAPTURE_MS,
@@ -12985,13 +12986,17 @@ __export(index_exports, {
12985
12986
  PROGRAM_IDS: () => PROGRAM_IDS,
12986
12987
  PulseSDK: () => PulseSDK,
12987
12988
  PulseSession: () => PulseSession,
12989
+ autocorrelation: () => autocorrelation,
12988
12990
  bigintToBytes32: () => bigintToBytes32,
12989
12991
  computeCommitment: () => computeCommitment,
12990
12992
  condense: () => condense,
12993
+ entropy: () => entropy,
12991
12994
  fetchIdentityState: () => fetchIdentityState,
12992
12995
  fuseFeatures: () => fuseFeatures,
12993
12996
  generateLissajousPoints: () => generateLissajousPoints,
12997
+ generateLissajousSequence: () => generateLissajousSequence,
12994
12998
  generatePhrase: () => generatePhrase,
12999
+ generatePhraseSequence: () => generatePhraseSequence,
12995
13000
  generateProof: () => generateProof,
12996
13001
  generateSalt: () => generateSalt,
12997
13002
  generateSolanaProof: () => generateSolanaProof,
@@ -13023,6 +13028,7 @@ var BN254_SCALAR_FIELD = BigInt(
13023
13028
  );
13024
13029
  var FINGERPRINT_BITS = 256;
13025
13030
  var DEFAULT_THRESHOLD = 30;
13031
+ var DEFAULT_MIN_DISTANCE = 3;
13026
13032
  var PROOF_A_SIZE = 64;
13027
13033
  var PROOF_B_SIZE = 128;
13028
13034
  var PROOF_C_SIZE = 64;
@@ -13043,8 +13049,7 @@ async function captureAudio(options = {}) {
13043
13049
  const {
13044
13050
  signal,
13045
13051
  minDurationMs = MIN_CAPTURE_MS,
13046
- maxDurationMs = MAX_CAPTURE_MS,
13047
- onAudioLevel
13052
+ maxDurationMs = MAX_CAPTURE_MS
13048
13053
  } = options;
13049
13054
  const stream = await navigator.mediaDevices.getUserMedia({
13050
13055
  audio: {
@@ -13064,13 +13069,7 @@ async function captureAudio(options = {}) {
13064
13069
  const bufferSize = 4096;
13065
13070
  const processor = ctx.createScriptProcessor(bufferSize, 1, 1);
13066
13071
  processor.onaudioprocess = (e2) => {
13067
- const data = e2.inputBuffer.getChannelData(0);
13068
- chunks.push(new Float32Array(data));
13069
- if (onAudioLevel) {
13070
- let sum = 0;
13071
- for (let i = 0; i < data.length; i++) sum += data[i] * data[i];
13072
- onAudioLevel(Math.sqrt(sum / data.length));
13073
- }
13072
+ chunks.push(new Float32Array(e2.inputBuffer.getChannelData(0)));
13074
13073
  };
13075
13074
  source.connect(processor);
13076
13075
  processor.connect(ctx.destination);
@@ -13269,6 +13268,37 @@ function condense(values) {
13269
13268
  kurtosis: kurtosis(values)
13270
13269
  };
13271
13270
  }
13271
+ function entropy(values, bins = 16) {
13272
+ if (values.length < 2) return 0;
13273
+ const min = Math.min(...values);
13274
+ const max = Math.max(...values);
13275
+ if (min === max) return 0;
13276
+ const counts = new Array(bins).fill(0);
13277
+ const range = max - min;
13278
+ for (const v of values) {
13279
+ const idx = Math.min(Math.floor((v - min) / range * bins), bins - 1);
13280
+ counts[idx]++;
13281
+ }
13282
+ let h = 0;
13283
+ for (const c of counts) {
13284
+ if (c > 0) {
13285
+ const p = c / values.length;
13286
+ h -= p * Math.log2(p);
13287
+ }
13288
+ }
13289
+ return h;
13290
+ }
13291
+ function autocorrelation(values, lag = 1) {
13292
+ if (values.length <= lag) return 0;
13293
+ const m = mean(values);
13294
+ const v = variance(values, m);
13295
+ if (v === 0) return 0;
13296
+ let sum = 0;
13297
+ for (let i = 0; i < values.length - lag; i++) {
13298
+ sum += (values[i] - m) * (values[i + lag] - m);
13299
+ }
13300
+ return sum / ((values.length - lag) * v);
13301
+ }
13272
13302
  function fuseFeatures(audio, motion, touch) {
13273
13303
  return [...audio, ...motion, ...touch];
13274
13304
  }
@@ -13283,10 +13313,10 @@ function extractMFCC(audio) {
13283
13313
  try {
13284
13314
  Meyda = require("meyda");
13285
13315
  } catch {
13286
- return new Array(NUM_MFCC * 3 * 4).fill(0);
13316
+ return new Array(NUM_MFCC * 3 * 4 + NUM_MFCC).fill(0);
13287
13317
  }
13288
13318
  const numFrames = Math.floor((samples.length - FRAME_SIZE) / HOP_SIZE) + 1;
13289
- if (numFrames < 3) return new Array(NUM_MFCC * 3 * 4).fill(0);
13319
+ if (numFrames < 3) return new Array(NUM_MFCC * 3 * 4 + NUM_MFCC).fill(0);
13290
13320
  const mfccFrames = [];
13291
13321
  for (let i = 0; i < numFrames; i++) {
13292
13322
  const start = i * HOP_SIZE;
@@ -13321,6 +13351,10 @@ function extractMFCC(audio) {
13321
13351
  const stats = condense(dd);
13322
13352
  features.push(stats.mean, stats.variance, stats.skewness, stats.kurtosis);
13323
13353
  }
13354
+ for (let c = 0; c < NUM_MFCC; c++) {
13355
+ const raw = mfccFrames.map((f) => f[c] ?? 0);
13356
+ features.push(entropy(raw));
13357
+ }
13324
13358
  return features;
13325
13359
  }
13326
13360
  function computeDeltas(frames) {
@@ -13335,7 +13369,7 @@ function computeDeltas(frames) {
13335
13369
 
13336
13370
  // src/extraction/kinematic.ts
13337
13371
  function extractMotionFeatures(samples) {
13338
- if (samples.length < 5) return new Array(48).fill(0);
13372
+ if (samples.length < 5) return new Array(54).fill(0);
13339
13373
  const axes = {
13340
13374
  ax: samples.map((s) => s.ax),
13341
13375
  ay: samples.map((s) => s.ay),
@@ -13361,10 +13395,19 @@ function extractMotionFeatures(samples) {
13361
13395
  jounceStats.kurtosis
13362
13396
  );
13363
13397
  }
13398
+ for (const values of Object.values(axes)) {
13399
+ const jerk = derivative(values);
13400
+ const windowSize = Math.max(5, Math.floor(jerk.length / 4));
13401
+ const windowVariances = [];
13402
+ for (let i = 0; i <= jerk.length - windowSize; i += windowSize) {
13403
+ windowVariances.push(variance(jerk.slice(i, i + windowSize)));
13404
+ }
13405
+ features.push(windowVariances.length >= 2 ? variance(windowVariances) : 0);
13406
+ }
13364
13407
  return features;
13365
13408
  }
13366
13409
  function extractTouchFeatures(samples) {
13367
- if (samples.length < 5) return new Array(32).fill(0);
13410
+ if (samples.length < 5) return new Array(36).fill(0);
13368
13411
  const x = samples.map((s) => s.x);
13369
13412
  const y = samples.map((s) => s.y);
13370
13413
  const pressure = samples.map((s) => s.pressure);
@@ -13384,6 +13427,14 @@ function extractTouchFeatures(samples) {
13384
13427
  const jerkY = derivative(accY);
13385
13428
  features.push(...Object.values(condense(jerkX)));
13386
13429
  features.push(...Object.values(condense(jerkY)));
13430
+ for (const values of [vx, vy, pressure, area]) {
13431
+ const windowSize = Math.max(5, Math.floor(values.length / 4));
13432
+ const windowVariances = [];
13433
+ for (let i = 0; i <= values.length - windowSize; i += windowSize) {
13434
+ windowVariances.push(variance(values.slice(i, i + windowSize)));
13435
+ }
13436
+ features.push(windowVariances.length >= 2 ? variance(windowVariances) : 0);
13437
+ }
13387
13438
  return features;
13388
13439
  }
13389
13440
  function derivative(values) {
@@ -13565,7 +13616,7 @@ async function getSnarkjs() {
13565
13616
  }
13566
13617
  return snarkjsModule;
13567
13618
  }
13568
- function prepareCircuitInput(current, previous, threshold = DEFAULT_THRESHOLD) {
13619
+ function prepareCircuitInput(current, previous, threshold = DEFAULT_THRESHOLD, minDistance = DEFAULT_MIN_DISTANCE) {
13569
13620
  return {
13570
13621
  ft_new: current.fingerprint,
13571
13622
  ft_prev: previous.fingerprint,
@@ -13573,7 +13624,8 @@ function prepareCircuitInput(current, previous, threshold = DEFAULT_THRESHOLD) {
13573
13624
  salt_prev: previous.salt.toString(),
13574
13625
  commitment_new: current.commitment.toString(),
13575
13626
  commitment_prev: previous.commitment.toString(),
13576
- threshold: threshold.toString()
13627
+ threshold: threshold.toString(),
13628
+ min_distance: minDistance.toString()
13577
13629
  };
13578
13630
  }
13579
13631
  async function generateProof(input, wasmPath, zkeyPath) {
@@ -13791,7 +13843,7 @@ var inMemoryStore = null;
13791
13843
 
13792
13844
  // src/pulse.ts
13793
13845
  function extractFeatures(data) {
13794
- const audioFeatures = data.audio ? extractMFCC(data.audio) : new Array(156).fill(0);
13846
+ const audioFeatures = data.audio ? extractMFCC(data.audio) : new Array(169).fill(0);
13795
13847
  const motionFeatures = extractMotionFeatures(data.motion);
13796
13848
  const touchFeatures = extractTouchFeatures(data.touch);
13797
13849
  return fuseFeatures(audioFeatures, motionFeatures, touchFeatures);
@@ -13887,14 +13939,13 @@ var PulseSession = class {
13887
13939
  this.touchElement = touchElement;
13888
13940
  }
13889
13941
  // --- Audio ---
13890
- async startAudio(onAudioLevel) {
13942
+ async startAudio() {
13891
13943
  if (this.audioStageState !== "idle")
13892
13944
  throw new Error("Audio capture already started");
13893
13945
  this.audioStageState = "capturing";
13894
13946
  this.audioController = new AbortController();
13895
13947
  this.audioPromise = captureAudio({
13896
- signal: this.audioController.signal,
13897
- onAudioLevel
13948
+ signal: this.audioController.signal
13898
13949
  }).catch(() => null);
13899
13950
  }
13900
13951
  async stopAudio() {
@@ -14142,6 +14193,28 @@ function generatePhrase(wordCount = 5) {
14142
14193
  }
14143
14194
  return words.join(" ");
14144
14195
  }
14196
+ function generatePhraseSequence(count = 3, wordCount = 4) {
14197
+ const subsetSize = Math.floor(SYLLABLES.length / count);
14198
+ const phrases = [];
14199
+ for (let p = 0; p < count; p++) {
14200
+ const start = p * subsetSize % SYLLABLES.length;
14201
+ const subset = [
14202
+ ...SYLLABLES.slice(start, start + subsetSize),
14203
+ ...SYLLABLES.slice(0, Math.max(0, start + subsetSize - SYLLABLES.length))
14204
+ ];
14205
+ const words = [];
14206
+ for (let w = 0; w < wordCount; w++) {
14207
+ const syllableCount = 2 + Math.floor(Math.random() * 2);
14208
+ let word = "";
14209
+ for (let s = 0; s < syllableCount; s++) {
14210
+ word += subset[Math.floor(Math.random() * subset.length)];
14211
+ }
14212
+ words.push(word);
14213
+ }
14214
+ phrases.push(words.join(" "));
14215
+ }
14216
+ return phrases;
14217
+ }
14145
14218
 
14146
14219
  // src/challenge/lissajous.ts
14147
14220
  function randomLissajousParams() {
@@ -14172,9 +14245,37 @@ function generateLissajousPoints(params) {
14172
14245
  }
14173
14246
  return result;
14174
14247
  }
14248
+ function generateLissajousSequence(count = 2) {
14249
+ const allRatios = [
14250
+ [1, 2],
14251
+ [2, 3],
14252
+ [3, 4],
14253
+ [3, 5],
14254
+ [4, 5],
14255
+ [1, 3],
14256
+ [2, 5],
14257
+ [5, 6],
14258
+ [3, 7],
14259
+ [4, 7]
14260
+ ];
14261
+ const shuffled = [...allRatios].sort(() => Math.random() - 0.5);
14262
+ const sequence = [];
14263
+ for (let i = 0; i < count; i++) {
14264
+ const pair = shuffled[i % shuffled.length];
14265
+ const params = {
14266
+ a: pair[0],
14267
+ b: pair[1],
14268
+ delta: Math.PI * (0.1 + Math.random() * 0.8),
14269
+ points: 200
14270
+ };
14271
+ sequence.push({ params, points: generateLissajousPoints(params) });
14272
+ }
14273
+ return sequence;
14274
+ }
14175
14275
  // Annotate the CommonJS export names for ESM import in node:
14176
14276
  0 && (module.exports = {
14177
14277
  DEFAULT_CAPTURE_MS,
14278
+ DEFAULT_MIN_DISTANCE,
14178
14279
  DEFAULT_THRESHOLD,
14179
14280
  FINGERPRINT_BITS,
14180
14281
  MAX_CAPTURE_MS,
@@ -14182,13 +14283,17 @@ function generateLissajousPoints(params) {
14182
14283
  PROGRAM_IDS,
14183
14284
  PulseSDK,
14184
14285
  PulseSession,
14286
+ autocorrelation,
14185
14287
  bigintToBytes32,
14186
14288
  computeCommitment,
14187
14289
  condense,
14290
+ entropy,
14188
14291
  fetchIdentityState,
14189
14292
  fuseFeatures,
14190
14293
  generateLissajousPoints,
14294
+ generateLissajousSequence,
14191
14295
  generatePhrase,
14296
+ generatePhraseSequence,
14192
14297
  generateProof,
14193
14298
  generateSalt,
14194
14299
  generateSolanaProof,