@alxxsck/ai-assistant 2.22.3 → 2.24.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 (41) hide show
  1. package/README.md +50 -12
  2. package/dist/App.d.ts +5 -0
  3. package/dist/{Avatar-QuPBAhUu.js → Avatar-C6dvOio6.js} +1 -1
  4. package/dist/ChatWidgetContent-BYZ7Qq06.js +8059 -0
  5. package/dist/api/chat-attachments.api.d.ts +36 -0
  6. package/dist/api/messages.api.d.ts +3 -1
  7. package/dist/api/messages.api.types.d.ts +14 -1
  8. package/dist/bridge-ai-chat-widget.es.js +3 -3
  9. package/dist/context/ChatWidgetContext.d.ts +12 -3
  10. package/dist/domain/command/CommandRuntime.d.ts +10 -4
  11. package/dist/domain/command/command.types.d.ts +12 -0
  12. package/dist/domain/compact-preview/CompactPreviewController.d.ts +123 -0
  13. package/dist/domain/message/cable/CableContext.d.ts +16 -1
  14. package/dist/domain/message/cable/widget-frame.d.ts +9 -0
  15. package/dist/domain/message/chat-experience.d.ts +16 -1
  16. package/dist/domain/message/message.store.d.ts +3 -2
  17. package/dist/domain/message/unread.d.ts +1 -0
  18. package/dist/domain/scenario/ScenarioChoiceSubmissionController.d.ts +49 -0
  19. package/dist/domain/scenario/scenario-choice.d.ts +6 -0
  20. package/dist/domain/scenario-interaction/ScenarioInteractionRuntime.d.ts +7 -1
  21. package/dist/domain/scenario-interaction/presentation-observer.d.ts +6 -1
  22. package/dist/domain/scenario-interaction/scenario-interaction.types.d.ts +10 -0
  23. package/dist/domain/ui/ui.service.d.ts +6 -5
  24. package/dist/domain/ui/widget-geometry.d.ts +28 -0
  25. package/dist/i18n/messages.d.ts +63 -2
  26. package/dist/index-WDFSbfaV.js +2208 -0
  27. package/dist/index.types.d.ts +15 -4
  28. package/dist/{index-BBsPIKJB.js → mount-CXUq37eu.js} +8636 -7821
  29. package/dist/ui/attachment-preview.d.ts +10 -0
  30. package/dist/ui/components/ChatAttachmentList.d.ts +3 -1
  31. package/dist/ui/components/ChatAttachmentPreviewDialog.d.ts +9 -0
  32. package/dist/ui/components/ChatMessage.d.ts +3 -0
  33. package/dist/ui/components/ChatUnreadIndicator.d.ts +3 -1
  34. package/dist/ui/components/ChatWidgetFrame.d.ts +9 -1
  35. package/dist/ui/components/CompactPreviewSurface.d.ts +8 -0
  36. package/dist/ui/components/CompactScenarioPreview.d.ts +24 -0
  37. package/dist/ui/components/OptimisticCustomerMessage.d.ts +4 -1
  38. package/dist/ui/components/compact-preview-content.d.ts +10 -0
  39. package/dist/ui/hooks/useAttentionInterruption.d.ts +1 -0
  40. package/package.json +1 -1
  41. package/dist/ChatWidgetContent-CZdK6qrV.js +0 -6363
@@ -0,0 +1,36 @@
1
+ import type { AppApi } from "@/App";
2
+ import { type ChatAttachmentTransport } from "@/domain/message/chat-experience";
3
+ import type { WidgetConfig } from "@/index.types";
4
+ export declare const CHAT_ATTACHMENT_MAX_FILES = 10;
5
+ export declare const CHAT_ATTACHMENT_MAX_BYTES: number;
6
+ export declare const CHAT_ATTACHMENT_MAX_TOTAL_BYTES: number;
7
+ export declare const CHAT_ATTACHMENT_CONTENT_TYPES: readonly ["application/pdf", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "application/vnd.openxmlformats-officedocument.presentationml.presentation", "image/jpeg", "image/png", "image/webp", "text/csv", "text/plain"];
8
+ export declare const CHAT_ATTACHMENT_ACCEPT: string;
9
+ type AttachmentContentType = (typeof CHAT_ATTACHMENT_CONTENT_TYPES)[number];
10
+ export declare class ChatAttachmentApiError extends Error {
11
+ readonly code: string;
12
+ readonly status?: number | undefined;
13
+ constructor(code: string, status?: number | undefined);
14
+ }
15
+ export declare const validAttachmentDisplayFilename: (raw: string) => boolean;
16
+ export declare const acceptedAttachmentContentType: (file: Pick<File, "name" | "type">) => AttachmentContentType | null;
17
+ declare const abortableDelay: (durationMs: number, signal?: AbortSignal) => Promise<void>;
18
+ export declare const sha256Hex: (file: Blob) => Promise<string>;
19
+ declare const uploadPresignedFile: ({ file, requiredHeaders, signal, uploadUrl, onProgress, }: {
20
+ file: File;
21
+ requiredHeaders: Record<string, string>;
22
+ signal?: AbortSignal;
23
+ uploadUrl: string;
24
+ onProgress?: (progress: number) => void;
25
+ }) => Promise<void>;
26
+ type BackendTransportDependencies = {
27
+ fetchImpl?: typeof fetch;
28
+ uploadFile?: typeof uploadPresignedFile;
29
+ digest?: typeof sha256Hex;
30
+ wait?: typeof abortableDelay;
31
+ pollIntervalMs?: number;
32
+ maxPolls?: number;
33
+ createId?: () => string;
34
+ };
35
+ export declare const createBackendChatAttachmentTransport: (config: WidgetConfig, api: Pick<AppApi, "onSessionExpired">, dependencies?: BackendTransportDependencies) => ChatAttachmentTransport;
36
+ export {};
@@ -1,7 +1,7 @@
1
1
  import type { AppApi } from "@/App";
