@volley/recognition-client-sdk 0.1.419 → 0.1.422

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.
Files changed (37) hide show
  1. package/dist/browser.bundled.d.ts +1185 -0
  2. package/dist/browser.d.ts +10 -0
  3. package/dist/browser.d.ts.map +1 -0
  4. package/dist/config-builder.d.ts +134 -0
  5. package/dist/config-builder.d.ts.map +1 -0
  6. package/dist/errors.d.ts +41 -0
  7. package/dist/errors.d.ts.map +1 -0
  8. package/dist/factory.d.ts +36 -0
  9. package/dist/factory.d.ts.map +1 -0
  10. package/dist/index.bundled.d.ts +2396 -0
  11. package/dist/index.d.ts +16 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +0 -12
  14. package/dist/index.js.map +2 -2
  15. package/dist/recog-client-sdk.browser.d.ts +10 -0
  16. package/dist/recog-client-sdk.browser.d.ts.map +1 -0
  17. package/dist/recognition-client.d.ts +124 -0
  18. package/dist/recognition-client.d.ts.map +1 -0
  19. package/dist/recognition-client.types.d.ts +271 -0
  20. package/dist/recognition-client.types.d.ts.map +1 -0
  21. package/dist/simplified-vgf-recognition-client.d.ts +178 -0
  22. package/dist/simplified-vgf-recognition-client.d.ts.map +1 -0
  23. package/dist/utils/audio-ring-buffer.d.ts +69 -0
  24. package/dist/utils/audio-ring-buffer.d.ts.map +1 -0
  25. package/dist/utils/message-handler.d.ts +45 -0
  26. package/dist/utils/message-handler.d.ts.map +1 -0
  27. package/dist/utils/url-builder.d.ts +28 -0
  28. package/dist/utils/url-builder.d.ts.map +1 -0
  29. package/dist/vgf-recognition-mapper.d.ts +66 -0
  30. package/dist/vgf-recognition-mapper.d.ts.map +1 -0
  31. package/dist/vgf-recognition-state.d.ts +91 -0
  32. package/dist/vgf-recognition-state.d.ts.map +1 -0
  33. package/package.json +3 -3
  34. package/src/simplified-vgf-recognition-client.integration.spec.ts +10 -8
  35. package/src/simplified-vgf-recognition-client.spec.ts +10 -5
  36. package/src/simplified-vgf-recognition-client.ts +1 -17
  37. package/src/utils/message-handler.spec.ts +4 -4
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Message Handler for Recognition Client
3
+ * Routes incoming WebSocket messages to appropriate callbacks
4
+ */
5
+ import { type TranscriptionResultV1, type FunctionCallResultV1, type MetadataResultV1, type ErrorResultV1, type ClientControlMessageV1 } from '@recog/shared-types';
6
+ export interface MessageHandlerCallbacks {
7
+ onTranscript: (result: TranscriptionResultV1) => void;
8
+ onFunctionCall: (result: FunctionCallResultV1) => void;
9
+ onMetadata: (metadata: MetadataResultV1) => void;
10
+ onError: (error: ErrorResultV1) => void;
11
+ onControlMessage: (msg: ClientControlMessageV1) => void;
12
+ logger?: (level: 'debug' | 'info' | 'warn' | 'error', message: string, data?: any) => void;
13
+ }
14
+ export declare class MessageHandler {
15
+ private firstTranscriptTime;
16
+ private sessionStartTime;
17
+ private callbacks;
18
+ constructor(callbacks: MessageHandlerCallbacks);
19
+ /**
20
+ * Set session start time for performance tracking
21
+ */
22
+ setSessionStartTime(time: number): void;
23
+ /**
24
+ * Handle incoming WebSocket message
25
+ */
26
+ handleMessage(msg: {
27
+ v: number;
28
+ type: string;
29
+ data: any;
30
+ }): void;
31
+ /**
32
+ * Handle transcript message and track performance metrics
33
+ * @param result - The transcription result from the server
34
+ */
35
+ private handleTranscription;
36
+ /**
37
+ * Get performance metrics
38
+ */
39
+ getMetrics(): {
40
+ sessionStartTime: number | null;
41
+ firstTranscriptTime: number | null;
42
+ timeToFirstTranscript: number | null;
43
+ };
44
+ }
45
+ //# sourceMappingURL=message-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-handler.d.ts","sourceRoot":"","sources":["../../src/utils/message-handler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAGL,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,sBAAsB,EAC5B,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,uBAAuB;IACtC,YAAY,EAAE,CAAC,MAAM,EAAE,qBAAqB,KAAK,IAAI,CAAC;IACtD,cAAc,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACvD,UAAU,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACjD,OAAO,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACxC,gBAAgB,EAAE,CAAC,GAAG,EAAE,sBAAsB,KAAK,IAAI,CAAC;IACxD,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;CAC5F;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,mBAAmB,CAAuB;IAClD,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,SAAS,CAA0B;gBAE/B,SAAS,EAAE,uBAAuB;IAI9C;;OAEG;IACH,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIvC;;OAEG;IACH,aAAa,CAAC,GAAG,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,GAAG,CAAA;KAAE,GAAG,IAAI;IAsDhE;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAgB3B;;OAEG;IACH,UAAU;;;;;CAUX"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * URL Builder for Recognition Client
3
+ * Handles WebSocket URL construction with query parameters
4
+ */
5
+ import type { GameContextV1, Stage } from '@recog/shared-types';
6
+ import type { RecognitionCallbackUrl } from '../recognition-client.types.js';
7
+ export interface UrlBuilderConfig {
8
+ url?: string;
9
+ stage?: Stage | string;
10
+ audioUtteranceId: string;
11
+ callbackUrls?: RecognitionCallbackUrl[];
12
+ userId?: string;
13
+ gameSessionId?: string;
14
+ deviceId?: string;
15
+ accountId?: string;
16
+ questionAnswerId?: string;
17
+ platform?: string;
18
+ gameContext?: GameContextV1;
19
+ /** Standalone gameId - takes precedence over gameContext.gameId if both provided */
20
+ gameId?: string;
21
+ }
22
+ /**
23
+ * Build WebSocket URL with all query parameters
24
+ * Either `url` or `stage` must be provided (or defaults to production if neither provided)
25
+ * If both are provided, `url` takes precedence over `stage`
26
+ */
27
+ export declare function buildWebSocketUrl(config: UrlBuilderConfig): string;
28
+ //# sourceMappingURL=url-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url-builder.d.ts","sourceRoot":"","sources":["../../src/utils/url-builder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAE7E,MAAM,WAAW,gBAAgB;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,sBAAsB,EAAE,CAAC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,aAAa,CAAC;IAC5B,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CA6DlE"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * VGF Recognition Mapper
3
+ *
4
+ * Maps between the existing recognition client types and the simplified VGF state.
5
+ * This provides a clean abstraction layer for game developers.
6
+ */
7
+ import { RecognitionState } from './vgf-recognition-state.js';
8
+ import { ClientState, IRecognitionClientConfig } from './recognition-client.types.js';
9
+ import { TranscriptionResultV1, ErrorResultV1 } from '@recog/shared-types';
10
+ /**
11
+ * Maps ClientState to RecordingStatus for VGF state
12
+ */
13
+ export declare function mapClientStateToRecordingStatus(clientState: ClientState): string;
14
+ /**
15
+ * Creates a VGF state from transcription result
16
+ */
17
+ export declare function mapTranscriptionResultToState(currentState: RecognitionState, result: TranscriptionResultV1, isRecording: boolean): RecognitionState;
18
+ /**
19
+ * Maps error to state
20
+ */
21
+ export declare function mapErrorToState(currentState: RecognitionState, error: ErrorResultV1): RecognitionState;
22
+ /**
23
+ * Creates initial VGF state from client config
24
+ */
25
+ export declare function createVGFStateFromConfig(config: IRecognitionClientConfig): RecognitionState;
26
+ /**
27
+ * Updates state when recording stops
28
+ */
29
+ export declare function updateStateOnStop(currentState: RecognitionState): RecognitionState;
30
+ /**
31
+ * Resets session state with a new UUID.
32
+ *
33
+ * This creates a fresh session state while preserving non-session fields
34
+ * (like promptSlotMap, asrConfig, etc.)
35
+ *
36
+ * Resets:
37
+ * - audioUtteranceId → new UUID
38
+ * - transcriptionStatus → NOT_STARTED
39
+ * - startRecordingStatus → READY
40
+ * - recognitionActionProcessingState → NOT_STARTED
41
+ * - finalTranscript → undefined
42
+ *
43
+ * @param currentState - The current recognition state
44
+ * @returns A new state with reset session fields and a new UUID
45
+ */
46
+ export declare function resetRecognitionVGFState(currentState: RecognitionState): RecognitionState;
47
+ /**
48
+ * Updates state when client becomes ready
49
+ */
50
+ export declare function updateStateOnReady(currentState: RecognitionState): RecognitionState;
51
+ /**
52
+ * Parses function call from transcript (STEP 3 support)
53
+ * This is a placeholder - actual implementation would use NLP/LLM
54
+ */
55
+ export declare function extractFunctionCallFromTranscript(transcript: string, gameContext?: any): {
56
+ metadata?: string;
57
+ confidence?: number;
58
+ } | null;
59
+ /**
60
+ * Updates state with function call results (STEP 3)
61
+ */
62
+ export declare function updateStateWithFunctionCall(currentState: RecognitionState, functionCall: {
63
+ metadata?: string;
64
+ confidence?: number;
65
+ }): RecognitionState;
66
+ //# sourceMappingURL=vgf-recognition-mapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vgf-recognition-mapper.d.ts","sourceRoot":"","sources":["../src/vgf-recognition-mapper.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACH,gBAAgB,EAKnB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACH,WAAW,EACX,wBAAwB,EAC3B,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACH,qBAAqB,EACrB,aAAa,EAChB,MAAM,qBAAqB,CAAC;AAE7B;;GAEG;AACH,wBAAgB,+BAA+B,CAAC,WAAW,EAAE,WAAW,GAAG,MAAM,CAmBhF;AAED;;GAEG;AACH,wBAAgB,6BAA6B,CACzC,YAAY,EAAE,gBAAgB,EAC9B,MAAM,EAAE,qBAAqB,EAC7B,WAAW,EAAE,OAAO,GACrB,gBAAgB,CAgDlB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC3B,YAAY,EAAE,gBAAgB,EAC9B,KAAK,EAAE,aAAa,GACrB,gBAAgB,CAOlB;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,wBAAwB,GAAG,gBAAgB,CAU3F;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,gBAAgB,GAAG,gBAAgB,CAMlF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,gBAAgB,GAAG,gBAAgB,CASzF;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,gBAAgB,GAAG,gBAAgB,CAKnF;AAED;;;GAGG;AACH,wBAAgB,iCAAiC,CAC7C,UAAU,EAAE,MAAM,EAClB,WAAW,CAAC,EAAE,GAAG,GAClB;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAiBnD;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CACvC,YAAY,EAAE,gBAAgB,EAC9B,YAAY,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GACzD,gBAAgB,CAOlB"}
@@ -0,0 +1,91 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * VGF-style state schema for game-side recognition state/results management.
4
+ *
5
+ * This schema provides a standardized way for game developers to manage
6
+ * voice recognition state and results in their applications. It supports:
7
+ *
8
+ * STEP 1: Basic transcription flow
9
+ * STEP 2: Mic auto-stop upon correct answer (using partial transcripts)
10
+ * STEP 3: Semantic/function-call outcomes for game actions
11
+ *
12
+ * Ideally this should be part of a more centralized shared type library to free
13
+ * game developers and provide helper functions (VGF? Platform SDK?).
14
+ */
15
+ export declare const RecognitionVGFStateSchema: z.ZodObject<{
16
+ audioUtteranceId: z.ZodString;
17
+ startRecordingStatus: z.ZodOptional<z.ZodString>;
18
+ transcriptionStatus: z.ZodOptional<z.ZodString>;
19
+ finalTranscript: z.ZodOptional<z.ZodString>;
20
+ finalConfidence: z.ZodOptional<z.ZodNumber>;
21
+ asrConfig: z.ZodOptional<z.ZodString>;
22
+ startRecordingTimestamp: z.ZodOptional<z.ZodString>;
23
+ finalRecordingTimestamp: z.ZodOptional<z.ZodString>;
24
+ finalTranscriptionTimestamp: z.ZodOptional<z.ZodString>;
25
+ pendingTranscript: z.ZodDefault<z.ZodOptional<z.ZodString>>;
26
+ pendingConfidence: z.ZodOptional<z.ZodNumber>;
27
+ functionCallMetadata: z.ZodOptional<z.ZodString>;
28
+ functionCallConfidence: z.ZodOptional<z.ZodNumber>;
29
+ finalFunctionCallTimestamp: z.ZodOptional<z.ZodString>;
30
+ promptSlotMap: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>>;
31
+ recognitionActionProcessingState: z.ZodOptional<z.ZodString>;
32
+ }, "strip", z.ZodTypeAny, {
33
+ audioUtteranceId: string;
34
+ pendingTranscript: string;
35
+ startRecordingStatus?: string | undefined;
36
+ transcriptionStatus?: string | undefined;
37
+ finalTranscript?: string | undefined;
38
+ finalConfidence?: number | undefined;
39
+ asrConfig?: string | undefined;
40
+ startRecordingTimestamp?: string | undefined;
41
+ finalRecordingTimestamp?: string | undefined;
42
+ finalTranscriptionTimestamp?: string | undefined;
43
+ pendingConfidence?: number | undefined;
44
+ functionCallMetadata?: string | undefined;
45
+ functionCallConfidence?: number | undefined;
46
+ finalFunctionCallTimestamp?: string | undefined;
47
+ promptSlotMap?: Record<string, string[]> | undefined;
48
+ recognitionActionProcessingState?: string | undefined;
49
+ }, {
50
+ audioUtteranceId: string;
51
+ startRecordingStatus?: string | undefined;
52
+ transcriptionStatus?: string | undefined;
53
+ finalTranscript?: string | undefined;
54
+ finalConfidence?: number | undefined;
55
+ asrConfig?: string | undefined;
56
+ startRecordingTimestamp?: string | undefined;
57
+ finalRecordingTimestamp?: string | undefined;
58
+ finalTranscriptionTimestamp?: string | undefined;
59
+ pendingTranscript?: string | undefined;
60
+ pendingConfidence?: number | undefined;
61
+ functionCallMetadata?: string | undefined;
62
+ functionCallConfidence?: number | undefined;
63
+ finalFunctionCallTimestamp?: string | undefined;
64
+ promptSlotMap?: Record<string, string[]> | undefined;
65
+ recognitionActionProcessingState?: string | undefined;
66
+ }>;
67
+ export type RecognitionState = z.infer<typeof RecognitionVGFStateSchema>;
68
+ export declare const RecordingStatus: {
69
+ readonly NOT_READY: "NOT_READY";
70
+ readonly READY: "READY";
71
+ readonly RECORDING: "RECORDING";
72
+ readonly FINISHED: "FINISHED";
73
+ };
74
+ export type RecordingStatusType = typeof RecordingStatus[keyof typeof RecordingStatus];
75
+ export declare const TranscriptionStatus: {
76
+ readonly NOT_STARTED: "NOT_STARTED";
77
+ readonly IN_PROGRESS: "IN_PROGRESS";
78
+ readonly FINALIZED: "FINALIZED";
79
+ readonly ABORTED: "ABORTED";
80
+ readonly ERROR: "ERROR";
81
+ };
82
+ export type TranscriptionStatusType = typeof TranscriptionStatus[keyof typeof TranscriptionStatus];
83
+ export declare const RecognitionActionProcessingState: {
84
+ readonly NOT_STARTED: "NOT_STARTED";
85
+ readonly IN_PROGRESS: "IN_PROGRESS";
86
+ readonly COMPLETED: "COMPLETED";
87
+ };
88
+ export type RecognitionActionProcessingStateType = typeof RecognitionActionProcessingState[keyof typeof RecognitionActionProcessingState];
89
+ export declare function createInitialRecognitionState(audioUtteranceId: string): RecognitionState;
90
+ export declare function isValidRecordingStatusTransition(from: string | undefined, to: string): boolean;
91
+ //# sourceMappingURL=vgf-recognition-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vgf-recognition-state.d.ts","sourceRoot":"","sources":["../src/vgf-recognition-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6BpC,CAAA;AAEF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAA;AAGxE,eAAO,MAAM,eAAe;;;;;CAKlB,CAAA;AAEV,MAAM,MAAM,mBAAmB,GAAG,OAAO,eAAe,CAAC,MAAM,OAAO,eAAe,CAAC,CAAA;AAEtF,eAAO,MAAM,mBAAmB;;;;;;CAMtB,CAAA;AAEV,MAAM,MAAM,uBAAuB,GAAG,OAAO,mBAAmB,CAAC,MAAM,OAAO,mBAAmB,CAAC,CAAA;AAElG,eAAO,MAAM,gCAAgC;;;;CAInC,CAAA;AAEV,MAAM,MAAM,oCAAoC,GAAG,OAAO,gCAAgC,CAAC,MAAM,OAAO,gCAAgC,CAAC,CAAA;AAGzI,wBAAgB,6BAA6B,CAAC,gBAAgB,EAAE,MAAM,GAAG,gBAAgB,CAQxF;AAGD,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAa9F"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volley/recognition-client-sdk",
3
- "version": "0.1.419",
3
+ "version": "0.1.422",
4
4
  "description": "Recognition Service TypeScript/Node.js Client SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -54,10 +54,10 @@
