@sapia-ai/chatbot-v3 1.1.0 → 1.2.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 (52) hide show
  1. package/dist/{EmbeddedVideoPlayer-COUZHpPa.js → EmbeddedVideoPlayer-C_8tKis8.js} +176 -186
  2. package/dist/{Preview-wPnYtccF.js → Preview-CbNUoib5.js} +3 -3
  3. package/dist/api/types/messages.d.ts +10 -2
  4. package/dist/api/types/responses.d.ts +1 -0
  5. package/dist/api.js +2 -2
  6. package/dist/core/bubbles/convert.d.ts +10 -0
  7. package/dist/core/bubbles/types.d.ts +1 -1
  8. package/dist/core/constants.d.ts +18 -0
  9. package/dist/core/events.d.ts +7 -0
  10. package/dist/core/internalUser.d.ts +13 -0
  11. package/dist/core/interview.d.ts +6 -2
  12. package/dist/core/persistence.d.ts +3 -0
  13. package/dist/core/redirect.d.ts +2 -0
  14. package/dist/core/store.d.ts +15 -1
  15. package/dist/core/validation/wordCount.d.ts +1 -0
  16. package/dist/{dist-Bwlks_W8.js → dist-7zan-9V3.js} +3 -3
  17. package/dist/embed/compat.d.ts +1 -1
  18. package/dist/embed/createChatbot.d.ts +4 -0
  19. package/dist/{fix-webm-duration-QAy9T6AR.js → fix-webm-duration-CJ-IsmuV.js} +2 -2
  20. package/dist/index.d.ts +1 -0
  21. package/dist/index.js +6055 -5621
  22. package/dist/{jsx-runtime-24G8w2Bf.js → jsx-runtime-CpK9sRtl.js} +2 -2
  23. package/dist/{messages-DCvBcspv.js → messages-t5h3u6nr.js} +5 -3
  24. package/dist/react/AnswerInput.d.ts +11 -5
  25. package/dist/react/SapiaChatbot.d.ts +5 -1
  26. package/dist/react/bubbleViewModel.d.ts +29 -6
  27. package/dist/react/strings.d.ts +29 -3
  28. package/dist/{react-DEF8h8hc.js → react--hlAJW6n.js} +3 -3
  29. package/dist/{react-y4vaW3OH.js → react-B6p_EGvt.js} +3 -3
  30. package/dist/{react-DDjfkpOE.js → react-BKQw6USG.js} +3 -3
  31. package/dist/{react-qa12DkXH.js → react-Bh9U4IBe.js} +3 -3
  32. package/dist/{react-DOgLiyZi.js → react-BtFG4mMU.js} +2 -2
  33. package/dist/{react-Bw5AugJc.js → react-Bx1VLQqP.js} +3 -3
  34. package/dist/{react-DPFikgTM.js → react-DFkljL-6.js} +3 -3
  35. package/dist/{react-BjO903pN.js → react-DJK2_GuR.js} +3 -3
  36. package/dist/{react-D8hV9uow.js → react-yXcg2glq.js} +3 -3
  37. package/dist/ui/chat/ChatBubble/ChatBubble.d.ts +1 -1
  38. package/dist/ui/chat/ChatLog/ChatLog.d.ts +4 -1
  39. package/dist/ui/chat/HintDisclosure/HintDisclosure.d.ts +20 -0
  40. package/dist/ui/chat/types.d.ts +5 -0
  41. package/dist/ui/constants.d.ts +5 -0
  42. package/dist/ui/hooks/usePreventPaste.d.ts +17 -0
  43. package/dist/ui/inputs/SliderInput/SliderInput.d.ts +1 -2
  44. package/dist/ui/inputs/TextAnswerInput/TextAnswerInput.d.ts +28 -11
  45. package/dist/ui/media/EmbeddedVideo/EmbeddedVideoPlayer.d.ts +1 -1
  46. package/dist/ui/primitives/MarkdownText/MarkdownText.d.ts +24 -0
  47. package/dist/ui/shell/CohortClosedScreen/CohortClosedScreen.d.ts +9 -0
  48. package/dist/ui/shell/DoneScreen/DoneScreen.d.ts +4 -1
  49. package/dist/ui/shell/Header/Header.d.ts +9 -0
  50. package/dist/ui/shell/TerminalMessageScreen/TerminalMessageScreen.d.ts +10 -0
  51. package/dist-umd/sapia-chatbot.umd.js +305 -305
  52. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
