@runtypelabs/persona 4.25.0 → 4.26.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 (236) hide show
  1. package/dist/animations/glyph-cycle.d.cts +1 -1
  2. package/dist/animations/glyph-cycle.d.ts +1 -1
  3. package/dist/animations/{types-Db61aI9J.d.cts → types-BwKTZhg9.d.cts} +14 -0
  4. package/dist/animations/{types-Db61aI9J.d.ts → types-BwKTZhg9.d.ts} +14 -0
  5. package/dist/animations/wipe.d.cts +1 -1
  6. package/dist/animations/wipe.d.ts +1 -1
  7. package/dist/animations-extra.d.cts +14 -0
  8. package/dist/animations-extra.d.ts +14 -0
  9. package/dist/approval-ui-QAKWNFNM.js +1 -0
  10. package/dist/approval-ui.cjs +2 -2
  11. package/dist/approval-ui.d.cts +180 -8
  12. package/dist/approval-ui.d.ts +180 -8
  13. package/dist/approval-ui.js +2 -2
  14. package/dist/{artifacts-ui-IGB5YKLO.js → artifacts-ui-NJGMM27I.js} +1 -1
  15. package/dist/artifacts-ui.cjs +4 -4
  16. package/dist/artifacts-ui.d.cts +180 -8
  17. package/dist/artifacts-ui.d.ts +180 -8
  18. package/dist/artifacts-ui.js +5 -5
  19. package/dist/chunk-2ANTRKTV.js +1 -0
  20. package/dist/chunk-4HPTPUC6.js +1 -0
  21. package/dist/chunk-6V3MNEKB.js +1 -0
  22. package/dist/chunk-HGUCGM3K.js +9 -0
  23. package/dist/chunk-IO5VVUKP.js +3 -0
  24. package/dist/chunk-J4J3Y2P2.js +1 -0
  25. package/dist/chunk-K6HKIP5K.js +1 -0
  26. package/dist/chunk-K7NA7G7J.js +1 -0
  27. package/dist/chunk-MXUTS3LB.js +1 -0
  28. package/dist/chunk-PDPVORUF.js +1 -0
  29. package/dist/chunk-QPPJUSZH.js +1 -0
  30. package/dist/chunk-QRKGQPU7.js +1 -0
  31. package/dist/chunk-S6PBWE75.js +1 -0
  32. package/dist/chunk-T7QM7YMF.js +1 -0
  33. package/dist/chunk-TR2H475E.js +1 -0
  34. package/dist/chunk-UPO4GUFC.js +1 -0
  35. package/dist/chunk-ZC66NAIR.js +1 -0
  36. package/dist/client-history-YPEG45Q4.js +1 -0
  37. package/dist/client-history.js +1 -0
  38. package/dist/client-stream-42FOI3EC.js +4 -0
  39. package/dist/client-stream.js +5 -0
  40. package/dist/codegen.cjs +1 -1
  41. package/dist/codegen.js +1 -1
  42. package/dist/context-mentions-E7HJYWO6.js +161 -0
  43. package/dist/{context-mentions-inline-I2RDEBAK.js → context-mentions-inline-QVESLW55.js} +1 -1
  44. package/dist/context-mentions.cjs +7 -7
  45. package/dist/context-mentions.d.cts +180 -8
  46. package/dist/context-mentions.d.ts +180 -8
  47. package/dist/context-mentions.js +8 -8
  48. package/dist/event-stream-view-FOIJHFQO.js +1 -0
  49. package/dist/event-stream-view.cjs +1 -1
  50. package/dist/event-stream-view.d.cts +183 -31
  51. package/dist/event-stream-view.d.ts +183 -31
  52. package/dist/event-stream-view.js +1 -1
  53. package/dist/forms-ui.d.cts +221 -667
  54. package/dist/forms-ui.d.ts +221 -667
  55. package/dist/history-shell-QDQ44PSE.js +3 -0
  56. package/dist/history-shell.js +3 -0
  57. package/dist/{history-view-BD6OXLNA.js → history-view-NCFBI5GR.js} +2 -2
  58. package/dist/history-view.cjs +2 -2
  59. package/dist/history-view.d.cts +35 -1
  60. package/dist/history-view.d.ts +35 -1
  61. package/dist/history-view.js +2 -2
  62. package/dist/index.cjs +80 -74
  63. package/dist/index.cjs.map +1 -1
  64. package/dist/index.d.cts +227 -738
  65. package/dist/index.d.ts +227 -738
  66. package/dist/index.global.js +3 -215
  67. package/dist/index.global.js.map +1 -1
  68. package/dist/index.js +81 -75
  69. package/dist/index.js.map +1 -1
  70. package/dist/install.global.js +1 -1
  71. package/dist/install.global.js.map +1 -1
  72. package/dist/launcher.global.js +3 -67
  73. package/dist/launcher.global.js.map +1 -1
  74. package/dist/markdown-parsers.js +22 -22
  75. package/dist/runtype-tts-entry-NFBVWR7N.js +1 -0
  76. package/dist/runtype-tts.js +1 -1
  77. package/dist/session-actions-ISSKSI66.js +1 -0
  78. package/dist/session-actions.js +1 -0
  79. package/dist/session-reconnect.d.cts +180 -8
  80. package/dist/session-reconnect.d.ts +180 -8
  81. package/dist/smart-dom-reader.cjs +13 -13
  82. package/dist/smart-dom-reader.d.cts +180 -8
  83. package/dist/smart-dom-reader.d.ts +180 -8
  84. package/dist/smart-dom-reader.js +11 -11
  85. package/dist/theme-editor-preview.cjs +116 -98
  86. package/dist/theme-editor-preview.d.cts +182 -8
  87. package/dist/theme-editor-preview.d.ts +182 -8
  88. package/dist/theme-editor-preview.js +49 -54
  89. package/dist/theme-editor.cjs +2 -2
  90. package/dist/theme-editor.d.cts +181 -23
  91. package/dist/theme-editor.d.ts +181 -23
  92. package/dist/theme-editor.js +2 -2
  93. package/dist/theme-reference.d.cts +14 -0
  94. package/dist/theme-reference.d.ts +14 -0
  95. package/dist/ui-extras-entry-WAYFKVNW.js +3 -0
  96. package/dist/ui-extras.js +72 -0
  97. package/dist/voice-runtime-UOQOFAFY.js +17 -0
  98. package/dist/voice-runtime.cjs +17 -1
  99. package/dist/voice-runtime.d.cts +11650 -232
  100. package/dist/voice-runtime.d.ts +11650 -232
  101. package/dist/voice-runtime.js +17 -1
  102. package/dist/voice-worklet-player.cjs +19 -4
  103. package/dist/voice-worklet-player.d.cts +9 -10
  104. package/dist/voice-worklet-player.d.ts +9 -10
  105. package/dist/voice-worklet-player.js +19 -4
  106. package/dist/webmcp-polyfill.js +1 -1
  107. package/dist/webmcp-runtime-V6ZWW3PI.js +6 -0
  108. package/dist/webmcp-runtime.cjs +2 -2
  109. package/dist/webmcp-runtime.d.cts +1 -33
  110. package/dist/webmcp-runtime.d.ts +1 -33
  111. package/dist/webmcp-runtime.js +2 -2
  112. package/dist/widget.css +1 -1
  113. package/package.json +11 -5
  114. package/src/approval-paused-tool-settle.test.ts +221 -0
  115. package/src/client-approve.test.ts +311 -0
  116. package/src/client-history-eager.ts +9 -0
  117. package/src/client-history-loader.ts +23 -0
  118. package/src/client-history.ts +579 -0
  119. package/src/client-resume.ts +177 -0
  120. package/src/client-stream-eager.ts +12 -0
  121. package/src/client-stream-loader.ts +25 -0
  122. package/src/client-stream.ts +1880 -0
  123. package/src/client.test.ts +31 -1
  124. package/src/client.ts +293 -2607
  125. package/src/components/approval-actions.test.ts +27 -0
  126. package/src/components/approval-actions.ts +27 -3
  127. package/src/components/message-bubble.test.ts +19 -0
  128. package/src/components/message-bubble.ts +2 -0
  129. package/src/components/registry.ts +6 -6
  130. package/src/components/tool-bubble.test.ts +9 -0
  131. package/src/components/tool-bubble.ts +1 -0
  132. package/src/history-shell-bundle.test.ts +54 -0
  133. package/src/history-shell-eager.ts +12 -0
  134. package/src/history-shell-loader.ts +27 -0
  135. package/src/history-shell.ts +2362 -0
  136. package/src/history-view-loader.ts +2 -0
  137. package/src/history-view.ts +3 -0
  138. package/src/index-core.ts +6 -0
  139. package/src/index-global.ts +90 -248
  140. package/src/index.ts +13 -0
  141. package/src/install.ts +6 -4
  142. package/src/internal/runtype-history-provider.test.ts +1 -0
  143. package/src/internal/runtype-history-provider.ts +28 -13
  144. package/src/plugins/registry.ts +8 -8
  145. package/src/session-actions-chunk.test.ts +55 -0
  146. package/src/session-actions-eager.ts +11 -0
  147. package/src/session-actions-loader.ts +37 -0
  148. package/src/session-actions.ts +909 -0
  149. package/src/session-history.test.ts +3 -1
  150. package/src/session.ts +852 -1536
  151. package/src/session.voice.test.ts +1170 -5
  152. package/src/styles/widget.css +58 -0
  153. package/src/theme-editor/state.ts +91 -91
  154. package/src/types.ts +198 -5
  155. package/src/ui-ask-user-sheet.ts +382 -0
  156. package/src/ui-extras-eager.ts +10 -0
  157. package/src/ui-extras-entry.ts +9 -0
  158. package/src/ui-extras-loader.ts +26 -0
  159. package/src/ui.artifact-rail-resize.test.ts +2 -1
  160. package/src/ui.ask-user-chunk.test.ts +93 -0
  161. package/src/ui.history-chunk.test.ts +2 -1
  162. package/src/ui.history-config-provider.test.ts +2 -1
  163. package/src/ui.history-confirm-fallback.test.ts +142 -0
  164. package/src/ui.history-controller.test.ts +2 -1
  165. package/src/ui.history-lifecycle.test.ts +2 -1
  166. package/src/ui.history-render-hooks.test.ts +2 -1
  167. package/src/ui.history-shell.test.ts +2 -1
  168. package/src/ui.rail-overlay.test.ts +2 -1
  169. package/src/ui.rail-resize.test.ts +2 -1
  170. package/src/ui.rail-trigger-pending.test.ts +2 -1
  171. package/src/ui.ts +340 -2770
  172. package/src/utils/attachment-manager.ts +67 -67
  173. package/src/utils/constants.ts +3 -0
  174. package/src/utils/context-mention-controller.ts +323 -323
  175. package/src/utils/context-mention-manager.ts +40 -40
  176. package/src/utils/context-mention-orchestrator.ts +28 -0
  177. package/src/utils/event-stream-buffer.ts +50 -50
  178. package/src/utils/event-stream-store.ts +43 -43
  179. package/src/utils/throughput-tracker.ts +34 -34
  180. package/src/utils/virtual-scroller.ts +67 -67
  181. package/src/utils/visitor-store.test.ts +50 -0
  182. package/src/utils/visitor-store.ts +23 -5
  183. package/src/voice/__fixtures__/voice-wire/README.md +1 -0
  184. package/src/voice/__fixtures__/voice-wire/client/cancel.json +3 -0
  185. package/src/voice/__fixtures__/voice-wire/client/context.json +4 -0
  186. package/src/voice/__fixtures__/voice-wire/client/delegation_result.json +6 -0
  187. package/src/voice/__fixtures__/voice-wire/client/delegation_update.json +6 -0
  188. package/src/voice/__fixtures__/voice-wire/client/ping.json +3 -0
  189. package/src/voice/__fixtures__/voice-wire/client/playback_progress.json +5 -0
  190. package/src/voice/__fixtures__/voice-wire/client/start.json +3 -0
  191. package/src/voice/__fixtures__/voice-wire/server/artifact.json +11 -0
  192. package/src/voice/__fixtures__/voice-wire/server/attached.json +4 -0
  193. package/src/voice/__fixtures__/voice-wire/server/audio_clear.json +4 -0
  194. package/src/voice/__fixtures__/voice-wire/server/audio_end.json +3 -0
  195. package/src/voice/__fixtures__/voice-wire/server/delegation_cancelled.json +5 -0
  196. package/src/voice/__fixtures__/voice-wire/server/delegation_completed.json +8 -0
  197. package/src/voice/__fixtures__/voice-wire/server/delegation_started.json +15 -0
  198. package/src/voice/__fixtures__/voice-wire/server/error.json +6 -0
  199. package/src/voice/__fixtures__/voice-wire/server/metrics.json +10 -0
  200. package/src/voice/__fixtures__/voice-wire/server/pong.json +3 -0
  201. package/src/voice/__fixtures__/voice-wire/server/session_config.json +16 -0
  202. package/src/voice/__fixtures__/voice-wire/server/transcript_final.json +7 -0
  203. package/src/voice/__fixtures__/voice-wire/server/transcript_interim.json +4 -0
  204. package/src/voice/__fixtures__/voice-wire/server/transcript_partial.json +6 -0
  205. package/src/voice/__fixtures__/voice-wire/server/transcript_update.json +10 -0
  206. package/src/voice/__fixtures__/voice-wire/server/warning.json +5 -0
  207. package/src/voice/audio-playback-manager.test.ts +57 -0
  208. package/src/voice/audio-playback-manager.ts +120 -98
  209. package/src/voice/browser-voice-provider.ts +37 -37
  210. package/src/voice/fallback-speech-engine.ts +22 -16
  211. package/src/voice/keyed-voice-transcript.ts +314 -0
  212. package/src/voice/mic-state-styles.ts +182 -0
  213. package/src/voice/read-aloud-controller.ts +46 -44
  214. package/src/voice/runtype-speech-engine.ts +35 -33
  215. package/src/voice/runtype-voice-provider.ts +670 -189
  216. package/src/voice/session-voice-wiring.ts +266 -0
  217. package/src/voice/voice-delegation.test.ts +71 -0
  218. package/src/voice/voice-delegation.ts +364 -0
  219. package/src/voice/voice-turn-tracker.ts +6 -6
  220. package/src/voice/voice.test.ts +981 -8
  221. package/src/voice/worklet-playback-engine.test.ts +81 -0
  222. package/src/voice/worklet-playback-engine.ts +21 -3
  223. package/src/voice-runtime-loader.ts +4 -1
  224. package/src/voice-runtime.ts +7 -0
  225. package/src/webmcp-runtime-entry.ts +41 -39
  226. package/dist/approval-ui-3NMYNXBD.js +0 -1
  227. package/dist/chunk-5EIIHQLQ.js +0 -1
  228. package/dist/chunk-7CO4T5KB.js +0 -1
  229. package/dist/chunk-EFJBUH5F.js +0 -1
  230. package/dist/chunk-R2TMIMDI.js +0 -3
  231. package/dist/chunk-UTVLOTWB.js +0 -9
  232. package/dist/context-mentions-45MODWOY.js +0 -161
  233. package/dist/event-stream-view-GL3ADRY2.js +0 -1
  234. package/dist/runtype-tts-entry-UJAEF7NZ.js +0 -1
  235. package/dist/voice-runtime-7DA7MJH4.js +0 -1
  236. package/dist/webmcp-runtime-JI7T4QYF.js +0 -6