54
54
  "semantic-release": "25.0.1",
55
55
  "ts-jest": "29.4.5",
56
56
  "typescript": "5.1.6",
57
- "@recog/shared-config": "1.0.0",
57
+ "@recog/shared-types": "1.0.0",
58
58
  "@recog/shared-utils": "1.0.0",
59
59
  "@recog/websocket": "1.0.0",
60
- "@recog/shared-types": "1.0.0"
60
+ "@recog/shared-config": "1.0.0"
61
61
  },
62
62
  "keywords": [
63
63
  "recognition",
@@ -354,9 +354,9 @@ describe('SimplifiedVGFRecognitionClient Integration - State Transitions', () =>
354
354
  expect(stateChangeCounter).toBe(3);
355
355
  expect(trackingCallback).toHaveBeenCalledTimes(3);
356
356
 
357
- // State should remain unchanged (synthetic empty transcript)
357
+ // Local state IS updated with late transcript, but callback was not called
358
358
  const finalState = client.getVGFState();
359
- expect(finalState.finalTranscript).toBe('');
359
+ expect(finalState.finalTranscript).toBe('late server response');
360
360
 
361
361
  // Log should indicate suppression
362
362
  const suppressionLog = loggerCalls.find(log =>
@@ -441,9 +441,10 @@ describe('SimplifiedVGFRecognitionClient Integration - State Transitions', () =>
441
441
  expect(stateChangeCounter).toBe(3);
442
442
  expect(trackingCallback).toHaveBeenCalledTimes(3);
443
443
 
444
- // State should remain ABORTED (not changed to FINALIZED)
444
+ // Local state IS updated (late transcript overwrites ABORTED), but callback was not called
445
445
  const finalState = client.getVGFState();
446
- expect(finalState.transcriptionStatus).toBe(TranscriptionStatus.ABORTED);
446
+ expect(finalState.transcriptionStatus).toBe(TranscriptionStatus.FINALIZED);
447
+ expect(finalState.finalTranscript).toBe('late server response');
447
448
 
448
449
  // Log should indicate suppression
449
450
  const suppressionLog = loggerCalls.find(log =>
@@ -549,9 +550,10 @@ describe('SimplifiedVGFRecognitionClient Integration - State Transitions', () =>
549
550
  expect(stateChangeCounter).toBe(3);
550
551
  expect(trackingCallback).toHaveBeenCalledTimes(3);
551
552
 
552
- // State should remain ERROR
553
+ // Local state IS updated (late transcript overwrites), but callback was not called
553
554
  const finalState = client.getVGFState();
554
- expect(finalState.transcriptionStatus).toBe(TranscriptionStatus.ERROR);
555
+ expect(finalState.transcriptionStatus).toBe(TranscriptionStatus.FINALIZED);
556
+ expect(finalState.finalTranscript).toBe('late server response');
555
557
 
556
558
  // Log should indicate suppression for the late transcript
557
559
  const suppressionLog = loggerCalls.find(log =>
@@ -643,9 +645,9 @@ describe('SimplifiedVGFRecognitionClient Integration - State Transitions', () =>
643
645
  );
644
646
  expect(suppressionLog).toBeDefined();
645
647
 
646
- // Final state should still have original transcript
648
+ // Local state IS updated with new transcript, but callback was not called
647
649
  const finalState = client.getVGFState();
648
- expect(finalState.finalTranscript).toBe('hello world');
650
+ expect(finalState.finalTranscript).toBe('different transcript');
649
651
  });
650
652
 
651
653
  it('should reset terminal status flag when new UUID is generated', () => {
@@ -1492,7 +1492,7 @@ describe('SimplifiedVGFRecognitionClient', () => {
1492
1492
  expect(stateChangeCallback).not.toHaveBeenCalled();
1493
1493
  });
1494
1494
 
1495
- it('should preserve original state after blocking duplicate', () => {
1495
+ it('should update local state but block callback for duplicate terminal', () => {
1496
1496
  // First terminal with specific transcript
1497
1497
  onTranscriptCallback({
1498
1498
  type: 'Transcription',
@@ -1502,7 +1502,9 @@ describe('SimplifiedVGFRecognitionClient', () => {
1502
1502
  is_finished: true
1503
1503
  });
1504
1504
 
1505
- // Attempt second terminal with different transcript
1505
+ stateChangeCallback.mockClear();
1506
+
1507
+ // Second terminal with different transcript
1506
1508
  onTranscriptCallback({
1507
1509
  type: 'Transcription',
1508
1510
  audioUtteranceId: clientUuid,
@@ -1511,10 +1513,13 @@ describe('SimplifiedVGFRecognitionClient', () => {
1511
1513
  is_finished: true
1512
1514
  });
1513
1515
 
1514
- // State should still have original values
1516
+ // Local state should be updated with new values
1515
1517
  const state = simplifiedClient.getVGFState();
1516
- expect(state.finalTranscript).toBe('original');
1517
- expect(state.finalConfidence).toBe(0.85);
1518
+ expect(state.finalTranscript).toBe('different');
1519
+ expect(state.finalConfidence).toBe(0.99);
1520
+
1521
+ // But callback should NOT have been called for the duplicate
1522
+ expect(stateChangeCallback).not.toHaveBeenCalled();
1518
1523
  });
1519
1524
  });
1520
1525
  });
@@ -246,18 +246,7 @@ export class SimplifiedVGFRecognitionClient implements ISimplifiedVGFRecognition
246
246
  return;
247
247
  }
248
248
 
249
- // Skip if terminal status already sent for THIS session
250
- // (If lastSentTerminalUuid exists but doesn't match expectedUuid, treat as new session)
251
- if (this.lastSentTerminalUuid === this.expectedUuid) {
252
- if (this.logger) {
253
- this.logger('info',
254
- `[RecogSDK:VGF] Duplicate terminal status suppressed (lastSentTerminalUuid: ${this.lastSentTerminalUuid})`
255
- );
256
- }
257
- return;
258
- }
259
-
260
- // Update VGF state based on transcript
249
+ // Update VGF state based on transcript (always update local state)
261
250
  this.state = mapTranscriptionResultToState(this.state, result, this.isRecordingAudio);
262
251
  this.notifyStateChange();
263
252
 
@@ -301,11 +290,6 @@ export class SimplifiedVGFRecognitionClient implements ISimplifiedVGFRecognition
301
290
  return;
302
291
  }
303
292
 
304
- // Skip if terminal status already sent for THIS session
305
- if (this.lastSentTerminalUuid === this.expectedUuid) {
306
- return;
307
- }
308
-
309
293
  this.isRecordingAudio = false; // Reset on error
310
294
  this.state = mapErrorToState(this.state, error);
311
295
  this.notifyStateChange();
@@ -111,7 +111,7 @@ describe('MessageHandler', () => {
111
111
  handler.handleMessage(msg);
112
112
  expect(mockLogger).toHaveBeenCalledWith(
113
113
  'debug',
114
- 'Unknown message type',
114
+ '[RecogSDK] Unknown message type',
115
115
  expect.objectContaining({ type: 'unknown' })
116
116
  );
117
117
  });
@@ -129,7 +129,7 @@ describe('MessageHandler', () => {
129
129
  handler.handleMessage(msg);
130
130
  expect(mockLogger).toHaveBeenCalledWith(
131
131
  'debug',
132
- 'Received WebSocket message',
132
+ '[RecogSDK] Received WebSocket message',
133
133
  expect.objectContaining({
134
134
  msgType: 'recognition_result',
135
135
  msgDataType: RecognitionResultTypeV1.TRANSCRIPTION
@@ -147,7 +147,7 @@ describe('MessageHandler', () => {
147
147
  handler.handleMessage(msg);
148
148
  expect(mockLogger).toHaveBeenCalledWith(
149
149
  'error',
150
- 'Received primitive msg.data from server',
150
+ '[RecogSDK] Received primitive msg.data from server',
151
151
  expect.objectContaining({
152
152
  dataType: 'string',
153
153
  data: 'primitive string'
@@ -253,7 +253,7 @@ describe('MessageHandler', () => {
253
253
 
254
254
  expect(mockLogger).toHaveBeenCalledWith(
255
255
  'debug',
256
- 'First transcript received',
256
+ '[RecogSDK] First transcript received',
257
257
  expect.objectContaining({
258
258
  timeToFirstTranscriptMs: expect.any(Number)
259
259
  })