2
2
  import type { WidgetConfig } from "@/index.types";
3
3
  import { type ConversationData, type MessageData, type RawConversationData, type RawMessageData, type SendMessageResult } from "./messages.api.types";
4
- import type { ChatAttachment } from "@/domain/message/chat-experience";
4
+ import { type ChatAttachment } from "@/domain/message/chat-experience";
5
5
  export declare const MAX_FETCH_MESSAGES = 100;
6
6
  export type CursorPage<T> = {
7
7
  items: T[];
@@ -28,6 +28,8 @@ export declare const normalizeMessageAuthorPresentation: (role: RawMessageData["
28
28
  presentationVersion: number | null;
29
29
  legacyAvatarUrl: string | null;
30
30
  }> | null;
31
+ export declare const normalizeChatAttachment: (candidate: unknown) => ChatAttachment | null;
32
+ export declare const normalizeChatAttachments: (values: readonly unknown[] | undefined) => ChatAttachment[];
31
33
  export declare const normalizeMessageForSession: (message: RawMessageData, interactionSessionId: string) => MessageData;
32
34
  export declare const listConversations: (config: WidgetConfig, api: AppApi, cursor?: string, signal?: AbortSignal) => Promise<VersionedCursorPage<ConversationData>>;
33
35
  export declare const listConversationMessages: (config: WidgetConfig, api: AppApi, conversationId: string, cursor?: string, signal?: AbortSignal) => Promise<CursorPage<MessageData>>;
@@ -108,6 +108,19 @@ export type RawConversationData = Omit<ConversationData, "responderPresentation"
108
108
  responderPresentation?: unknown;
109
109
  };
110
110
  export type MessageRole = "USER" | "ASSISTANT" | "ADMIN" | "SCENARIO" | "SYSTEM";
111
+ export type RawChatAttachment = {
112
+ id?: unknown;
113
+ displayFilename?: unknown;
114
+ contentType?: unknown;
115
+ sizeBytes?: unknown;
116
+ /** Compatibility fields for custom transports used before durable snapshots. */
117
+ fileName?: unknown;
118
+ mediaType?: unknown;
119
+ size?: unknown;
120
+ kind?: unknown;
121
+ downloadUrl?: unknown;
122
+ previewUrl?: unknown;
123
+ };
111
124
  export type RawMessageData = {
112
125
  id: string;
113
126
  threadId: string;
@@ -119,7 +132,7 @@ export type RawMessageData = {
119
132
  ordinal?: number;
120
133
  clientMessageId?: string | null;
121
134
  author?: unknown;
122
- attachments?: ChatAttachment[];
135
+ attachments?: unknown[];
123
136
  delivery?: MessageDelivery;
124
137
  createdAt: string;
125
138
  updatedAt: string;
@@ -1,8 +1,8 @@
1
- import { a1 as A, a2 as e, a4 as s, F as n, a3 as r } from "./index-BBsPIKJB.js";
1
+ import { C as A, E as e, S as s, d as E, c } from "./index-WDFSbfaV.js";
2
2
  export {
3
3
  A as ChatWidgetInstance,
4
4
  e as EXTERNALLY_AVAILABLE_ACTIONS,
5
5
  s as SUPPORTED_LOCALES,
6
- n as createMockAttachmentTransport,
7
- r as isExternallyAvailableAction
6
+ E as createMockAttachmentTransport,
7
+ c as isExternallyAvailableAction
8
8
  };
@@ -1,12 +1,14 @@
1
1
  import type { AppApi } from "@/App";
2
2
  import type { Avatar } from "@/avatar/Avatar";
3
3
  import type { WidgetConfig } from "@/index.types";
4
- import type { CtaCommandContext } from "@/domain/command/command.types";
4
+ import type { CtaActionExecutionResult, CtaCommandContext } from "@/domain/command/command.types";
5
5
  import type { SurfaceTransitionContext } from "@/domain/scenario-interaction/scenario-interaction.types";
6
+ import { CompactPreviewController } from "@/domain/compact-preview/CompactPreviewController";
6
7
  type ChatWidgetContextValue = {
7
8
  open: boolean;
8
9
  setOpen: (v: boolean) => void;
9
10
  transitionSurface: (open: boolean, context: SurfaceTransitionContext) => void;
11
+ openCompactPreview: (context: SurfaceTransitionContext) => Promise<boolean> | null;
10
12
  surfaceEpisodeId: string | null;
11
13
  isFullscreen: boolean;
12
14
  setFullscreen: (fullscreen: boolean) => void;
@@ -31,8 +33,15 @@ type ChatWidgetContextValue = {
31
33
  ctaTelegramBotUsername: string | null;
32
34
  ctaTelegramDeepLink: string | null;
33
35
  ctaManualCommand: string | null;
34
- executeCta: () => Promise<void>;
35
- dismissCta: () => void;
36
+ hasUnreadCta: boolean;
37
+ compactPreviewController: CompactPreviewController;
38
+ getCtaPresentation: (commandId: string) => {
39
+ busy: boolean;
40
+ buttonLabel: string | null;
41
+ feedback: string | null;
42
+ };
43
+ executeCta: (commandId?: string, compactSurfaceEpisodeId?: string) => Promise<CtaActionExecutionResult>;
44
+ dismissCta: (commandId?: string, compactSurfaceEpisodeId?: string) => boolean;
36
45
  };
37
46
  export declare const ChatWidgetContextProvider: ({ children, config, api, }: {
38
47
  children: React.ReactNode;
@@ -1,13 +1,13 @@
1
1
  import type { CustomerInteractionSession } from "@/types";
2
- import { type CommandAck, type CommandEnvelope, type CommandEventDetail, 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 UiTarget } from "./command.types";
3
3
  import type { ScenarioInteractionRuntime } from "@/domain/scenario-interaction/ScenarioInteractionRuntime";
4
4
  type AckSender = (ack: CommandAck) => Promise<unknown>;
5
5
  type InternalHandlers = {
6
6
  setAssistantVisible?: (visible: boolean, commandId: string) => void;
7
- setChatOpen?: (open: boolean, commandId: string) => void;
7
+ setChatOpen?: (open: boolean, commandId: string) => void | Promise<void>;
8
8
  playAnimation?: (animation: string, expiresAt?: string | null) => void | Promise<void>;
9
9
  showCta?: (context: CtaCommandContext) => void;
10
- connectTelegram?: (context: CtaCommandContext) => void | Promise<void>;
10
+ connectTelegram?: (context: CtaCommandContext) => CtaActionExecutionResult | Promise<CtaActionExecutionResult>;
11
11
  speakText?: (context: SpeakTextCommandContext) => void | Promise<void>;
12
12
  startVoiceConversation?: (context: StartVoiceConversationCommandContext) => void | Promise<void>;
13
13
  };
@@ -20,6 +20,10 @@ export declare class CommandRuntime {
20
20
  private resolveInternalHandlers;
21
21
  private readonly completed;
22
22
  private readonly inFlight;
23
+ private readonly completedCtaActions;
24
+ private readonly terminalCtaActions;
25
+ private readonly ctaActionRetentionOrder;
26
+ private readonly inFlightCtaActions;
23
27
  private readonly highlights;
24
28
  private readonly targetObservationCleanups;
25
29
  private readonly targetActivationOwners;
@@ -34,7 +38,9 @@ export declare class CommandRuntime {
34
38
  setInternalHandlers(handlers: InternalHandlers): () => void;
35
39
  execute(envelope: CommandEnvelope, session: CustomerInteractionSession, sendAck: AckSender): Promise<void>;
36
40
  updateSession(session: CustomerInteractionSession): void;
37
- executeCtaAction(context: CtaCommandContext): Promise<void>;
41
+ executeCtaAction(context: CtaCommandContext): Promise<CtaActionExecutionResult>;
42
+ private executeCtaActionOnce;
43
+ private retainCtaAction;
38
44
  destroy(): void;
39
45
  private executeOnce;
40
46
  private dispatch;
@@ -100,11 +100,23 @@ export type HighlightCommandContext = HostCommandContext & {
100
100
  };
101
101
  export type CtaCommandContext = Omit<HostCommandContext, "target"> & {
102
102
  label: string;
103
+ text?: string;
103
104
  action: "none" | "open_modal" | "open_page" | "connect_telegram";
105
+ conversationId?: string;
106
+ scenarioRunId: string;
107
+ expiresAt: string | null;
108
+ presentation: "PRIMARY";
109
+ attention: "NONE" | "NUDGE_ONCE";
104
110
  target?: string;
105
111
  /** Resolved once when SHOW_CTA arrives and reused unchanged after the click. */
106
112
  uiAction?: Readonly<UiCommandActionSnapshot>;
107
113
  };
114
+ /**
115
+ * User-activation outcome. A terminal failure may be ambiguous: the host side
116
+ * effect could already have happened, so the same command must not be offered
117
+ * for retry.
118
+ */
119
+ export type CtaActionExecutionResult = "COMPLETED" | "AWAITING_CONFIRMATION" | "RETRYABLE_FAILURE" | "TERMINAL_FAILURE";
108
120
  export type StartVoiceConversationCommandContext = {
109
121
  commandId: string;
110
122
  commandType: "start_voice_conversation";
@@ -0,0 +1,123 @@
1
+ import { type MessageData } from "@/api/messages.api.types";
2
+ import type { CtaCommandContext } from "@/domain/command/command.types";
3
+ import { type ScenarioChoiceCommand, type ScenarioChoiceLifecycleChanged } from "@/domain/scenario/scenario-choice";
4
+ export type CompactPreviewKind = "INFO" | "CHOICE" | "CTA";
5
+ export type CompactPreviewDisplayMode = "DIRECT" | "OPEN_IN_CHAT" | "ACTION_ONLY";
6
+ export type CompactPreviewPhase = "IDLE" | "ENTERING" | "INTERACTIVE" | "EXITING" | "GAP";
7
+ export type CompactPreviewExitReason = "DISMISSED" | "COLLAPSED" | "ACTIVATED" | "ANSWERED" | "RESOLVED" | "EXPIRED" | "OPENED_CHAT" | "ROUTING_CHANGED";
8
+ export type CompactPreviewAdmission = "SHOWN" | "QUEUED" | "DUPLICATE" | "QUEUE_OVERFLOW" | "SUPPRESSED_OPEN_CHAT" | "SUPPRESSED_HIDDEN_CHROME" | "SUPPRESSED_HIDDEN_DOCUMENT" | "SUPPRESSED_STARTUP_HISTORY" | "INELIGIBLE" | "EXPIRED" | "RESOLVED";
9
+ export type CompactPreviewMessageSource = "LIVE" | "HISTORY_REPLAY";
10
+ type CompactPreviewCandidateBase = {
11
+ key: string;
12
+ kind: CompactPreviewKind;
13
+ principalSessionId: string;
14
+ conversationId: string;
15
+ scenarioRunId?: string;
16
+ subjectId: string;
17
+ attributionHandle: string;
18
+ messageId?: string;
19
+ text: string;
20
+ leadText: string;
21
+ displayMode: CompactPreviewDisplayMode;
22
+ createdAt: string;
23
+ expiresAt: string | null;
24
+ /** Local monotonic deadline derived from the authoritative server clock. */
25
+ deadlineAtMs: number | null;
26
+ coalescedCount: number;
27
+ };
28
+ export type CompactPreviewInfoCandidate = CompactPreviewCandidateBase & {
29
+ kind: "INFO";
30
+ message: MessageData;
31
+ };
32
+ export type CompactPreviewChoiceCandidate = CompactPreviewCandidateBase & {
33
+ kind: "CHOICE";
34
+ message: MessageData;
35
+ choice: ScenarioChoiceCommand;
36
+ lifecycleRevision: number;
37
+ };
38
+ export type CompactPreviewCtaCandidate = CompactPreviewCandidateBase & {
39
+ kind: "CTA";
40
+ cta: CtaCommandContext;
41
+ };
42
+ export type CompactPreviewCandidate = CompactPreviewInfoCandidate | CompactPreviewChoiceCandidate | CompactPreviewCtaCandidate;
43
+ export type CompactPreviewSnapshot = {
44
+ principalSessionId: string;
45
+ active: CompactPreviewCandidate | null;
46
+ queue: readonly CompactPreviewCandidate[];
47
+ phase: CompactPreviewPhase;
48
+ exitReason: CompactPreviewExitReason | null;
49
+ surfaceEpisodeId: string | null;
50
+ dragSuspended: boolean;
51
+ error: string | null;
52
+ };
53
+ type CompactPreviewRouting = {
54
+ chatOpen: boolean;
55
+ chromeVisible: boolean;
56
+ launcherVisible: boolean;
57
+ };
58
+ type CompactPreviewControllerOptions = {
59
+ enabled: boolean;
60
+ principalSessionId: string;
61
+ createId?: () => string;
62
+ now?: () => number;
63
+ isDocumentVisible?: () => boolean;
64
+ diagnostics?: CompactPreviewDiagnostics;
65
+ };
66
+ type CompactPreviewDiagnostics = (event: string, details?: Readonly<Record<string, unknown>>) => void;
67
+ export declare class CompactPreviewController {
68
+ private state;
69
+ private routing;
70
+ private readonly listeners;
71
+ private readonly dragSuspensionListeners;
72
+ private readonly seen;
73
+ private readonly seenChoiceMessages;
74
+ private readonly scenarioRunsByConversation;
75
+ private readonly createId;
76
+ private readonly now;
77
+ private readonly isDocumentVisible;
78
+ private diagnostics?;
79
+ private enabled;
80
+ private insertionSequence;
81
+ private readonly insertionOrder;
82
+ private resolvedWhileOpeningKey;
83
+ private gestureDragSuspended;
84
+ private dragBaseSnapshot;
85
+ private dragTransientSnapshot;
86
+ constructor(options: CompactPreviewControllerOptions);
87
+ getSnapshot: () => CompactPreviewSnapshot;
88
+ subscribe: (listener: () => void) => (() => void);
89
+ subscribeDragSuspension: (listener: (suspended: boolean) => void) => (() => void);
90
+ setDiagnostics(diagnostics?: CompactPreviewDiagnostics): void;
91
+ setEnabled(enabled: boolean): void;
92
+ setRouting(routing: CompactPreviewRouting): void;
93
+ rememberScenarioConversation(conversationId: string, scenarioRunId?: string): void;
94
+ replacePrincipal(principalSessionId: string): void;
95
+ receiveMessage(message: MessageData, source?: CompactPreviewMessageSource): CompactPreviewAdmission;
96
+ receiveCta(context: CtaCommandContext): CompactPreviewAdmission;
97
+ handleChoiceLifecycle(event: ScenarioChoiceLifecycleChanged): void;
98
+ markInteractive(): void;
99
+ forceOpenInChat(candidateKey: string): void;
100
+ beginExit(reason: CompactPreviewExitReason): void;
101
+ restoreAfterError(message: string): void;
102
+ completeExit(): void;
103
+ promoteNext(): void;
104
+ setDragSuspended(dragSuspended: boolean): void;
105
+ expireDue(): void;
106
+ clear(reason: CompactPreviewExitReason): void;
107
+ private admit;
108
+ private reconcileMessageCandidate;
109
+ private reconcileRemovedChoice;
110
+ private reconcileTerminalChoice;
111
+ private refreshLiveCandidate;
112
+ private rememberSeen;
113
+ private rememberChoiceMessage;
114
+ private infoCandidate;
115
+ private choiceCandidate;
116
+ private key;
117
+ private messageIdentity;
118
+ private sortQueue;
119
+ private report;
120
+ private setState;
121
+ private emit;
122
+ }
123
+ export {};
@@ -4,17 +4,32 @@ import { NetworkStatus } from "./CableContext.types";
4
4
  import type { WidgetConfig } from "@/index.types";
5
5
  import type { CommandRuntime } from "@/domain/command/CommandRuntime";
6
6
  import { EndUserTypingClient } from "@/domain/message/typing/end-user-typing.client";
7
+ import type { MessageData } from "@/api/messages.api.types";
8
+ import type { ScenarioChoiceLifecycleChanged, ScenarioChoiceSubmissionResult } from "@/domain/scenario/scenario-choice";
9
+ import type { CompactPreviewMessageSource } from "@/domain/compact-preview/CompactPreviewController";
10
+ import type { ScenarioConversationCreated } from "./widget-message";
11
+ import { ScenarioChoiceSubmissionController } from "@/domain/scenario/ScenarioChoiceSubmissionController";
7
12
  interface CableContextType {
8
13
  anycableService: AnyCableService | null;
9
14
  endUserTypingClient: EndUserTypingClient;
10
15
  networkStatus: NetworkStatus;
16
+ scenarioChoiceSubmissionController: ScenarioChoiceSubmissionController;
11
17
  }
18
+ export type CableServiceFactory = (url: string, onMessage?: (message: unknown) => void, onAuthenticationError?: () => void) => AnyCableService;
12
19
  interface CableProviderProps {
13
20
  children: ReactNode;
14
21
  config: WidgetConfig;
15
22
  commandRuntime: CommandRuntime;
16
23
  onSessionExpired?: () => void;
24
+ frameHandlers?: CableFrameHandlers;
25
+ createCableService?: CableServiceFactory;
17
26
  }
18
- export declare const CableProvider: ({ children, config, commandRuntime, onSessionExpired, }: CableProviderProps) => import("react/jsx-runtime").JSX.Element;
27
+ export type CableFrameHandlers = {
28
+ onScenarioConversation?: (conversation: ScenarioConversationCreated) => void;
29
+ onRealtimeMessage?: (message: MessageData, source: CompactPreviewMessageSource) => void;
30
+ onScenarioChoiceLifecycle?: (event: ScenarioChoiceLifecycleChanged) => void;
31
+ onScenarioChoiceSubmissionResult?: (result: ScenarioChoiceSubmissionResult) => void;
32
+ };
33
+ export declare const CableProvider: ({ children, config, commandRuntime, onSessionExpired, frameHandlers, createCableService, }: CableProviderProps) => import("react/jsx-runtime").JSX.Element;
19
34
  export declare const useCable: () => CableContextType;
20
35
  export {};
@@ -2,12 +2,21 @@ import type { CustomerInteractionSession } from "@/types";
2
2
  import type { CommandAck } from "@/domain/command/command.types";
3
3
  import type { CommandRuntime } from "@/domain/command/CommandRuntime";
4
4
  import type { MessageStoreActions } from "@/domain/message/message.store";
5
+ import type { MessageData } from "@/api/messages.api.types";
6
+ import type { ScenarioChoiceLifecycleChanged, ScenarioChoiceSubmissionResult } from "@/domain/scenario/scenario-choice";
7
+ import type { CompactPreviewMessageSource } from "@/domain/compact-preview/CompactPreviewController";
8
+ import type { ScenarioConversationCreated } from "./widget-message";
5
9
  type WidgetFrameDependencies = {
6
10
  messageActions: Pick<MessageStoreActions, "handleConversationCreated" | "handleScenarioChoiceLifecycleChanged" | "handleScenarioChoiceSubmissionResult" | "handleMessageDelta" | "handleMessagesAgenticSteps" | "handleMessage" | "handleChatTyping" | "handleResponderPresentationChanged">;
7
11
  commandRuntime: CommandRuntime;
8
12
  session: CustomerInteractionSession;
9
13
  sendAck: (ack: CommandAck) => Promise<unknown>;
10
14
  onPresenceOnline: () => void;
15
+ onScenarioConversation?: (conversation: ScenarioConversationCreated) => void;
16
+ realtimeMessageSource?: CompactPreviewMessageSource;
17
+ onRealtimeMessage?: (message: MessageData, source: CompactPreviewMessageSource) => void;
18
+ onScenarioChoiceLifecycle?: (event: ScenarioChoiceLifecycleChanged) => void;
19
+ onScenarioChoiceSubmissionResult?: (result: ScenarioChoiceSubmissionResult) => void;
11
20
  };
12
21
  export declare const dispatchWidgetFrame: (message: unknown, dependencies: WidgetFrameDependencies) => Promise<void> | null;
13
22
  export {};
@@ -5,16 +5,30 @@ export type ChatAttachment = {
5
5
  mediaType: string;
6
6
  size: number;
7
7
  kind: ChatAttachmentKind;
8
- downloadUrl: string;
8
+ /** Ephemeral local/demo URLs only. Durable backend snapshots never contain URLs. */
9
+ downloadUrl?: string;
9
10
  previewUrl?: string;
10
11
  };
12
+ export type ChatAttachmentUploadStage = "preparing" | "uploading" | "scanning";
11
13
  export type ChatAttachmentUploadInput = {
12
14
  file: File;
15
+ conversationId?: string;
13
16
  signal?: AbortSignal;
14
17
  onProgress?: (progress: number) => void;
18
+ onStage?: (stage: ChatAttachmentUploadStage) => void;
19
+ };
20
+ export type ChatAttachmentAccessInput = {
21
+ attachmentId: string;
22
+ conversationId?: string;
23
+ };
24
+ export type ChatAttachmentGrant = {
25
+ url: string;
26
+ expiresAt: string;
15
27
  };
16
28
  export interface ChatAttachmentTransport {
17
29
  upload(input: ChatAttachmentUploadInput): Promise<ChatAttachment>;
30
+ grant?(input: ChatAttachmentAccessInput): Promise<ChatAttachmentGrant>;
31
+ revoke?(input: ChatAttachmentAccessInput): Promise<void>;
18
32
  }
19
33
  export type ChatTypingState = {
20
34
  conversationId: string;
@@ -28,6 +42,7 @@ export type ChatTypingState = {
28
42
  export declare const normalizeChatExperienceFrame: (frame: unknown) => {
29
43
  typing: ChatTypingState | null;
30
44
  };
45
+ export declare const attachmentKindFor: (mediaType: string) => ChatAttachmentKind;
31
46
  /**
32
47
  * A deterministic transport for visual QA and integration tests. Production
33
48
  * integrators replace it with their signed-upload adapter through WidgetConfig.
@@ -66,15 +66,16 @@ export type MessageStore = {
66
66
  assistantSuspendedConversationIds: Set<string>;
67
67
  actions: MessageStoreActions;
68
68
  };
69
+ export type MessageFrameHandlingResult = "INSERTED" | "UPDATED" | "DUPLICATE" | "IGNORED";
69
70
  export type MessageStoreActions = {
70
71
  initMessageStore: (config: WidgetConfig, api: AppApi) => Promise<void>;
71
72
  disposeMessageStore: () => void;
72
73
  setCurrentThread: (conversationId: string) => Promise<void>;
73
74
  createNewConversation: () => Promise<string>;
74
75
  handleConversationCreated: (conversation: ScenarioConversationCreated) => void;
75
- handleScenarioChoiceLifecycleChanged: (event: ScenarioChoiceLifecycleChanged) => void;
76
+ handleScenarioChoiceLifecycleChanged: (event: ScenarioChoiceLifecycleChanged) => boolean;
76
77
  handleScenarioChoiceSubmissionResult: (result: ScenarioChoiceSubmissionResult) => void;
77
- handleMessage: (message: MessageData) => void;
78
+ handleMessage: (message: MessageData) => MessageFrameHandlingResult;
78
79
  handleResponderPresentationChanged: (hint: ResponderPresentationChanged) => void;
79
80
  refreshResponderPresentation: (conversationId: string, minimumRevision?: string, retryAfterTransportFailure?: boolean) => void;
80
81
  handleMessageDelta: (delta: ChatMessageDeltaData) => void;
@@ -0,0 +1 @@
1
+ export declare function isLatestMessageUnread(latestMessageDate: string | null, latestReadMessageDate: string | null): boolean;
@@ -0,0 +1,49 @@
1
+ import type { ScenarioChoiceInput, ScenarioChoiceLifecycle, ScenarioChoiceSubmissionResult } from "./scenario-choice";
2
+ export type ScenarioChoiceSubmissionKey = Readonly<{
3
+ principalId: string;
4
+ inputRequestId: string;
5
+ }>;
6
+ export type ScenarioChoiceSubmissionRequest = ScenarioChoiceSubmissionKey & Readonly<{
7
+ optionId: string;
8
+ presentationContext?: ScenarioChoiceInput["presentationContext"];
9
+ }>;
10
+ export type ScenarioChoiceSubmissionStatus = "idle" | "submitting" | "error" | "terminal";
11
+ export type ScenarioChoiceSubmissionSnapshot = Readonly<{
12
+ status: ScenarioChoiceSubmissionStatus;
13
+ clientEventId: string | null;
14
+ selectedOptionId: string | null;
15
+ presentationContext: ScenarioChoiceInput["presentationContext"] | null;
16
+ lastOutcome: ScenarioChoiceSubmissionResult["outcome"] | null;
17
+ failure: "network" | "timeout" | null;
18
+ }>;
19
+ export type ScenarioChoiceTransport = (input: ScenarioChoiceInput) => Promise<unknown>;
20
+ export type ScenarioChoiceSubmissionControllerOptions = Readonly<{
21
+ submitChoice: ScenarioChoiceTransport;
22
+ createClientEventId?: () => string;
23
+ confirmationTimeoutMs?: number;
24
+ retryDelayMs?: number;
25
+ }>;
26
+ export declare class ScenarioChoiceSubmissionController {
27
+ private readonly entries;
28
+ private readonly listeners;
29
+ private submitChoice;
30
+ private readonly createClientEventId;
31
+ private readonly confirmationTimeoutMs;
32
+ private readonly retryDelayMs;
33
+ constructor({ submitChoice, createClientEventId, confirmationTimeoutMs, retryDelayMs, }: ScenarioChoiceSubmissionControllerOptions);
34
+ getSnapshot(key: ScenarioChoiceSubmissionKey): ScenarioChoiceSubmissionSnapshot;
35
+ setTransport(submitChoice: ScenarioChoiceTransport): void;
36
+ subscribe(key: ScenarioChoiceSubmissionKey, listener: () => void): () => void;
37
+ submit(request: ScenarioChoiceSubmissionRequest): Promise<void>;
38
+ handleLifecycle(key: ScenarioChoiceSubmissionKey, lifecycle: ScenarioChoiceLifecycle): void;
39
+ handleResult(principalId: string, result: ScenarioChoiceSubmissionResult): void;
40
+ reset(): void;
41
+ dispose(): void;
42
+ private startConfirmationWindow;
43
+ private send;
44
+ private scheduleRetry;
45
+ private transitionToError;
46
+ private pruneEntries;
47
+ private clearTimers;
48
+ private emit;
49
+ }
@@ -2,6 +2,8 @@ import type { MessageData } from "@/api/messages.api.types";
2
2
  export type ScenarioChoiceOption = {
3
3
  id: string;
4
4
  label: string;
5
+ presentation: "PRIMARY" | "TERTIARY";
6
+ attention: "NONE" | "NUDGE_ONCE";
5
7
  };
6
8
  export declare const SCENARIO_CHOICE_LIFECYCLE_STATUSES: readonly ["PENDING", "ANSWERED", "TIMED_OUT", "CANCELLED"];
7
9
  export type ScenarioChoiceLifecycleStatus = (typeof SCENARIO_CHOICE_LIFECYCLE_STATUSES)[number];
@@ -49,6 +51,10 @@ export type ScenarioChoiceInput = {
49
51
  inputRequestId: string;
50
52
  optionId: string;
51
53
  clientEventId: string;
54
+ presentationContext?: {
55
+ surface: "CHAT" | "COMPACT_PREVIEW";
56
+ surfaceEpisodeId: string;
57
+ };
52
58
  };
53
59
  export declare const parseScenarioChoiceLifecycle: (value: unknown) => ScenarioChoiceLifecycle | null;
54
60
  export declare const parseScenarioChoiceLifecycleChanged: (value: unknown) => ScenarioChoiceLifecycleChanged | null;
@@ -1,10 +1,11 @@
1
1
  import type { CustomerInteractionSession } from "@/types";
2
2
  import { ScenarioInteractionCollector } from "./ScenarioInteractionCollector";
3
- import type { EngagementSubject, PresentationSubject, SurfaceState, SurfaceTransition, SurfaceTransitionContext } from "./scenario-interaction.types";
3
+ import type { CompactEngagementSubject, CompactPresentationSubject, EngagementSubject, PresentationSubject, SurfaceState, SurfaceTransition, SurfaceTransitionContext } from "./scenario-interaction.types";
4
4
  type RuntimeOptions = ConstructorParameters<typeof ScenarioInteractionCollector>[2];
5
5
  export declare class ScenarioInteractionRuntime {
6
6
  readonly collector: ScenarioInteractionCollector;
7
7
  private readonly surfaceController;
8
+ private compactSurfaceEpisode;
8
9
  private readonly presented;
9
10
  private readonly engaged;
10
11
  constructor(getSession: () => CustomerInteractionSession, onSessionExpired: () => void, options?: RuntimeOptions);
@@ -14,6 +15,11 @@ export declare class ScenarioInteractionRuntime {
14
15
  transitionSurface(nextState: SurfaceState, context: SurfaceTransitionContext): SurfaceTransition;
15
16
  getSurfaceState(): SurfaceState;
16
17
  getSurfaceEpisodeId(): string | null;
18
+ openCompactSurfaceEpisode(episodeId?: string): string;
19
+ closeCompactSurfaceEpisode(expectedEpisodeId?: string | undefined): boolean;
20
+ getCompactSurfaceEpisodeId(): string | null;
21
+ recordCompactPresented(subject: CompactPresentationSubject, expectedEpisodeId?: string | undefined): boolean;
22
+ recordCompactEngagement(subject: CompactEngagementSubject, expectedEpisodeId?: string | undefined): boolean;
17
23
  getPrincipalSessionId(): string;
18
24
  recordPresented(subject: PresentationSubject, expectedEpisodeId?: string | null): boolean;
19
25
  recordEngagement(subject: EngagementSubject): boolean;
@@ -8,5 +8,10 @@ type PresentationObserverInput = {
8
8
  intersectionRatio?: number;
9
9
  dwellMs?: number;
10
10
  };
11
- export declare const observePresentedElement: ({ element, isActive, isPresentable, onPresented, intersectionRatio, dwellMs, }: PresentationObserverInput) => (() => void);
11
+ export type PresentationObserverHandle = (() => void) & {
12
+ pause: () => void;
13
+ resume: () => void;
14
+ refresh: () => void;
15
+ };
16
+ export declare const observePresentedElement: ({ element, isActive, isPresentable, onPresented, intersectionRatio, dwellMs, }: PresentationObserverInput) => PresentationObserverHandle;
12
17
  export {};
@@ -5,13 +5,16 @@ export declare const SCENARIO_INTERACTION_ACTORS: readonly ["USER", "SCENARIO",
5
5
  export declare const SCENARIO_INTERACTION_SUBJECT_KINDS: readonly ["SURFACE", "MESSAGE", "CHOICE", "CTA", "TARGET"];
6
6
  export declare const SCENARIO_INTERACTION_RESULTS: readonly ["SUCCEEDED", "FAILED"];
7
7
  export declare const SCENARIO_INTERACTION_REASONS: readonly ["USER_LAUNCHER", "USER_CLOSE_BUTTON", "HOST_SET_OPEN", "SCENARIO_COMMAND", "UNKNOWN"];
8
+ export declare const PRESENTATION_SURFACES: readonly ["CHAT", "COMPACT_PREVIEW"];
8
9
  export type ScenarioInteractionKind = (typeof SCENARIO_INTERACTION_KINDS)[number];
9
10
  export type ScenarioInteractionType = (typeof SCENARIO_INTERACTION_TYPES)[number];
10
11
  export type ScenarioInteractionActor = (typeof SCENARIO_INTERACTION_ACTORS)[number];
11
12
  export type ScenarioInteractionSubjectKind = (typeof SCENARIO_INTERACTION_SUBJECT_KINDS)[number];
12
13
  export type ScenarioInteractionResult = (typeof SCENARIO_INTERACTION_RESULTS)[number];
13
14
  export type ScenarioInteractionReason = (typeof SCENARIO_INTERACTION_REASONS)[number];
15
+ export type PresentationSurface = (typeof PRESENTATION_SURFACES)[number];
14
16
  export type ScenarioInteractionDetails = {
17
+ surface?: PresentationSurface;
15
18
  presentationOrdinal?: number;
16
19
  };
17
20
  export type ScenarioInteractionObservation = {
@@ -82,3 +85,10 @@ export type EngagementSubject = {
82
85
  attributionHandle?: string;
83
86
  result?: ScenarioInteractionResult;
84
87
  };
88
+ export type CompactPresentationSubject = Omit<PresentationSubject, "subjectKind"> & {
89
+ subjectKind: Extract<PresentationSubject["subjectKind"], "MESSAGE" | "CHOICE" | "CTA">;
90
+ };
91
+ export type CompactEngagementSubject = Omit<EngagementSubject, "type" | "subjectKind"> & {
92
+ type: Extract<EngagementSubject["type"], "CTA_CLICKED" | "CTA_DISMISSED">;
93
+ subjectKind: "CTA";
94
+ };
@@ -1,13 +1,14 @@
1
- import type { Point } from "./widget-geometry";
1
+ import { type DockedLauncher, type Point, type Viewport } from "./widget-geometry";
2
2
  type PanelGeometry = Partial<Point> & {
3
3
  width: number;
4
4
  height: number;
5
5
  };
6
6
  export type WidgetUIData = {
7
- version: 2;
8
- launcher?: Point;
7
+ version: 3;
8
+ launcher: DockedLauncher;
9
9
  panel: PanelGeometry;
10
10
  };
11
- export declare function loadWidgetUIData(): WidgetUIData | null;
12
- export declare function saveWidgetUIData(data: WidgetUIData): void;
11
+ export declare function loadWidgetUIData(scope?: string | null, viewport?: Viewport): WidgetUIData | null;
12
+ export declare function claimLegacyWidgetUIData(scope: string | null | undefined, viewport: Viewport): WidgetUIData | null;
13
+ export declare function saveWidgetUIData(scope: string | null | undefined, data: WidgetUIData, viewport: Viewport): void;
13
14
  export {};
@@ -8,16 +8,44 @@ export type Size = {
8
8
  };
9
9
  export type Rect = Point & Size;
10
10
  export type Viewport = Rect;
11
+ export type ViewportInsets = {
12
+ top: number;
13
+ right: number;
14
+ bottom: number;
15
+ left: number;
16
+ };
17
+ export type LauncherSide = "LEFT" | "RIGHT";
18
+ export type DockedLauncher = {
19
+ side: LauncherSide;
20
+ yRatio: number;
21
+ };
22
+ export type CompactPreviewPlacement = "above" | "below";
23
+ export type CompactPreviewLayout = {
24
+ x: number;
25
+ y: number;
26
+ width: number;
27
+ maxHeight: number;
28
+ placement: CompactPreviewPlacement;
29
+ avoidanceFailed: boolean;
30
+ };
11
31
  export declare const LAUNCHER_SIZE = 52;
32
+ export declare const LAUNCHER_EDGE_INSET = 24;
12
33
  export declare const VIEWPORT_MARGIN = 16;
13
34
  export declare const MIN_WIDGET_WIDTH = 240;
14
35
  export declare const DEFAULT_WIDGET_WIDTH = 360;
15
36
  export declare const MIN_WIDGET_HEIGHT = 320;
16
37
  export declare const DEFAULT_WIDGET_HEIGHT = 680;
38
+ export declare const COMPACT_PREVIEW_MAX_WIDTH = 320;
39
+ export declare const COMPACT_PREVIEW_MARGIN = 12;
40
+ export declare const COMPACT_PREVIEW_GAP = 12;
17
41
  export declare function getViewport(): Viewport;
42
+ export declare function insetViewport(viewport: Viewport, insets: ViewportInsets): Viewport;
18
43
  export declare function getDefaultLauncherPosition(viewport: Viewport): Point;
44
+ export declare function dockLauncherPosition(point: Point, viewport: Viewport): DockedLauncher;
45
+ export declare function launcherPointFromDock(launcher: DockedLauncher, viewport: Viewport): Point;
19
46
  export declare function clampLauncherPosition(point: Point, viewport: Viewport): Point;
20
47
  export declare function clampPanelRect(rect: Rect, viewport: Viewport): Rect;
21
48
  export declare function placePanelFromLauncher(launcher: Point, size: Size, viewport: Viewport): Rect;
49
+ export declare function placeCompactPreview(launcher: Point, measuredSize: Size, viewport: Viewport, side: LauncherSide, avoidRect?: Rect | null): CompactPreviewLayout;
22
50
  export declare function resizePanelFromNorthWest(start: Rect, delta: Point, axes: "top" | "left" | "corner", viewport: Viewport): Rect;
23
51
  export declare function resizePanelFromSouthEast(start: Rect, delta: Point, viewport: Viewport): Rect;