@@ -0,0 +1,26 @@
1
+ import { createChunkLoader } from "./utils/chunk-loader";
2
+
3
+ /**
4
+ * Loader indirection for the `ui-extras` chunk (`ui-extras-entry.ts`): UI glue
5
+ * for opt-in or interaction-only features. The ESM/CJS fallback imports
6
+ * `./ui-extras-entry` directly (inlined by `--splitting false`); the IIFE/CDN
7
+ * build marks it external and registers a loader for the sibling
8
+ * `ui-extras.js` chunk (see `index-global.ts`).
9
+ */
10
+ export type UiExtrasModule = typeof import("./ui-extras-entry");
11
+
12
+ const { setLoader, load, provide, getSync } = createChunkLoader<UiExtrasModule>({
13
+ fallbackImport: () => import("./ui-extras-entry"),
14
+ });
15
+
16
+ /** Override how the chunk is fetched (the IIFE build registers a sibling-URL loader). */
17
+ export const setUiExtrasLoader = setLoader;
18
+
19
+ /** Load the chunk. Memoized; retries after rejection. */
20
+ export const loadUiExtras = load;
21
+
22
+ /** Eagerly supply the module (`ui-extras-eager.ts`: npm builds and tests). */
23
+ export const provideUiExtras = provide;
24
+
25
+ /** Synchronous access once loaded/provided; null before that. */
26
+ export const getUiExtrasSync = getSync;
@@ -11,6 +11,7 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vite
11
11
 
