@alxxsck/ai-assistant 2.26.0 → 3.1.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.
Files changed (61) hide show
  1. package/README.md +20 -2
  2. package/dist/{Avatar-Cr3PsHzw.js → Avatar-B9TOQAF_.js} +19082 -18842
  3. package/dist/ChatWidgetContent-BuhVlKYA.js +5376 -0
  4. package/dist/api/audio.api.d.ts +1 -1
  5. package/dist/api/messages.api.types.d.ts +2 -1
  6. package/dist/avatar/AnimationManager.d.ts +7 -3
  7. package/dist/avatar/Avatar.d.ts +20 -3
  8. package/dist/avatar/avatarMeshes.d.ts +8 -0
  9. package/dist/avatar/loaders.d.ts +16 -0
  10. package/dist/bridge-ai-chat-widget.es.js +1 -1
  11. package/dist/constants.d.ts +1 -0
  12. package/dist/context/ChatWidgetContext.d.ts +3 -2
  13. package/dist/domain/command/CommandRuntime.d.ts +9 -2
  14. package/dist/domain/command/UiCommandService.d.ts +4 -2
  15. package/dist/domain/command/command.types.d.ts +8 -0
  16. package/dist/domain/message/cable/CableContext.d.ts +1 -0
  17. package/dist/domain/message/message.store.d.ts +7 -0
  18. package/dist/domain/scenario-interaction/scenario-interaction.types.d.ts +3 -0
  19. package/dist/domain/speech/SpeechPlayer.d.ts +2 -0
  20. package/dist/domain/ui/widget-geometry.d.ts +7 -5
  21. package/dist/domain/voice/media/ReliableVoiceUplink.d.ts +4 -0
  22. package/dist/domain/voice/voice-audio-meter.d.ts +12 -0
  23. package/dist/domain/voice/voice-input-spectrum.d.ts +17 -0
  24. package/dist/domain/voice/voice.store.d.ts +5 -1
  25. package/dist/i18n/messages.d.ts +6 -2
  26. package/dist/index-DCsPGgIk.js +2891 -0
  27. package/dist/index.d.ts +2 -1
  28. package/dist/index.types.d.ts +4 -2
  29. package/dist/inter-cyrillic-wght-italic.woff2 +0 -0
  30. package/dist/inter-cyrillic-wght-normal.woff2 +0 -0
  31. package/dist/inter-latin-ext-wght-italic.woff2 +0 -0
  32. package/dist/inter-latin-ext-wght-normal.woff2 +0 -0
  33. package/dist/inter-latin-wght-italic.woff2 +0 -0
  34. package/dist/inter-latin-wght-normal.woff2 +0 -0
  35. package/dist/{mount-DCjHLj_e.js → mount-BpvW4tbc.js} +9631 -5577
  36. package/dist/roboto-cyrillic-400-normal.woff2 +0 -0
  37. package/dist/roboto-cyrillic-700-normal.woff2 +0 -0
  38. package/dist/roboto-latin-400-normal.woff2 +0 -0
  39. package/dist/roboto-latin-700-normal.woff2 +0 -0
  40. package/dist/roboto-latin-ext-400-normal.woff2 +0 -0
  41. package/dist/roboto-latin-ext-700-normal.woff2 +0 -0
  42. package/dist/ui/components/ChatInlineProgressStatus.d.ts +5 -0
  43. package/dist/ui/components/ChatListItem.d.ts +4 -1
  44. package/dist/ui/components/ChatSurfaceFrameContext.d.ts +6 -0
  45. package/dist/ui/components/ChatTopControls.d.ts +5 -5
  46. package/dist/ui/components/ChatUiActionProgress.d.ts +1 -0
  47. package/dist/ui/components/ChatVoiceMode.d.ts +5 -1
  48. package/dist/ui/components/ChatWidgetFrame.d.ts +5 -3
  49. package/dist/ui/components/VoiceComposer.d.ts +23 -0
  50. package/dist/ui/components/voice-waveform.d.ts +20 -0
  51. package/dist/ui/error-messages.d.ts +1 -0
  52. package/dist/ui/responder-visual-state.d.ts +7 -0
  53. package/dist/widget-fonts-CFulYNmT.js +16 -0
  54. package/dist/widget-fonts.d.ts +1 -0
  55. package/package.json +6 -2
  56. package/dist/ChatWidgetContent-DJ2Y9kKV.js +0 -8045
  57. package/dist/avatar/consolidateMaterialGroups.d.ts +0 -2
  58. package/dist/index-D01wjcEK.js +0 -2809
  59. package/dist/ui/chat-composer-layout.d.ts +0 -1
  60. package/dist/ui/components/overlays/ChatBlurOverlayTop.d.ts +0 -1
  61. package/dist/ui/components/overlays/ChatDarkOverlay.d.ts +0 -1