- import { w as e } from "./messages-DCvBcspv.js";
2
- import { t } from "./react-DOgLiyZi.js";
1
+ import { T as e } from "./messages-t5h3u6nr.js";
2
+ import { t } from "./react-BtFG4mMU.js";
3
3
  //#region ../../node_modules/.pnpm/react-player@3.4.0_@svta+cml-cta@1.0.6_@svta+cml-structured-field-values@1.1.3_@svta+cm_a4f64f295cc2a316f39ce360b0a6e480/node_modules/react-player/dist/Preview.js
4
4
  var n = /* @__PURE__ */ e(t(), 1), r = "64px", i = {}, a = ({ src: e, light: t, oEmbedUrl: a, onClickPreview: o, playIcon: s, previewTabIndex: c, previewAriaLabel: l }) => {
5
5
  let [u, d] = (0, n.useState)(null);
@@ -81,4 +81,4 @@ var n = /* @__PURE__ */ e(t(), 1), r = "64px", i = {}, a = ({ src: e, light: t,
81
81
  //#endregion
82
82
  export { a as default };
83
83
 
84
- //# sourceMappingURL=Preview-wPnYtccF.js.map
84
+ //# sourceMappingURL=Preview-CbNUoib5.js.map
@@ -65,7 +65,15 @@ export declare enum AssessmentStatus {
65
65
  FI_PREDICTION_COMPLETED = "FI_PREDICTION_COMPLETED",
66
66
  FI_ASSESSMENT_DUPLICATED = "FI_ASSESSMENT_DUPLICATED"
67
67
  }
68
- export type MessageIdentifier = string;
68
+ /**
69
+ * Backend message identifiers the widget branches on. The wire carries many more; only the ones
70
+ * with FE behaviour are enumerated, and the bubble field stays widened to `string` so unknown
71
+ * identifiers pass through untouched.
72
+ */
73
+ export declare enum MessageIdentifier {
74
+ SUBMIT_QUESTION = "SUBMIT_QUESTION_MESSAGE",
75
+ FEEDBACK_CUSTOMER_SATISFACTION_QUESTION = "FEEDBACK_CUSTOMER_SATISFACTION_QUESTION_MESSAGE"
76
+ }
69
77
  export type Trigger = {
70
78
  action: string;
71
79
  };
@@ -146,7 +154,7 @@ export type Answer = {
146
154
  export interface Message<T> {
147
155
  id: string;
148
156
  sender: MessageSender;
149
- messageIdentifier?: MessageIdentifier;
157
+ messageIdentifier?: MessageIdentifier | string;
150
158
  data: T;
151
159
  }
152
160
  export type CandidateResponse = Message<Answer>;
@@ -65,6 +65,7 @@ export type VideoSignedUrlResponse = {
65
65
  export type AccessManagementResponse = {
66
66
  shouldAuthenticate: boolean;
67
67
  emailHint?: string;
68
+ /** Assessment language; the gate renders before /start/ats, so this is its only source. */
68
69
  language?: string;
69
70
  };
70
71
  /** POST verify-access — whether the candidate email matches. */
package/dist/api.js CHANGED
@@ -1,2 +1,2 @@
1
- import { _ as e, a as t, b as n, c as r, d as i, f as a, g as o, h as s, i as c, l, m as u, n as d, o as f, p, r as m, s as h, t as g, u as _, v, x as y, y as b } from "./messages-DCvBcspv.js";
2
- export { s as API_ORIGIN_BY_ENV, p as AgUiEventType, g as AssessmentStatus, d as ButtonType, o as CHATBOT_API_PATH, a as ChatbotApiClient, m as ContentComponent, c as ContentType, e as DEFAULT_LOCAL_API_ORIGIN, v as ENVIRONMENTS, t as MessageKind, f as MessageSender, h as PHService, r as PiiQuestionType, l as QuestionType, b as REGIONS, _ as RuleName, u as SummaryActionStatus, i as VideoQuestionType, n as buildChatbotBaseUrl, y as resolveApiOrigin };
1
+ import { S as e, _ as t, a as n, b as r, c as i, d as a, f as o, g as s, h as c, i as l, l as u, m as d, n as f, o as p, p as m, r as h, s as g, t as _, u as v, v as y, x as b, y as x } from "./messages-t5h3u6nr.js";
2
+ export { s as API_ORIGIN_BY_ENV, d as AgUiEventType, _ as AssessmentStatus, f as ButtonType, t as CHATBOT_API_PATH, m as ChatbotApiClient, h as ContentComponent, l as ContentType, y as DEFAULT_LOCAL_API_ORIGIN, x as ENVIRONMENTS, n as MessageIdentifier, p as MessageKind, g as MessageSender, i as PHService, u as PiiQuestionType, v as QuestionType, r as REGIONS, a as RuleName, c as SummaryActionStatus, o as VideoQuestionType, b as buildChatbotBaseUrl, e as resolveApiOrigin };
@@ -2,6 +2,16 @@ import { PiiQuestionType, VideoQuestionType, AgentResponse, CandidateResponse, T
2
2
  import { MessageBubble } from './types';
3
3
  export declare const piiQuestionTypeFromQuestionTags: (tags?: Tag[]) => PiiQuestionType | undefined;
4
4
  export declare const videoQuestionTypeFromQuestionTags: (tags?: Tag[]) => VideoQuestionType | undefined;
5
+ /**
6
+ * Whether this question opts out of the paste block. An absent tag means blocked: the default is
7
+ * the restrictive one, so a question only becomes pasteable when the BE says so explicitly.
8
+ */
9
+ export declare const questionTagsAllowPaste: (tags?: Tag[]) => boolean;
10
+ /**
11
+ * Whether this question is the SMS-consent prompt. The BE marks it with a button-type tag rather
12
+ * than a dedicated message identifier, and the tag rides along on the answer bubble too.
13
+ */
14
+ export declare const questionTagsAreSmsConsent: (tags?: Tag[]) => boolean;
5
15
  export declare const convertRawCandidateResponseToMessageBubble: (response: CandidateResponse) => MessageBubble;
6
16
  export declare const convertRawAgentResponseToMessageBubble: (response: AgentResponse, hasNext?: boolean) => MessageBubble;
7
17
  export declare const convertRawResponseToMessageBubble: (response: CandidateResponse | AgentResponse, hasNext?: boolean) => MessageBubble | undefined;
@@ -32,7 +32,7 @@ export type MessageBubbleData = {
32
32
  export type MessageBubble = {
33
33
  status: BubbleStatus;
34
34
  sender: MessageSender;
35
- messageIdentifier?: MessageIdentifier;
35
+ messageIdentifier?: MessageIdentifier | string;
36
36
  id: string;
37
37
  types: QuestionType[];
38
38
  usageWhitelist?: PHService[];
@@ -1,3 +1,4 @@
1
+ import { AssessmentStatus } from '../api/types/messages';
1
2
  /** Streaming reveal cadence: ms per char-step (React passes 0 under prefers-reduced-motion). */
2
3
  export declare const STREAM_REVEAL_MS = 12;
3
4
  /** Chars revealed per reveal tick — bounds store writes to ~60/s at the default cadence. */
@@ -6,9 +7,26 @@ export declare const STREAM_CHARS_PER_TICK = 2;
6
7
  export declare const STREAM_BUBBLE_ID_PREFIX = "stream-live";
7
8
  export declare const QUESTION_TAG_PII_TYPE = "PII_TYPE";
8
9
  export declare const QUESTION_TAG_VIDEO_TYPE = "VIDEO_TYPE";
10
+ export declare const QUESTION_TAG_PH_BUTTON_TYPE = "PH_BUTTON_TYPE";
11
+ /** Question tag opting a single question out of the paste block. */
12
+ export declare const QUESTION_TAG_ALLOW_PASTE = "ALLOW_PASTE";
13
+ /** PH_BUTTON_TYPE value marking the SMS-consent prompt and its answer. */
14
+ export declare const QUESTION_TAG_VALUE_SMS_CONSENT = "ph_sms_consent";
15
+ /** Tag values are wire strings, not booleans; only the exact string counts as opt-in. */
16
+ export declare const QUESTION_TAG_VALUE_TRUE = "true";
17
+ /**
18
+ * Statuses the assessment reaches once the candidate has pressed the final submit. Every answer
19
+ * is final from that point, so the chat log drops its Edit affordances.
20
+ */
21
+ export declare const SUBMITTED_ASSESSMENT_STATUSES: readonly AssessmentStatus[];
9
22
  /** Experience-rating slider range and starting position (legacy defaultSliderValue). */
10
23
  export declare const SLIDER_MIN = 1;
11
24
  export declare const SLIDER_MAX = 10;
12
25
  export declare const SLIDER_DEFAULT_VALUE = 8;
13
26
  /** Default video answer length when the question carries no max-seconds rule. */
14
27
  export declare const MAX_VIDEO_SECONDS_DEFAULT = 60;
28
+ /**
29
+ * Answer text the backend reads as "the candidate skipped the optional feedback comment". Sending
30
+ * nothing is a valid answer there, so an empty composer submits this sentinel rather than ''.
31
+ */
32
+ export declare const SKIP_FEEDBACK_COMMENT_TEXT = "SAPIA_SKIP_FEEDBACK";
@@ -12,8 +12,15 @@ export type ChatbotEventMap = {
12
12
  [CHATBOT_EVENTS.ASSESSMENT_STARTED]: {
13
13
  assessmentId: string;
14
14
  };
15
+ /**
16
+ * `justCompleted` is true only when the interview finished during this session — the legacy
17
+ * FI_ASSESSMENT_ENDED semantics (fired off a /next with no next question). It is false when the
18
+ * assessment was already over on load, or ended on a terminal message, where legacy stayed
19
+ * silent. A host acting on the end of an interview (an auto-redirect, analytics) must gate on it.
20
+ */
15
21
  [CHATBOT_EVENTS.ASSESSMENT_ENDED]: {
16
22
  assessmentId: string;
23
+ justCompleted: boolean;
17
24
  };
18
25
  [CHATBOT_EVENTS.ASSESSMENT_DUPLICATED]: {
19
26
  assessmentId: string;
@@ -0,0 +1,13 @@
1
+ /** Query params the widget honours on the hosting page. Mirrors legacy src/types/UrlParamKey.ts. */
2
+ export declare enum UrlParamKey {
3
+ /** Legacy internal-tester flag; bypasses the paste block. */
4
+ INTERNAL_USER = "internalUser",
5
+ /** Sapia-staff flag; same bypass, kept separate so hosts can tell the two audiences apart. */
6
+ SAPIAN = "sapian"
7
+ }
8
+ /**
9
+ * Reads the internal-session flags off the host page URL. Either param unlocks paste, so a tester
10
+ * can re-enable it from the address bar without a host redeploy — the block is an integrity signal
11
+ * aimed at candidates, not a security boundary.
12
+ */
13
+ export declare const isInternalUserFromUrl: () => boolean;
@@ -12,6 +12,7 @@ export declare const ERROR_TYPE_VERIFY_ACCESS_FAILED = "FI_VERIFY_ACCESS_FAILED"
12
12
  export declare const ERROR_TYPE_DETECT_AGC_FAILED = "FI_DETECT_AGC_FAILED";
13
13
  export declare const LOG_TYPE_INVALID_PHONE_NUMBER = "INVALID_PHONE_NUMBER";
14
14
  export declare const LOG_TYPE_INVALID_PASTE_ANSWER_ACTION = "INVALID_PASTE_ANSWER_ACTION";
15
+ export declare const LOG_TYPE_INVALID_REDIRECT_URL = "INVALID_REDIRECT_URL";
15
16
  /** Outcome of a validated PII submission (drives the UI error/confirm modals). */
16
17
  export declare enum PiiSubmitStatus {
17
18
  SUBMITTED = "submitted",
@@ -48,7 +49,8 @@ export declare class InterviewEngine {
48
49
  private readonly emitter;
49
50
  private readonly assessmentId;
50
51
  private readonly userAgent;
51
- private readonly language;
52
+ /** Reassigned when /access-management names the assessment language before the gate. */
53
+ private language;
52
54
  private readonly welcomeGate;
53
55
  private readonly streamRevealMs;
54
56
  /** Aborts the in-flight stream when a new load starts. */
@@ -163,7 +165,9 @@ export declare class InterviewEngine {
163
165
  continueAfterAgcWarning(): Promise<void>;
164
166
  /** Dismiss the AGC warning to edit the answer (legacy "Edit"). */
165
167
  dismissAgcWarning(): void;
166
- /** Emits the legacy INVALID_PASTE_ANSWER_ACTION log when paste is disallowed. */
168
+ /** Dismiss the edit-failure dialog; the composer reopens on the edit, so send retries it. */
169
+ dismissEditFailure(): void;
170
+ /** Emits the legacy INVALID_PASTE_ANSWER_ACTION log; fires only once a paste has been refused. */
167
171
  reportPaste(): void;
168
172
  /** Submit the experience-rating slider score (legacy handleSubmitRating). */
169
173
  submitSliderAnswer(score: number): Promise<void>;
@@ -2,3 +2,6 @@
2
2
  export declare const getVerifiedEmail: (assessmentId: string, now?: number) => string | undefined;
3
3
  /** Persist a verified email for the assessment with the 30-minute legacy TTL. */
4
4
  export declare const saveVerifiedEmail: (assessmentId: string, candidateEmail: string, now?: number) => void;
5
+ /** Whether the candidate opted out of the below-minimum word-count warning for this assessment. */
6
+ export declare const getSkipShortAnswerWarning: (assessmentId: string) => boolean;
7
+ export declare const setSkipShortAnswerWarning: (assessmentId: string, skip: boolean) => void;
@@ -0,0 +1,2 @@
1
+ /** Only absolute http(s) URLs are offered; anything else is host misconfiguration, not a link. */
2
+ export declare const validRedirectUrl: (url?: string) => string | undefined;
@@ -12,7 +12,15 @@ export declare enum InterviewPhase {
12
12
  WELCOME = "welcome",
13
13
  ACTIVE = "active",
14
14
  ENDED = "ended",
15
- FAILED = "failed"
15
+ FAILED = "failed",
16
+ /** The cohort behind this assessment is closed; the interview can never be taken. */
17
+ COHORT_CLOSED = "cohort-closed",
18
+ /**
19
+ * The backend ended the interview with an explanatory message (archived cohort / reused
20
+ * assessment) rather than a completion. Distinct from ENDED so the shell shows the message
21
+ * instead of the completion screen.
22
+ */
23
+ TERMINAL_MESSAGE = "terminal-message"
16
24
  }
17
25
  export type InterviewState = {
18
26
  phase: InterviewPhase;
@@ -53,12 +61,18 @@ export type InterviewState = {
53
61
  /** AGC (automated-content) detection attempt count + whether another check is allowed. */
54
62
  agcAttempt?: number;
55
63
  allowNextAgcDetection?: boolean;
64
+ /** Terms accepted for this assessment; locks PII and SMS-consent answers against re-editing. */
65
+ termsAccepted?: boolean;
56
66
  /** Show the AGC warning modal (automated content suspected on submit). */
57
67
  showAgcWarning?: boolean;
68
+ /** Question whose edited answer failed to save; drives the retry dialog. */
69
+ editFailedQuestionId?: string;
58
70
  /** Interview language (resolved); drives translations + text direction. */
59
71
  language?: string;
60
72
  /** Fetched translation file (flat key/value); undefined → bundled fallback only. */
61
73
  translations?: Record<string, string>;
74
+ /** Backend copy for TERMINAL_MESSAGE (archived cohort / reused assessment), as markdown. */
75
+ terminalMessage?: string;
62
76
  lastError?: {
63
77
  type: string;
64
78
  message: string;
@@ -0,0 +1 @@
1
+ export declare const countWords: (text: string) => number;
@@ -1,5 +1,5 @@
1
- import { w as e } from "./messages-DCvBcspv.js";
2
- import { t } from "./react-DOgLiyZi.js";
1
+ import { T as e } from "./messages-t5h3u6nr.js";
2
+ import { t } from "./react-BtFG4mMU.js";
3
3
  import { t as n } from "./defineProperty-DiFpLzd2.js";
4
4
  import { n as r, t as i } from "./hls-mBncGwsE.js";
5
5
  import { a, i as o, n as s, r as c, t as l } from "./classPrivateFieldSet2-DtQPURVD.js";
@@ -14324,4 +14324,4 @@ var Kw = Object.prototype.hasOwnProperty, qw = (e, t) => {
14324
14324
  //#endregion
14325
14325
  export { Or as MaxResolution, C as MediaError, kr as MinResolution, Ar as RenditionOrder, rT as default, ha as generatePlayerInitTime, nT as playerSoftwareName, tT as playerSoftwareVersion };
14326
14326
 
14327
- //# sourceMappingURL=dist-Bwlks_W8.js.map
14327
+ //# sourceMappingURL=dist-7zan-9V3.js.map
@@ -58,7 +58,7 @@ export type PHChatBotOptions = {
58
58
  jobRequisitionShortcode?: string;
59
59
  applicationShortcode?: string;
60
60
  showHeader?: boolean;
61
- /** Accepted for source compatibility; the widget does not order assessments. */
61
+ /** Internal session (Sapia staff / QA): permits paste on every question. */
62
62
  isInternalUser?: boolean;
63
63
  apiOrigin?: string;
64
64
  };
@@ -9,8 +9,12 @@ export type CreateChatbotOptions = {
9
9
  apiOrigin?: string;
10
10
  theme?: Theme;
11
11
  showHeader?: boolean;
12
+ /** Internal session (Sapia staff / QA): permits paste on every question. */
13
+ isInternalUser?: boolean;
12
14
  /** Interview language override (e.g. "fr"); defaults to the browser language. */
13
15
  language?: string;
16
+ /** Absolute http(s) URL offered as a "continue" CTA once the interview ends. */
17
+ redirectUrl?: string;
14
18
  /** Custom fetch (e.g. a mocked SSE stream for demos/tests). */
15
19
  fetchImpl?: typeof fetch;
16
20
  };
@@ -1,4 +1,4 @@
1
- import { S as e } from "./messages-DCvBcspv.js";
1
+ import { C as e } from "./messages-t5h3u6nr.js";
2
2
  //#region ../../node_modules/.pnpm/fix-webm-duration@1.0.6/node_modules/fix-webm-duration/fix-webm-duration.js
3
3
  var t = /* @__PURE__ */ e(((e, t) => {
4
4
  (function(e, n) {
@@ -1054,4 +1054,4 @@ var t = /* @__PURE__ */ e(((e, t) => {
1054
1054
  //#endregion
1055
1055
  export default t();
1056
1056
 
1057
- //# sourceMappingURL=fix-webm-duration-QAy9T6AR.js.map
1057
+ //# sourceMappingURL=fix-webm-duration-CJ-IsmuV.js.map
package/dist/index.d.ts CHANGED
@@ -2,5 +2,6 @@ export { createChatbot, type Chatbot, type CreateChatbotOptions } from './embed/
2
2
  export { DEFAULT_CONTAINER_ID } from './embed/constants';
3
3
  export { PHChatBot, PHChatBotCallbackFunction, CHATBOT_CONTAINER_ID, type PHChatBotCallback, type PHChatBotOptions, type PHChatBotTheme, } from './embed/compat';
4
4
  export { CHATBOT_EVENTS, type ChatbotEventName, type ChatbotEventMap, } from './core/events';
5
+ export { validRedirectUrl } from './core/redirect';
5
6
  export type { Theme, ThemeSection } from './core/theme/types';
6
7
  export { ENVIRONMENTS, REGIONS, type Env, type Region } from './api/config';