12
12
  import { createAgentExperience } from "./ui";
13
13
  import { createHistoryView } from "./components/history-view";
14
+ import { showHistoryConfirm } from "./components/history-confirm-dialog";
14
15
  import { setHistoryViewLoader } from "./history-view-loader";
15
16
  import { setHistoryProviderFactory } from "./internal/history-provider-registry";
16
17
  import {
@@ -162,7 +163,7 @@ const dragArtifact = (mount: HTMLElement, dx: number) => {
162
163
  describe("history rail beside a resizable artifact split", () => {
163
164
  beforeEach(() => {
164
165
  window.scrollTo = vi.fn();
165
- setHistoryViewLoader(async () => ({ createHistoryView }));
166
+ setHistoryViewLoader(async () => ({ createHistoryView, showHistoryConfirm }));
166
167
  });
167
168
 
168
169
  afterEach(() => {
@@ -0,0 +1,93 @@
1
+ // @vitest-environment jsdom
2
+ import { describe, it, expect, vi } from "vitest";
3
+
4
+ // Simulate the IIFE/CDN path: the ui-extras chunk is not provided up front and
5
+ // each test controls when its load resolves.
6
+ const chunk = vi.hoisted(() => {
7
+ const click = vi.fn();
8
+ const keydown = vi.fn();
9
+ let release: () => void = () => {};
10
+ let promise: Promise<unknown> = Promise.resolve();
11
+ const reset = () => {
12
+ click.mockClear();
13
+ keydown.mockClear();
14
+ promise = new Promise((resolve) => {
15
+ release = () =>
16
+ resolve({
17
+ createAskUserSheetHandlers: () => ({ click, keydown }),
18
+ createContextMentionOrchestrator: () => null,
19
+ });
20
+ });
21
+ };
22
+ return { click, keydown, reset, release: () => release(), load: () => promise };
23
+ });
24
+
25
+ vi.mock("./ui-extras-loader", () => ({
26
+ getUiExtrasSync: () => null,
27
+ loadUiExtras: () => chunk.load(),
28
+ setUiExtrasLoader: () => {},
29
+ provideUiExtras: () => {},
30
+ }));
31
+
32
+ import { createAgentExperience } from "./ui";
33
+
34
+ const mountWithSheet = () => {
35
+ const mount = document.createElement("div");
36
+ document.body.appendChild(mount);
37
+ const controller = createAgentExperience(mount, {
38
+ apiUrl: "https://api.example.com/chat",
39
+ launcher: { enabled: false },
40
+ });
41
+ const overlay = mount.querySelector<HTMLElement>("[data-persona-composer-overlay]")!;
42
+ const sheet = document.createElement("div");
43
+ sheet.setAttribute("data-persona-ask-sheet-for", "tool-1");
44
+ const pill = document.createElement("button");
45
+ pill.setAttribute("data-ask-user-action", "pick");
46
+ sheet.appendChild(pill);
47
+ overlay.appendChild(sheet);
48
+ return { controller, sheet, pill };
49
+ };
50
+
51
+ const flush = () => new Promise((resolve) => setTimeout(resolve, 0));
52
+
53
+ describe("ask-user sheet events waiting on the lazy chunk", () => {
54
+ it("handles only the first of repeated early clicks", async () => {
55
+ chunk.reset();
56
+ const { controller, pill } = mountWithSheet();
57
+ pill.click();
58
+ pill.click();
59
+ chunk.release();
60
+ await flush();
61
+ expect(chunk.click).toHaveBeenCalledTimes(1);
62
+ controller.destroy();
63
+ });
64
+
65
+ it("typing in the free-text input does not block the Enter that submits", async () => {
66
+ chunk.reset();
67
+ const { controller, sheet } = mountWithSheet();
68
+ const input = document.createElement("input");
69
+ input.setAttribute("data-ask-free-text-input", "true");
70
+ sheet.appendChild(input);
71
+ const key = (k: string) =>
72
+ input.dispatchEvent(new KeyboardEvent("keydown", { key: k, bubbles: true }));
73
+ key("h");
74
+ key("i");
75
+ key("Enter");
76
+ chunk.release();
77
+ await flush();
78
+ expect(chunk.keydown).toHaveBeenCalledTimes(1);
79
+ expect((chunk.keydown.mock.calls[0][0] as KeyboardEvent).key).toBe("Enter");
80
+ controller.destroy();
81
+ });
82
+
83
+ it("drops a queued click whose sheet was removed before the chunk loaded", async () => {
84
+ chunk.reset();
85
+ const { controller, sheet, pill } = mountWithSheet();
86
+ pill.click();
87
+ sheet.remove();
88
+ chunk.release();
89
+ await flush();
90
+ expect(chunk.click).not.toHaveBeenCalled();
91
+ controller.destroy();
92
+ });
93
+ });
@@ -10,6 +10,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
10
10
 
11
11
  import { createAgentExperience } from "./ui";
12
12
  import { createHistoryView } from "./components/history-view";
13
+ import { showHistoryConfirm } from "./components/history-confirm-dialog";
13
14
  import { setHistoryViewLoader } from "./history-view-loader";
14
15
  import { setHistoryProviderFactory } from "./internal/history-provider-registry";
15
16
  import { createDemoHistoryProvider } from "./internal/demo-history-provider";
@@ -63,7 +64,7 @@ describe("history lazy chunk", () => {
63
64
  expect(body.hasAttribute("inert")).toBe(false);
64
65
 
65
66
  // A dropped fetch must not disable the feature for the session.
66
- setHistoryViewLoader(async () => ({ createHistoryView }));
67
+ setHistoryViewLoader(async () => ({ createHistoryView, showHistoryConfirm }));
67
68
  mount.querySelector<HTMLButtonElement>("[data-persona-history-toggle]")!.click();
68
69
  await flush();
69
70
  expect(mount.querySelector(".persona-history-view")).not.toBeNull();
@@ -11,6 +11,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
11
11
 
12
12
  import { createAgentExperience } from "./ui";
13
13
  import { createHistoryView } from "./components/history-view";
14
+ import { showHistoryConfirm } from "./components/history-confirm-dialog";
14
15
  import { setHistoryViewLoader } from "./history-view-loader";
15
16
  import { setHistoryProviderFactory } from "./internal/history-provider-registry";
16
17
  import {
@@ -118,7 +119,7 @@ describe("features.history.provider", () => {
118
119
  window.scrollTo = vi.fn();
119
120
  // No registry override: this suite exercises the public config path only.
120
121
  setHistoryProviderFactory(null);
121
- setHistoryViewLoader(async () => ({ createHistoryView }));
122
+ setHistoryViewLoader(async () => ({ createHistoryView, showHistoryConfirm }));
122
123
  });
123
124
 
124
125
  afterEach(() => {
@@ -0,0 +1,142 @@
1
+ // @vitest-environment jsdom
2
+
3
+ /**
4
+ * Version skew on the CDN: a fresh `index.global.js` paired with a cached
5
+ * `history-view.js` that predates the `showHistoryConfirm` export. Own file
6
+ * because `loadHistoryView` memoizes the resolved module per module graph.
7
+ */
8
+
9
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
10
+
11
+ import { createAgentExperience } from "./ui";
12
+ import { createHistoryView } from "./components/history-view";
13
+ import { setHistoryViewLoader } from "./history-view-loader";
14
+ import type { HistoryViewOptions } from "./history-view-entry";
15
+ import { setHistoryProviderFactory } from "./internal/history-provider-registry";
16
+ import {
17
+ createDemoHistoryProvider,
18
+ type DemoHistoryConversationSeed,
19
+ } from "./internal/demo-history-provider";
20
+
21
+ const SEEDS: DemoHistoryConversationSeed[] = [
22
+ {
23
+ id: "conv-a",
24
+ title: "Order status",
25
+ targetId: null,
26
+ messages: [{ id: "a1", role: "user", content: "where is my order" }],
27
+ },
28
+ ];
29
+
30
+ const flush = async (times = 12) => {
31
+ for (let i = 0; i < times; i += 1) {
32
+ // eslint-disable-next-line no-await-in-loop
33
+ await Promise.resolve();
34
+ }
35
+ };
36
+
37
+ type HistoryViewModule = Awaited<
38
+ ReturnType<Parameters<typeof setHistoryViewLoader>[0]>
39
+ >;
40
+
41
+ describe("history confirm with a stale chunk", () => {
42
+ let controller: ReturnType<typeof createAgentExperience> | null = null;
43
+ let mount: HTMLElement | null = null;
44
+
45
+ beforeEach(() => {
46
+ window.scrollTo = vi.fn();
47
+ setHistoryProviderFactory(() =>
48
+ createDemoHistoryProvider({ conversations: SEEDS })
49
+ );
50
+ });
51
+
52
+ afterEach(() => {
53
+ setHistoryProviderFactory(null);
54
+ controller?.destroy();
55
+ controller = null;
56
+ mount?.remove();
57
+ mount = null;
58
+ document.body.innerHTML = "";
59
+ vi.restoreAllMocks();
60
+ });
61
+
62
+ // Runs first: a resolved chunk is memoized for the rest of the module graph,
63
+ // so the rejecting loader only takes effect before any successful load.
64
+ it("falls back to the native confirm when the chunk fails to load", async () => {
65
+ setHistoryViewLoader(async () => {
66
+ throw new Error("chunk fetch failed");
67
+ });
68
+ mount = document.createElement("div");
69
+ document.body.appendChild(mount);
70
+ controller = createAgentExperience(mount, {
71
+ apiUrl: "https://api.example.com/chat",
72
+ launcher: { enabled: false },
73
+ persistState: false,
74
+ features: { history: { enabled: true } },
75
+ } as unknown as Parameters<typeof createAgentExperience>[1]);
76
+ await controller.openConversation("conv-a");
77
+ await flush();
78
+
79
+ // The title-menu delete needs no history view: it goes straight to the
80
+ // confirm, whose chunk load rejects.
81
+ const deleteFromTitleMenu = () =>
82
+ mount!
83
+ .querySelector('[data-persona-theme-zone="header"]')!
84
+ .dispatchEvent(
85
+ new CustomEvent("persona:title-menu-builtin", {
86
+ bubbles: true,
87
+ detail: { actionId: "delete" },
88
+ })
89
+ );
90
+ const listIds = async () =>
91
+ (await controller!.listConversations({ limit: 10 })).items.map(
92
+ (item) => item.id
93
+ );
94
+
95
+ const confirmSpy = vi.spyOn(window, "confirm").mockReturnValue(false);
96
+ deleteFromTitleMenu();
97
+ await flush();
98
+ expect(confirmSpy).toHaveBeenCalledTimes(1);
99
+ expect(await listIds()).toContain("conv-a");
100
+
101
+ confirmSpy.mockReturnValue(true);
102
+ deleteFromTitleMenu();
103
+ await flush();
104
+ expect(confirmSpy).toHaveBeenCalledTimes(2);
105
+ expect(await listIds()).not.toContain("conv-a");
106
+ });
107
+
108
+ it("falls back to the native confirm when the chunk lacks showHistoryConfirm", async () => {
109
+ let viewOptions: HistoryViewOptions | null = null;
110
+ // Older chunk shape: no showHistoryConfirm export.
111
+ setHistoryViewLoader(
112
+ async () =>
113
+ ({
114
+ createHistoryView: (options: HistoryViewOptions) => {
115
+ viewOptions = options;
116
+ return createHistoryView(options);
117
+ },
118
+ }) as unknown as HistoryViewModule
119
+ );
120
+ mount = document.createElement("div");
121
+ document.body.appendChild(mount);
122
+ controller = createAgentExperience(mount, {
123
+ apiUrl: "https://api.example.com/chat",
124
+ launcher: { enabled: false },
125
+ persistState: false,
126
+ features: { history: { enabled: true } },
127
+ } as unknown as Parameters<typeof createAgentExperience>[1]);
128
+
129
+ mount.querySelector<HTMLButtonElement>("[data-persona-history-toggle]")!.click();
130
+ await flush();
131
+ expect(viewOptions).not.toBeNull();
132
+ const requestClear = viewOptions!.onRequestClearHistory!;
133
+
134
+ const confirmSpy = vi.spyOn(window, "confirm").mockReturnValue(false);
135
+ await expect(requestClear()).resolves.toBe("cancelled");
136
+ expect(confirmSpy).toHaveBeenCalledTimes(1);
137
+
138
+ confirmSpy.mockReturnValue(true);
139
+ await expect(requestClear()).resolves.toBe("cleared");
140
+ expect(confirmSpy).toHaveBeenCalledTimes(2);
141
+ });
142
+ });
@@ -13,6 +13,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
13
13
  import { createAgentExperience } from "./ui";
14
14
  import { initAgentWidget } from "./runtime/init";
15
15
  import { createHistoryView } from "./components/history-view";
16
+ import { showHistoryConfirm } from "./components/history-confirm-dialog";
16
17
  import { setHistoryViewLoader } from "./history-view-loader";
17
18
  import { setHistoryProviderFactory } from "./internal/history-provider-registry";
18
19
  import {
@@ -87,7 +88,7 @@ const setup = (
87
88
  describe("history controller API", () => {
88
89
  beforeEach(() => {
89
90
  window.scrollTo = vi.fn();
90
- setHistoryViewLoader(async () => ({ createHistoryView }));
91
+ setHistoryViewLoader(async () => ({ createHistoryView, showHistoryConfirm }));
91
92
  });
92
93
 
93
94
  afterEach(() => {
@@ -52,6 +52,7 @@ vi.mock("./utils/visitor-store", async (importOriginal) => {
52
52
 
53
53
  import { createAgentExperience } from "./ui";
54
54
  import { createHistoryView } from "./components/history-view";
55
+ import { showHistoryConfirm } from "./components/history-confirm-dialog";
55
56
  import { setHistoryViewLoader } from "./history-view-loader";
56
57
  import { setHistoryProviderFactory } from "./internal/history-provider-registry";
57
58
  import { createDemoHistoryProvider } from "./internal/demo-history-provider";
@@ -109,7 +110,7 @@ const mount = (config: Record<string, unknown>) => {
109
110
  describe("history controller lifecycle", () => {
110
111
  beforeEach(() => {
111
112
  window.scrollTo = vi.fn();
112
- setHistoryViewLoader(async () => ({ createHistoryView }));
113
+ setHistoryViewLoader(async () => ({ createHistoryView, showHistoryConfirm }));
113
114
  installFetch();
114
115
  });
115
116
 
@@ -11,6 +11,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
11
11
 
12
12
  import { createAgentExperience } from "./ui";
13
13
  import { createHistoryView } from "./components/history-view";
14
+ import { showHistoryConfirm } from "./components/history-confirm-dialog";
14
15
  import { setHistoryViewLoader } from "./history-view-loader";
15
16
  import { setHistoryProviderFactory } from "./internal/history-provider-registry";
16
17
  import {
@@ -127,7 +128,7 @@ const fullViewPlugin = (
127
128
  describe("history render hooks", () => {
128
129
  beforeEach(() => {
129
130
  window.scrollTo = vi.fn();
130
- setHistoryViewLoader(async () => ({ createHistoryView }));
131
+ setHistoryViewLoader(async () => ({ createHistoryView, showHistoryConfirm }));
131
132
  });
132
133
 
133
134
  afterEach(() => {
@@ -13,6 +13,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
13
13
 
14
14
  import { createAgentExperience } from "./ui";
15
15
  import { createHistoryView } from "./components/history-view";
16
+ import { showHistoryConfirm } from "./components/history-confirm-dialog";
16
17
  import { setHistoryViewLoader } from "./history-view-loader";
17
18
  import { setHistoryProviderFactory } from "./internal/history-provider-registry";
18
19
  import {
@@ -169,7 +170,7 @@ describe("history shell", () => {
169
170
  beforeEach(() => {
170
171
  window.scrollTo = vi.fn();
171
172
  // The loader's production path is a sibling-URL / external-subpath import.
172
- setHistoryViewLoader(async () => ({ createHistoryView }));
173
+ setHistoryViewLoader(async () => ({ createHistoryView, showHistoryConfirm }));
173
174
  });
174
175
 
175
176
  afterEach(() => {
@@ -12,6 +12,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
12
12
 
13
13
  import { createAgentExperience } from "./ui";
14
14
  import { createHistoryView } from "./components/history-view";
15
+ import { showHistoryConfirm } from "./components/history-confirm-dialog";
15
16
  import { setHistoryViewLoader } from "./history-view-loader";
16
17
  import { setHistoryProviderFactory } from "./internal/history-provider-registry";
17
18
  import {
@@ -53,7 +54,7 @@ const wait = (ms: number) =>
53
54
  /** Past the 300ms pointer-out grace. */
54
55
  const GRACE = 380;
55
56
 
56
- const loadHistoryChunk = vi.fn(async () => ({ createHistoryView }));
57
+ const loadHistoryChunk = vi.fn(async () => ({ createHistoryView, showHistoryConfirm }));
57
58
 
58
59
  const setup = (
59
60
  options: {
@@ -10,6 +10,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
10
10
 
11
11
  import { createAgentExperience } from "./ui";
12
12
  import { createHistoryView } from "./components/history-view";
13
+ import { showHistoryConfirm } from "./components/history-confirm-dialog";
13
14
  import { setHistoryViewLoader } from "./history-view-loader";
14
15
  import { setHistoryProviderFactory } from "./internal/history-provider-registry";
15
16
  import {
@@ -103,7 +104,7 @@ const drag = (handle: HTMLElement, dx: number, opts?: { hold?: boolean }) => {
103
104
  describe("rail resize handle", () => {
104
105
  beforeEach(() => {
105
106
  window.scrollTo = vi.fn();
106
- setHistoryViewLoader(async () => ({ createHistoryView }));
107
+ setHistoryViewLoader(async () => ({ createHistoryView, showHistoryConfirm }));
107
108
  });
108
109
 
109
110
  afterEach(() => {
@@ -13,6 +13,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
13
13
 
14
14
  import { createAgentExperience } from "./ui";
15
15
  import { createHistoryView } from "./components/history-view";
16
+ import { showHistoryConfirm } from "./components/history-confirm-dialog";
16
17
  import { setHistoryViewLoader } from "./history-view-loader";
17
18
  import { setHistoryProviderFactory } from "./internal/history-provider-registry";
18
19
  import { createDemoHistoryProvider } from "./internal/demo-history-provider";
@@ -103,7 +104,7 @@ describe("rail trigger during a docking open", () => {
103
104
  setHistoryViewLoader(
104
105
  () =>
105
106
  new Promise((resolve) => {
106
- release = () => resolve({ createHistoryView });
107
+ release = () => resolve({ createHistoryView, showHistoryConfirm });
107
108
  })
108
109
  );
109
110
  const { controller: ctl } = setup();