@aiqa/sdk 1.5.0 → 1.6.0-develop.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.
@@ -32,7 +32,7 @@ declare enum ConversationLanguage {
32
32
  ES = "es",
33
33
  FR = "fr"
34
34
  }
35
- type SdkEvent = Methods | "connect" | "disconnect" | "close" | "error" | "reconnect_attempt" | "reconnect_error" | "reconnect_failed" | "authenticated" | "app_version" | "FACT_VERIFIED";
35
+ type SdkEvent = Methods | "connect" | "disconnect" | "close" | "error" | "reconnect_attempt" | "reconnect_error" | "reconnect_failed" | "authenticated" | "app_version" | "FACT_VERIFIED" | "TRANSCRIPTION_UPDATED";
36
36
  declare enum FactAnswerFormatType {
37
37
  string = "STRING",
38
38
  number = "NUMBER",
@@ -182,6 +182,72 @@ interface FactVerifiedPayload {
182
182
  type FlagFactResult = {
183
183
  success: boolean;
184
184
  };
185
+ /**
186
+ * Coarse dialog-act label produced by the backend's `UtteranceEnricher`.
187
+ * Advisory only — do not gate production behaviour on this value; the
188
+ * enricher emits its best-effort categorical guess plus a separate
189
+ * `dialog_act_confidence` band.
190
+ *
191
+ * Mirrors `DialogAct` in
192
+ * `libs/types/src/utterance-enrichment.ts` on the backend.
193
+ */
194
+ type DialogAct = "question" | "answer" | "acknowledgement" | "filler" | "other";
195
+ /**
196
+ * Categorical confidence band for `dialog_act` ONLY. Named distinctly
197
+ * from the numeric STT `confidence` field on the same utterance — the
198
+ * two are unrelated and conflating them is a documented drift hazard on
199
+ * the backend.
200
+ *
201
+ * Mirrors `DialogActConfidence` in
202
+ * `libs/types/src/utterance-enrichment.ts` on the backend.
203
+ */
204
+ type DialogActConfidence = "high" | "medium" | "low";
205
+ /**
206
+ * Persisted utterance shape — matches `TranscriptionUtteranceDto` on the
207
+ * gateway (`apps/realtime-verification/src/socket/realtimeVerification.gateway.output.dto.ts`)
208
+ * and the `Utterance` interface in `libs/types/src/common.ts` field for
209
+ * field. Sent over the wire by the `TRANSCRIPTION_UPDATED` broadcast.
210
+ *
211
+ * The 7 enricher classification fields are all optional because
212
+ * pre-cutover utterances (persisted before backend PR #234) lack them.
213
+ * Treat any missing or nullish value as "unknown" — NOT as a deliberate
214
+ * negative signal. `is_ivr_filler_candidate === false` in particular
215
+ * does NOT round-trip through `undefined`, so consumer logic that wants
216
+ * "definitely not a filler" must check `=== false` after confirming the
217
+ * field is present.
218
+ */
219
+ type Utterance = {
220
+ /** UUIDv7 — chronologically sortable. Use `id` (NOT `order`) for sorting. */
221
+ id: string;
222
+ call_id: number;
223
+ /**
224
+ * NOT YET IMPLEMENTED — currently hardcoded to 0 at write sites on
225
+ * the backend. Do not use for sorting; sort by `id` (UUIDv7) instead.
226
+ */
227
+ order: number;
228
+ is_agent: boolean | null;
229
+ speaker: number;
230
+ text: string;
231
+ text_en: string | null;
232
+ start_time: number;
233
+ end_time: number;
234
+ dialog_act?: DialogAct;
235
+ dialog_act_confidence?: DialogActConfidence;
236
+ confidence?: number | null;
237
+ top_alt_margin?: number | null;
238
+ is_ivr_filler_candidate?: boolean;
239
+ spoken_digits_normalized?: string | null;
240
+ vendor?: string | null;
241
+ };
242
+ /**
243
+ * Payload of the `TRANSCRIPTION_UPDATED` broadcast event. Mirrors
244
+ * `TranscriptionUpdateResponseDto` on the gateway.
245
+ */
246
+ type TranscriptionUpdate = {
247
+ policyNumber: string;
248
+ conversationId: string;
249
+ utterance: Utterance;
250
+ };
185
251
  type SdkError = {
186
252
  code: "INVALID_CONFIG" | "CONNECTION_ERROR" | "TIMEOUT" | "SERVER_ERROR" | "UNEXPECTED_RESPONSE";
187
253
  message: string;
@@ -191,7 +257,7 @@ declare abstract class IAiqaSdk {
191
257
  abstract connect(): void;
192
258
  abstract disconnect(): void;
193
259
  abstract on(eventName: string, callback: (...args: any[]) => any): void;
194
- abstract off(eventName: string): void;
260
+ abstract off(eventName: string, callback?: (...args: any[]) => any): void;
195
261
  abstract getApplication(): Promise<Application>;
196
262
  abstract addFact(fact: ApplicationFactInput): Promise<AddFactResult>;
197
263
  abstract flagFact(factId: string): Promise<FlagFactResult>;
@@ -208,6 +274,7 @@ declare class AiQaSdk implements IAiqaSdk {
208
274
  cachedApplication?: Application;
209
275
  internalListeners: Map<SdkEvent, Set<(...args: any[]) => void>>;
210
276
  internalEvents: Set<SdkEvent>;
277
+ private wrappersByEvent;
211
278
  constructor(applicationId: string, applicationSecret: string, url?: string);
212
279
  on(name: SdkEvent, callback: (...args: any[]) => any): void;
213
280
  off(name: SdkEvent, callback?: (...args: any[]) => any): void;
@@ -229,4 +296,4 @@ declare class AiQaSdk implements IAiqaSdk {
229
296
  private isManagerEvent;
230
297
  }
231
298
 
232
- export { type AddFactResult, AiQaSdk, type Application, type ApplicationFact, type ApplicationFactAnswerChoice, type ApplicationFactAnswerFormat, type ApplicationFactInput, ConversationLanguage, FactAnswerFormatType, type FactVerifiedPayload, type FlagFactResult, IAiqaSdk, Methods, type QuestionStatus, type SdkError, type SdkEvent, type SetConversationLanguageResult, type SetConversationProductResult, type VerificationStatus };
299
+ export { type AddFactResult, AiQaSdk, type Application, type ApplicationFact, type ApplicationFactAnswerChoice, type ApplicationFactAnswerFormat, type ApplicationFactInput, ConversationLanguage, type DialogAct, type DialogActConfidence, FactAnswerFormatType, type FactVerifiedPayload, type FlagFactResult, IAiqaSdk, Methods, type QuestionStatus, type SdkError, type SdkEvent, type SetConversationLanguageResult, type SetConversationProductResult, type TranscriptionUpdate, type Utterance, type VerificationStatus };
@@ -3396,7 +3396,7 @@ var compareVersions = (left, right) => {
3396
3396
  // package.json
3397
3397
  var package_default = {
3398
3398
  name: "@aiqa/sdk",
3399
- version: "1.5.0-develop.1",
3399
+ version: "1.5.0-develop.4",
3400
3400
  main: "dist/node/node.js",
3401
3401
  module: "dist/node/node.js",
3402
3402
  types: "dist/node/node.d.ts",
@@ -3478,9 +3478,43 @@ var package_default = {
3478
3478
  var SDK_VERSION = package_default.version ?? "0.0.1";
3479
3479
  var DEFAULT_URL = "ws://localhost:3025/ws";
3480
3480
  var DEFAULT_TIMEOUT_MS = 6e4;
3481
+ var RUNTIME = typeof window !== "undefined" && typeof window.document !== "undefined" ? "browser" : "node";
3482
+ var USER_AGENT = RUNTIME === "browser" ? typeof navigator !== "undefined" ? navigator.userAgent : "browser/unknown" : typeof process !== "undefined" ? `node ${process.version} ${process.platform}` : "node/unknown";
3483
+ function isThenable(value2) {
3484
+ return value2 !== null && typeof value2 === "object" && typeof value2.then === "function";
3485
+ }
3486
+ function wrapAckHandler(callback) {
3487
+ return (...args) => {
3488
+ const last = args[args.length - 1];
3489
+ const hasAck = typeof last === "function";
3490
+ const payload = hasAck ? args.slice(0, -1) : args;
3491
+ try {
3492
+ const handlerResult = callback(...payload);
3493
+ if (isThenable(handlerResult)) {
3494
+ handlerResult.then(void 0, (err) => {
3495
+ console.error("[AiQaSdk] async event handler rejected:", err);
3496
+ });
3497
+ }
3498
+ } catch (err) {
3499
+ console.error("[AiQaSdk] event handler threw:", err);
3500
+ } finally {
3501
+ if (hasAck) {
3502
+ try {
3503
+ last();
3504
+ } catch {
3505
+ }
3506
+ }
3507
+ }
3508
+ };
3509
+ }
3481
3510
  var AiQaSdk = class {
3482
3511
  constructor(applicationId, applicationSecret, url2) {
3483
3512
  this.socket = null;
3513
+ // event → originalCallback → Set<wrappedCallback> registered on
3514
+ // socket.io. A Set per (event, callback) so that subscribing the
3515
+ // same fn twice registers two wrappers and two off() calls clear
3516
+ // both.
3517
+ this.wrappersByEvent = /* @__PURE__ */ new Map();
3484
3518
  this.url = url2 ?? DEFAULT_URL;
3485
3519
  this.internalListeners = /* @__PURE__ */ new Map();
3486
3520
  this.internalEvents = /* @__PURE__ */ new Set(["authenticated"]);
@@ -3505,7 +3539,19 @@ var AiQaSdk = class {
3505
3539
  if (this.isManagerEvent(name)) {
3506
3540
  this.socket.io.on(name, callback);
3507
3541
  } else {
3508
- this.socket.on(name, callback);
3542
+ const wrapped = wrapAckHandler(callback);
3543
+ let wrappersByCallback = this.wrappersByEvent.get(name);
3544
+ if (!wrappersByCallback) {
3545
+ wrappersByCallback = /* @__PURE__ */ new Map();
3546
+ this.wrappersByEvent.set(name, wrappersByCallback);
3547
+ }
3548
+ let wrappers = wrappersByCallback.get(callback);
3549
+ if (!wrappers) {
3550
+ wrappers = /* @__PURE__ */ new Set();
3551
+ wrappersByCallback.set(callback, wrappers);
3552
+ }
3553
+ wrappers.add(wrapped);
3554
+ this.socket.on(name, wrapped);
3509
3555
  }
3510
3556
  }
3511
3557
  off(name, callback) {
@@ -3528,6 +3574,25 @@ var AiQaSdk = class {
3528
3574
  }
3529
3575
  if (this.isManagerEvent(name)) {
3530
3576
  this.socket.io.off(name, callback);
3577
+ return;
3578
+ }
3579
+ if (!callback) {
3580
+ this.socket.off(name);
3581
+ this.wrappersByEvent.delete(name);
3582
+ return;
3583
+ }
3584
+ const wrappersByCallback = this.wrappersByEvent.get(name);
3585
+ const wrappers = wrappersByCallback?.get(callback);
3586
+ if (wrappers && wrappers.size > 0) {
3587
+ const wrapped = wrappers.values().next().value;
3588
+ wrappers.delete(wrapped);
3589
+ this.socket.off(name, wrapped);
3590
+ if (wrappers.size === 0) {
3591
+ wrappersByCallback.delete(callback);
3592
+ if (wrappersByCallback.size === 0) {
3593
+ this.wrappersByEvent.delete(name);
3594
+ }
3595
+ }
3531
3596
  } else {
3532
3597
  this.socket.off(name, callback);
3533
3598
  }
@@ -3545,34 +3610,38 @@ var AiQaSdk = class {
3545
3610
  this.socket.on("connect", () => {
3546
3611
  this.authenticate();
3547
3612
  });
3548
- this.socket.on("app_version", (payload) => {
3549
- let versionRange = payload;
3550
- if (typeof payload === "string") {
3551
- try {
3552
- versionRange = JSON.parse(payload);
3553
- } catch (error) {
3554
- versionRange = payload;
3613
+ this.socket.on(
3614
+ "app_version",
3615
+ wrapAckHandler((payload) => {
3616
+ let versionRange = payload;
3617
+ if (typeof payload === "string") {
3618
+ try {
3619
+ versionRange = JSON.parse(payload);
3620
+ } catch (error) {
3621
+ versionRange = payload;
3622
+ }
3555
3623
  }
3556
- }
3557
- const minVersion = versionRange?.min;
3558
- const maxVersion = versionRange?.max;
3559
- if (minVersion || maxVersion) {
3560
- const tooLow = minVersion ? compareVersions(SDK_VERSION, minVersion) < 0 : false;
3561
- const tooHigh = maxVersion ? compareVersions(SDK_VERSION, maxVersion) > 0 : false;
3562
- if (tooLow || tooHigh) {
3563
- const error = new Error(
3564
- `SDK version ${SDK_VERSION} is not supported. Supported range: ${minVersion || "-"} to ${maxVersion || "-"}.`
3565
- );
3566
- this.socket.disconnect();
3567
- this.socket.emit("error", error);
3624
+ const minVersion = versionRange?.min;
3625
+ const maxVersion = versionRange?.max;
3626
+ if (minVersion || maxVersion) {
3627
+ const tooLow = minVersion ? compareVersions(SDK_VERSION, minVersion) < 0 : false;
3628
+ const tooHigh = maxVersion ? compareVersions(SDK_VERSION, maxVersion) > 0 : false;
3629
+ if (tooLow || tooHigh) {
3630
+ const error = new Error(
3631
+ `SDK version ${SDK_VERSION} is not supported. Supported range: ${minVersion || "-"} to ${maxVersion || "-"}.`
3632
+ );
3633
+ this.socket.disconnect();
3634
+ this.socket.emit("error", error);
3635
+ }
3568
3636
  }
3569
- }
3570
- });
3637
+ })
3638
+ );
3571
3639
  }
3572
3640
  disconnect() {
3573
3641
  if (this.socket) {
3574
3642
  this.socket.disconnect();
3575
3643
  }
3644
+ this.wrappersByEvent.clear();
3576
3645
  }
3577
3646
  sendMessage(method, data) {
3578
3647
  return new Promise((resolve, reject) => {
@@ -3669,7 +3738,10 @@ var AiQaSdk = class {
3669
3738
  try {
3670
3739
  const result = await this.sendMessage("AUTHENTICATE" /* AUTHENTICATE */, {
3671
3740
  applicationId: this.applicationId,
3672
- applicationSecretKey: this.applicationSecretKey
3741
+ applicationSecretKey: this.applicationSecretKey,
3742
+ sdkVersion: SDK_VERSION,
3743
+ runtime: RUNTIME,
3744
+ userAgent: USER_AGENT
3673
3745
  });
3674
3746
  const application = result && result.data !== void 0 ? result.data : result;
3675
3747
  this.cachedApplication = application;