@@ -16,5 +16,5 @@ export declare class AudioApiError extends Error {
16
16
  readonly status: number;
17
17
  constructor(status: number, message: string);
18
18
  }
19
- export declare const fetchProtectedAudio: (config: WidgetConfig, api: AppApi, request: ProtectedAudioRequest) => Promise<Blob>;
19
+ export declare const fetchProtectedAudio: (config: WidgetConfig, api: AppApi, request: ProtectedAudioRequest, signal?: AbortSignal) => Promise<Blob>;
20
20
  export declare const fetchProtectedAudioStream: (config: WidgetConfig, api: AppApi, request: StreamingAudioRequest, signal: AbortSignal) => Promise<ReadableStream<Uint8Array>>;
@@ -31,7 +31,8 @@ export type MessageDelivery = {
31
31
  export declare enum MessageStatus {
32
32
  Completed = "MESSAGE_STATUS_COMPLETED",
33
33
  Writing = "MESSAGE_STATUS_WRITING",
34
- Failed = "MESSAGE_STATUS_FAILED"
34
+ Failed = "MESSAGE_STATUS_FAILED",
35
+ Cancelled = "MESSAGE_STATUS_CANCELLED"
35
36
  }
36
37
  export type CustomerContent = {
37
38
  text: string;
@@ -1,5 +1,6 @@
1
- import { Object3D, Group } from "three";
1
+ import { Object3D } from "three";
2
2
  import type { Viseme } from "@/domain/speech/LipSyncRuntime";
3
+ import { type AnimatedAsset } from "./loaders";
3
4
  import type { Avatar } from "./Avatar";
4
5
  export declare const BLEND_SHAPE_KEYS: readonly ["blendShape1.AE", "blendShape1.ChJ", "blendShape1.FV", "blendShape1.Oh", "blendShape1.Ah", "blendShape1.Ih", "blendShape1.L", "blendShape1.SZ", "blendShape1.Woo", "blendShape1.MBP", "blendShape1.Smile_Closed", "blendShape1.Smile_Open", "blendShape1.Kiss"];
5
6
  export type BlendShapeKey = (typeof BLEND_SHAPE_KEYS)[number];
@@ -16,7 +17,7 @@ type InitialAnimationAssets = {
16
17
  animationData: AnimationAssetsData;
17
18
  mandatoryAnimations: Array<{
18
19
  data: AnimationAsset;
19
- animation: Group;
20
+ animation: AnimatedAsset;
20
21
  }>;
21
22
  };
22
23
  export default class AnimationManager {
@@ -29,9 +30,11 @@ export default class AnimationManager {
29
30
  private lazyAnimations;
30
31
  private animationLoads;
31
32
  private loadingManager;
32
- private fbxLoader;
33
+ private animationLoader;
33
34
  private initialAssets;
34
35
  private backgroundPreloadCancelled;
36
+ private backgroundPreloadGeneration;
37
+ private backgroundPreloadComplete;
35
38
  private latestActionRequest;
36
39
  private pendingActionLoads;
37
40
  private idleFallbackPending;
@@ -47,6 +50,7 @@ export default class AnimationManager {
47
50
  private loadAnimationLazy;
48
51
  loadAction(name: string): Promise<void>;
49
52
  cancelBackgroundPreload(): void;
53
+ setBackgroundPreloadEnabled(enabled: boolean): void;
50
54
  private scheduleBackgroundPreload;
51
55
  private waitForBackgroundOpportunity;
52
56
  private preloadRemainingAnimations;
@@ -1,5 +1,5 @@
1
1
  import { Mesh, Vector3 } from "three";
2
- import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader.js";
2
+ import type { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
3
3
  import type { AppApi } from "@/App";
4
4
  import type { LipSyncSnapshot } from "@/domain/speech/LipSyncRuntime";
5
5
  import type { ExternallyAvailableActions } from "./publicAnimations";
@@ -9,12 +9,18 @@ type CameraParams = {
9
9
  lookAt: Vector3;
10
10
  fov: number;
11
11
  };
12
+ export type AvatarFramingMode = "default" | "chat-surface";
12
13
  export declare class Avatar extends EventTarget {
13
14
  private canvas;
14
15
  private videoContainer;
15
16
  api: AppApi;
16
- initialAssetsfbxLoader: FBXLoader;
17
+ initialAssetsLoader: GLTFLoader;
17
18
  head: Mesh;
19
+ /**
20
+ * The brows are a separate primitive in glTF but share the head's blend
21
+ * shapes, so they have to follow the same influences array. See syncFaceMorphs.
22
+ */
23
+ private brows?;
18
24
  private renderer;
19
25
  private sound?;
20
26
  private cubeTexture;
@@ -26,6 +32,7 @@ export declare class Avatar extends EventTarget {
26
32
  private body;
27
33
  private animationManager;
28
34
  private cameraParams;
35
+ private framingMode;
29
36
  private locale?;
30
37
  private animationFrame;
31
38
  private renderingEnabled;
@@ -33,6 +40,7 @@ export declare class Avatar extends EventTarget {
33
40
  constructor(canvas: HTMLCanvasElement, videoContainer: HTMLDivElement, api: AppApi);
34
41
  private lightAmbient;
35
42
  private lightDirectional;
43
+ private lightRim;
36
44
  private loadingManager;
37
45
  private criticalAssetsReady;
38
46
  private sounds;
@@ -40,13 +48,22 @@ export declare class Avatar extends EventTarget {
40
48
  init(): Promise<void>;
41
49
  initSounds(locale: string): void;
42
50
  private tempLookAt;
43
- setCamera(params: CameraParams, cb?: () => void): void;
51
+ setCamera(params: CameraParams, cb?: () => void, duration?: number): void;
52
+ setFramingMode(mode: AvatarFramingMode): void;
44
53
  private width;
45
54
  private height;
46
55
  onResize: (width?: number, height?: number) => void;
47
56
  animate: () => void;
57
+ /**
58
+ * The head and the brows are two meshes sharing one set of blend shapes.
59
+ * Everything that drives the face (lip sync, blend shape scenarios, gsap
60
+ * tweens) writes into `head.morphTargetInfluences`, and some of it swaps the
61
+ * array outright, so the brows follow the head's current array every frame.
62
+ */
63
+ private syncFaceMorphs;
48
64
  setRenderingEnabled(enabled: boolean): void;
49
65
  cancelBackgroundPreload(): void;
66
+ setBackgroundPreloadEnabled(enabled: boolean): void;
50
67
  waitForCriticalAssets(): Promise<void>;
51
68
  private SPEAKING_ANIMATION_NAME_1;
52
69
  private SPEAKING_ANIMATION_NAME_2;
@@ -0,0 +1,8 @@
1
+ import type { Object3D, SkinnedMesh } from "three";
2
+ export type AvatarMeshSlot = "eyes" | "hair" | "head" | "brows" | "shell" | "body";
3
+ export type AvatarMeshes = Partial<Record<AvatarMeshSlot, SkinnedMesh>>;
4
+ /** Maps the meshes of a loaded avatar rig onto the slots the renderer knows about. */
5
+ export declare function collectAvatarMeshes(root: Object3D): AvatarMeshes;
6
+ /** Slots the avatar cannot render without. */
7
+ export declare const REQUIRED_AVATAR_MESH_SLOTS: AvatarMeshSlot[];
8
+ export declare function findMissingAvatarMeshes(meshes: AvatarMeshes): AvatarMeshSlot[];
@@ -0,0 +1,16 @@
1
+ import type { AnimationClip, LoadingManager } from "three";
2
+ import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
3
+ /**
4
+ * Anything the avatar loads carries animation clips: the rig itself has none,
5
+ * an animation asset has exactly one. Narrowing to this shape keeps the call
6
+ * sites independent of whether a GLTF or a plain Object3D came back.
7
+ */
8
+ export type AnimatedAsset = {
9
+ animations: AnimationClip[];
10
+ };
11
+ /**
12
+ * Avatar assets are glTF binaries compressed with EXT_meshopt_compression
13
+ * (see scripts/asset-pipeline/optimize-assets.mjs). The decoder ships its
14
+ * WebAssembly inline, so no extra file has to be served.
15
+ */
16
+ export declare function createAvatarLoader(manager?: LoadingManager): GLTFLoader;
@@ -1,4 +1,4 @@
1
- import { C as A, E as e, S as s, d as E, c } from "./index-D01wjcEK.js";
1
+ import { C as A, E as e, S as s, d as E, c } from "./index-DCsPGgIk.js";
2
2
  export {
3
3
  A as ChatWidgetInstance,
4
4
  e as EXTERNALLY_AVAILABLE_ACTIONS,
@@ -1,2 +1,3 @@
1
1
  export declare const SM_BREAKPOINT = 640;
2
+ export declare function isPhoneViewport(): boolean;
2
3
  export declare const SHOW_REACTION_MESSAGE_DURATION = 5000;
@@ -3,6 +3,7 @@ import type { Avatar } from "@/avatar/Avatar";
3
3
  import type { WidgetConfig } from "@/index.types";
4
4
  import type { CtaActionExecutionResult, CtaCommandContext } from "@/domain/command/command.types";
5
5
  import type { SurfaceTransitionContext } from "@/domain/scenario-interaction/scenario-interaction.types";
6
+ import type { SurfaceFocusDisposition } from "@/domain/scenario-interaction/scenario-interaction.types";
6
7
  import { CompactPreviewController } from "@/domain/compact-preview/CompactPreviewController";
7
8
  type ChatWidgetContextValue = {
8
9
  open: boolean;
@@ -10,8 +11,8 @@ type ChatWidgetContextValue = {
10
11
  transitionSurface: (open: boolean, context: SurfaceTransitionContext) => void;
11
12
  openCompactPreview: (context: SurfaceTransitionContext) => Promise<boolean> | null;
12
13
  surfaceEpisodeId: string | null;
13
- isFullscreen: boolean;
14
- setFullscreen: (fullscreen: boolean) => void;
14
+ closeFocusDisposition: SurfaceFocusDisposition;
15
+ uiActionHandoffPending: boolean;
15
16
  contentActivated: boolean;
16
17
  isAvatarCoveredByChatList: boolean;
17
18
  setAvatarCoveredByChatList: (covered: boolean) => void;
@@ -1,7 +1,12 @@
1
1
  import type { CustomerInteractionSession } from "@/types";
2
- import { type CommandAck, type CommandEnvelope, type CommandEventDetail, type CtaActionExecutionResult, type CtaCommandContext, type HostCommandHandlerName, type HostCommandHandlers, type SpeakTextCommandContext, type StartVoiceConversationCommandContext, type UiTarget } from "./command.types";
2
+ import { type CommandAck, type CommandEnvelope, type CommandEventDetail, type CtaActionExecutionResult, type CtaCommandContext, type HostCommandHandlerName, type HostCommandHandlers, type SpeakTextCommandContext, type StartVoiceConversationCommandContext, type UiCommandActionSnapshot, type UiActionPolicies, type UiTarget } from "./command.types";
3
3
  import type { ScenarioInteractionRuntime } from "@/domain/scenario-interaction/ScenarioInteractionRuntime";
4
4
  type AckSender = (ack: CommandAck) => Promise<unknown>;
5
+ type UiActionInvocation = "COMMAND" | "CTA";
6
+ type UiActionPresentationHandoff = {
7
+ complete: () => void;
8
+ cancel: () => void;
9
+ };
5
10
  type InternalHandlers = {
6
11
  setAssistantVisible?: (visible: boolean, commandId: string) => void;
7
12
  setChatOpen?: (open: boolean, commandId: string) => void | Promise<void>;
@@ -10,6 +15,7 @@ type InternalHandlers = {
10
15
  connectTelegram?: (context: CtaCommandContext) => CtaActionExecutionResult | Promise<CtaActionExecutionResult>;
11
16
  speakText?: (context: SpeakTextCommandContext) => void | Promise<void>;
12
17
  startVoiceConversation?: (context: StartVoiceConversationCommandContext) => void | Promise<void>;
18
+ prepareUiActionHandoff?: (action: Readonly<UiCommandActionSnapshot>, commandId: string, invocation: UiActionInvocation) => UiActionPresentationHandoff | undefined;
13
19
  };
14
20
  export declare class CommandRuntime {
15
21
  private readonly onStatus?;
@@ -32,7 +38,7 @@ export declare class CommandRuntime {
32
38
  private principalSessionId;
33
39
  private principalGeneration;
34
40
  private destroyed;
35
- constructor(targets?: Record<string, UiTarget>, handlers?: HostCommandHandlers, onStatus?: ((detail: CommandEventDetail) => void) | undefined, scenarioInteractions?: ScenarioInteractionRuntime | undefined);
41
+ constructor(targets?: Record<string, UiTarget>, handlers?: HostCommandHandlers, onStatus?: ((detail: CommandEventDetail) => void) | undefined, scenarioInteractions?: ScenarioInteractionRuntime | undefined, actionPolicies?: UiActionPolicies);
36
42
  registerTarget(code: string, target: UiTarget): () => void;
37
43
  registerHostHandler<K extends HostCommandHandlerName>(name: K, handler: NonNullable<HostCommandHandlers[K]>): () => void;
38
44
  setInternalHandlers(handlers: InternalHandlers): () => void;
@@ -40,6 +46,7 @@ export declare class CommandRuntime {
40
46
  updateSession(session: CustomerInteractionSession): void;
41
47
  executeCtaAction(context: CtaCommandContext): Promise<CtaActionExecutionResult>;
42
48
  private executeCtaActionOnce;
49
+ private executeUiAction;
43
50
  private retainCtaAction;
44
51
  destroy(): void;
45
52
  private executeOnce;
@@ -1,9 +1,10 @@
1
- import type { HostCommandHandlerName, HostCommandHandlers, UiCommandActionSnapshot, UiCommandTarget, UiTarget } from "./command.types";
1
+ import type { HostCommandHandlerName, HostCommandHandlers, UiCommandActionSnapshot, UiCommandTarget, UiActionPolicies, UiTarget } from "./command.types";
2
2
  type NavigationCommandType = "open_page" | "open_modal";
3
3
  export declare class UiCommandService {
4
+ private readonly actionPolicies;
4
5
  private readonly targets;
5
6
  private readonly handlers;
6
- constructor(targets?: Record<string, UiTarget>, handlers?: HostCommandHandlers);
7
+ constructor(targets?: Record<string, UiTarget>, handlers?: HostCommandHandlers, actionPolicies?: UiActionPolicies);
7
8
  registerTarget(code: string, target: UiTarget): () => void;
8
9
  getTarget(code: string): UiTarget | undefined;
9
10
  getHandler<K extends HostCommandHandlerName>(name: K): HostCommandHandlers[K];
@@ -16,6 +17,7 @@ export declare class UiCommandService {
16
17
  private resolveLegacyAction;
17
18
  private assertLegacyTarget;
18
19
  private targetUrl;
20
+ private mobileDisposition;
19
21
  private applyUrlFallback;
20
22
  private isSameOriginUrl;
21
23
  private requireNonEmptyString;
@@ -44,6 +44,12 @@ export type ElementCommandTarget = {
44
44
  };
45
45
  export type UiCommandTarget = PageCommandTarget | ModalCommandTarget | ElementCommandTarget;
46
46
  export type UiTargetKind = "button" | "element" | "modal" | "page";
47
+ export declare const MOBILE_ACTION_DISPOSITIONS: readonly ["keep", "collapseOnSuccess", "immersive"];
48
+ export type MobileActionDisposition = (typeof MOBILE_ACTION_DISPOSITIONS)[number];
49
+ export type UiActionPolicy = {
50
+ mobileDisposition: MobileActionDisposition;
51
+ };
52
+ export type UiActionPolicies = Readonly<Record<string, UiActionPolicy>>;
47
53
  export type UiTarget = {
48
54
  kind: UiTargetKind;
49
55
  /** Trusted selector supplied by the host application. */
@@ -79,10 +85,12 @@ export type UiCommandActionSnapshot = {
79
85
  kind: "page";
80
86
  code: string;
81
87
  route: string;
88
+ mobileDisposition: MobileActionDisposition;
82
89
  target?: PageCommandTarget;
83
90
  } | {
84
91
  kind: "modal";
85
92
  code: string;
93
+ mobileDisposition: MobileActionDisposition;
86
94
  modalName?: string;
87
95
  url?: string;
88
96
  selector?: string;
@@ -13,6 +13,7 @@ interface CableContextType {
13
13
  anycableService: AnyCableService | null;
14
14
  endUserTypingClient: EndUserTypingClient;
15
15
  networkStatus: NetworkStatus;
16
+ networkStatusChangedAt: number;
16
17
  scenarioChoiceSubmissionController: ScenarioChoiceSubmissionController;
17
18
  }
18
19
  export type CableServiceFactory = (url: string, onMessage?: (message: unknown) => void, onAuthenticationError?: () => void) => AnyCableService;
@@ -3,11 +3,14 @@ import type { AppApi } from "@/App";
3
3
  import type { WidgetConfig } from "@/index.types";
4
4
  import { type MessageStreamState } from "./message-stream";
5
5
  import type { ChatAttachment, ChatTypingState } from "./chat-experience";
6
+ import { type SafeResponderPresentation } from "@/domain/support-presentation/support-presentation.types";
6
7
  import type { ResponderPresentationChanged, ScenarioConversationCreated } from "./cable/widget-message";
7
8
  import { type ScenarioChoiceLifecycleChanged, type ScenarioChoiceSubmissionResult } from "@/domain/scenario/scenario-choice";
8
9
  export interface ThreadSummary {
9
10
  threadId: string;
11
+ title: string | null;
10
12
  lastMessage: MessageData;
13
+ responderPresentation: SafeResponderPresentation;
11
14
  }
12
15
  export type AgentResponsePhase = "idle" | "awaiting-first-token" | "streaming";
13
16
  export type ConversationNavigationRequest = {
@@ -41,6 +44,7 @@ export type MessageStore = {
41
44
  threads: Map<string, MessageData[]>;
42
45
  threadsPreviews: ThreadSummary[];
43
46
  sendError: string | null;
47
+ sendErrorOccurredAt: number | null;
44
48
  config: WidgetConfig | null;
45
49
  api: AppApi | null;
46
50
  latestReadMessageDate: string | null;
@@ -54,6 +58,7 @@ export type MessageStore = {
54
58
  pendingCustomerMessage: PendingCustomerMessage | null;
55
59
  responsePhase: AgentResponsePhase;
56
60
  sendInFlight: boolean;
61
+ isCreatingConversation: boolean;
57
62
  typingByConversation: Map<string, ChatTypingState>;
58
63
  concealedConversationIds: Set<string>;
59
64
  concealedMessageIds: Set<string>;
@@ -127,10 +132,12 @@ export declare const useLatestMessageDate: () => string | null;
127
132
  export declare const useConversationNavigationRequest: () => ConversationNavigationRequest | null;
128
133
  export declare const useScenarioChoiceSubmissionResult: (clientEventId?: string) => ScenarioChoiceSubmissionResult | undefined;
129
134
  export declare const useSendError: () => string | null;
135
+ export declare const useSendErrorOccurredAt: () => number | null;
130
136
  export declare const selectIsCurrentThreadLoading: (state: MessageStore) => boolean;
131
137
  export declare const useIsCurrentThreadLoading: () => boolean;
132
138
  export declare const useIsAwaitingFirstToken: () => boolean;
133
139
  export declare const useIsResponseActive: () => boolean;
140
+ export declare const useIsCreatingConversation: () => boolean;
134
141
  export declare const selectCurrentThreadPendingCustomerMessage: (state: MessageStore) => PendingCustomerMessage | null;
135
142
  export declare const usePendingCustomerMessage: () => PendingCustomerMessage | null;
136
143
  export declare const useMessageStream: (messageId: string) => MessageStreamState | undefined;
@@ -59,10 +59,13 @@ export type SurfaceState = "OPEN" | "CLOSED";
59
59
  export type SurfaceTransitionActor = ScenarioInteractionActor | "SYSTEM";
60
60
  /** Exact SDK-local reasons; state-changing reasons are preserved on the wire. */
61
61
  export type SurfaceTransitionReason = "USER_LAUNCHER" | "USER_CLOSE_BUTTON" | "VOICE_SWITCH_TO_TEXT" | "HOST_SET_OPEN" | "SCENARIO_COMMAND" | "SYSTEM_RESTORE";
62
+ export type SurfaceFocusDisposition = "RETURN_TO_LAUNCHER" | "PRESERVE_HOST";
62
63
  export type SurfaceTransitionContext = {
63
64
  actor: SurfaceTransitionActor;
64
65
  reason: SurfaceTransitionReason;
65
66
  sourceCommandId?: string;
67
+ /** Local focus ownership; it is never serialized to interaction observations. */
68
+ focusDisposition?: SurfaceFocusDisposition;
66
69
  };
67
70
  export type SurfaceTransition = {
68
71
  changed: boolean;
@@ -1,6 +1,8 @@
1
1
  export type SpeechPlaybackOptions = {
2
2
  onStarted?: () => void;
3
3
  onStopped?: (completed: boolean) => void;
4
+ onLevel?: (level: number) => void;
5
+ levelContext?: AudioContext;
4
6
  };
5
7
  export type PcmStreamLoader = (signal: AbortSignal) => Promise<ReadableStream<Uint8Array>>;
6
8
  export type PcmPlaybackOptions = SpeechPlaybackOptions & {
@@ -31,10 +31,11 @@ export type CompactPreviewLayout = {
31
31
  export declare const LAUNCHER_SIZE = 52;
32
32
  export declare const LAUNCHER_EDGE_INSET = 24;
33
33
  export declare const VIEWPORT_MARGIN = 16;
34
- export declare const MIN_WIDGET_WIDTH = 240;
35
- export declare const DEFAULT_WIDGET_WIDTH = 360;
36
- export declare const MIN_WIDGET_HEIGHT = 320;
34
+ export declare const CHAT_SURFACE_ASPECT_RATIO: number;
35
+ export declare const MIN_WIDGET_HEIGHT = 519;
36
+ export declare const MIN_WIDGET_WIDTH: number;
37
37
  export declare const DEFAULT_WIDGET_HEIGHT = 680;
38
+ export declare const DEFAULT_WIDGET_WIDTH: number;
38
39
  export declare const COMPACT_PREVIEW_MAX_WIDTH = 320;
39
40
  export declare const COMPACT_PREVIEW_MARGIN = 12;
40
41
  export declare const COMPACT_PREVIEW_GAP = 12;
@@ -45,7 +46,8 @@ export declare function dockLauncherPosition(point: Point, viewport: Viewport):
45
46
  export declare function launcherPointFromDock(launcher: DockedLauncher, viewport: Viewport): Point;
46
47
  export declare function clampLauncherPosition(point: Point, viewport: Viewport): Point;
47
48
  export declare function clampPanelRect(rect: Rect, viewport: Viewport): Rect;
49
+ export declare function dockPanelRect(rect: Rect, viewport: Viewport): Rect;
50
+ export declare function dockPanelRectToSide(rect: Rect, viewport: Viewport, side: LauncherSide): Rect;
48
51
  export declare function placePanelFromLauncher(launcher: Point, size: Size, viewport: Viewport): Rect;
49
52
  export declare function placeCompactPreview(launcher: Point, measuredSize: Size, viewport: Viewport, side: LauncherSide, avoidRect?: Rect | null): CompactPreviewLayout;
50
- export declare function resizePanelFromNorthWest(start: Rect, delta: Point, axes: "top" | "left" | "corner", viewport: Viewport): Rect;
51
- export declare function resizePanelFromSouthEast(start: Rect, delta: Point, viewport: Viewport): Rect;
53
+ export declare function resizePanelFromTopCorner(start: Rect, delta: Point, corner: "northWest" | "northEast", viewport: Viewport): Rect;
@@ -6,10 +6,14 @@ export declare class ReliableVoiceUplink {
6
6
  private readonly queue;
7
7
  private sending;
8
8
  private closed;
9
+ private enabled;
10
+ private epoch;
9
11
  private firstCaptureSample;
10
12
  private firstDispatchAtMs;
11
13
  constructor(send: (chunk: CapturedAudioChunk) => Promise<VoiceAudioAcknowledgement>, onFailure: (error: Error) => void);
14
+ get isAcceptingInput(): boolean;
12
15
  enqueue(chunk: CapturedAudioChunk): void;
16
+ setEnabled(enabled: boolean): void;
13
17
  close(): void;
14
18
  private drain;
15
19
  private waitForPacing;
@@ -0,0 +1,12 @@
1
+ export type VoiceAudioMeterChannel = "input" | "output";
2
+ type VoiceAudioMeterListener = (level: number) => void;
3
+ export declare class VoiceAudioMeter {
4
+ private readonly levels;
5
+ private readonly listeners;
6
+ write(channel: VoiceAudioMeterChannel, value: unknown): void;
7
+ read(channel: VoiceAudioMeterChannel): number;
8
+ subscribe(channel: VoiceAudioMeterChannel, listener: VoiceAudioMeterListener): () => void;
9
+ reset(channel?: VoiceAudioMeterChannel): void;
10
+ }
11
+ export declare const voiceAudioMeter: VoiceAudioMeter;
12
+ export {};
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Owns the optional visualization branch of the current microphone graph.
3
+ * Capture remains in the AudioWorklet; this object only reads the browser's
4
+ * native FFT into caller-owned buffers.
5
+ */
6
+ export declare class VoiceInputSpectrum {
7
+ private graph;
8
+ private cachedBandCount;
9
+ private bandStarts;
10
+ private bandEnds;
11
+ attach(context: AudioContext, source: AudioNode): void;
12
+ detach(context?: AudioContext): void;
13
+ readBands(target: Float32Array): void;
14
+ private invalidateBandGeometry;
15
+ private ensureBandGeometry;
16
+ }
17
+ export declare const voiceInputSpectrum: VoiceInputSpectrum;
@@ -1,6 +1,7 @@
1
1
  import { type Socket } from "socket.io-client";
2
2
  import type { AppApi } from "@/App";
3
3
  import type { VoiceSessionData } from "@/api/voice.api.types";
4
+ import { ReliableVoiceUplink } from "./media/ReliableVoiceUplink";
4
5
  import type { SpeakTextCommandContext, StartVoiceConversationCommandContext } from "@/domain/command/command.types";
5
6
  import type { WidgetConfig } from "@/index.types";
6
7
  type VoiceStatus = "idle" | "connecting" | "listening" | "speaking" | "error";
@@ -16,20 +17,23 @@ type VoiceStore = {
16
17
  localStream: MediaStream | null;
17
18
  inputAudioContext: AudioContext | null;
18
19
  inputAudioNode: AudioWorkletNode | null;
20
+ inputAudioUplink: ReliableVoiceUplink | null;
19
21
  pendingInvitation: StartVoiceConversationCommandContext | null;
20
22
  customerMessageTranscript: string;
21
23
  customerMessageTranscriptVisible: boolean;
22
24
  assistantMessageTranscript: string;
23
25
  assistantMessageTranscriptVisible: boolean;
24
26
  assistantResponseInProgress: boolean;
27
+ assistantAudioPlaying: boolean;
25
28
  microphoneEnabled: boolean;
29
+ microphoneMutedByUser: boolean;
26
30
  generation: number;
27
31
  actions: VoiceStoreActions;
28
32
  };
29
33
  type VoiceStoreActions = {
30
34
  updateSession: (config: WidgetConfig, api: AppApi) => Promise<void>;
31
35
  startListening: (config: WidgetConfig, api: AppApi) => Promise<void>;
32
- offerConversation: (invitation: StartVoiceConversationCommandContext) => Promise<void>;
36
+ offerConversation: (invitation: StartVoiceConversationCommandContext, config: WidgetConfig, api: AppApi) => Promise<void>;
33
37
  acceptConversation: (config: WidgetConfig, api: AppApi) => Promise<void>;
34
38
  declineConversation: (config: WidgetConfig, api: AppApi) => Promise<void>;
35
39
  speakText: (config: WidgetConfig, api: AppApi, speech: SpeakTextCommandContext) => Promise<void>;
@@ -14,6 +14,7 @@ declare const en: {
14
14
  };
15
15
  readonly controls: {
16
16
  readonly close: "Close chat";
17
+ readonly collapse: "Collapse chat";
17
18
  readonly move: "Move Lola";
18
19
  readonly exitFullscreen: "Exit fullscreen";
19
20
  readonly enterFullscreen: "Enter fullscreen";
@@ -25,6 +26,7 @@ declare const en: {
25
26
  readonly identity: {
26
27
  readonly support: "Support";
27
28
  readonly system: "System";
29
+ readonly online: "online";
28
30
  readonly aiAssistant: "AI assistant";
29
31
  readonly waiting: "Waiting for a specialist";
30
32
  readonly operator: "Support operator";
@@ -33,8 +35,9 @@ declare const en: {
33
35
  };
34
36
  readonly chat: {
35
37
  readonly back: "Back to chat";
36
- readonly listTitle: "Chats";
38
+ readonly listTitle: "Chat history";
37
39
  readonly new: "New chat";
40
+ readonly createNew: "Create new chat";
38
41
  readonly loadOlder: "Load older chats";
39
42
  readonly retryOlder: "Retry older chats";
40
43
  readonly olderLoadFailed: "Couldn't load older chats.";
@@ -44,10 +47,11 @@ declare const en: {
44
47
  readonly previousLoadFailed: "Couldn't load previous messages.";
45
48
  readonly loadingOlderMessages: "Loading older messages";
46
49
  readonly loadingMessages: "Loading messages";
50
+ readonly openingDestination: "Opening…";
47
51
  readonly switchToVoice: "Switch to voice chat";
48
52
  readonly talk: "Talk";
49
53
  readonly messageLabel: "Message";
50
- readonly messagePlaceholder: "Message…";
54
+ readonly messagePlaceholder: "Write to Lola";
51
55
  readonly send: "Send message";
52
56
  readonly thinking: "AI is thinking";
53
57
  readonly unread: "Unread messages";