@runtypelabs/persona 4.24.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 (237) 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 +243 -8
  12. package/dist/approval-ui.d.ts +243 -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 +243 -8
  17. package/dist/artifacts-ui.d.ts +243 -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 +243 -8
  46. package/dist/context-mentions.d.ts +243 -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 +246 -31
  51. package/dist/event-stream-view.d.ts +246 -31
  52. package/dist/event-stream-view.js +1 -1
  53. package/dist/forms-ui.d.cts +295 -667
  54. package/dist/forms-ui.d.ts +295 -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 +301 -738
  65. package/dist/index.d.ts +301 -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 +243 -8
  80. package/dist/session-reconnect.d.ts +243 -8
  81. package/dist/smart-dom-reader.cjs +13 -13
  82. package/dist/smart-dom-reader.d.cts +243 -8
  83. package/dist/smart-dom-reader.d.ts +243 -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 +245 -8
  87. package/dist/theme-editor-preview.d.ts +245 -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 +244 -23
  91. package/dist/theme-editor.d.ts +244 -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 +11643 -192
  100. package/dist/voice-runtime.d.ts +11643 -192
  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 +8 -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 +875 -1530
  151. package/src/session.voice.test.ts +1241 -5
  152. package/src/styles/widget.css +58 -0
  153. package/src/theme-editor/state.ts +91 -91
  154. package/src/types.ts +269 -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 +399 -2770
  172. package/src/ui.voice-prewarm.test.ts +222 -0
  173. package/src/utils/attachment-manager.ts +67 -67
  174. package/src/utils/constants.ts +3 -0
  175. package/src/utils/context-mention-controller.ts +323 -323
  176. package/src/utils/context-mention-manager.ts +40 -40
  177. package/src/utils/context-mention-orchestrator.ts +28 -0
  178. package/src/utils/event-stream-buffer.ts +50 -50
  179. package/src/utils/event-stream-store.ts +43 -43
  180. package/src/utils/throughput-tracker.ts +34 -34
  181. package/src/utils/virtual-scroller.ts +67 -67
  182. package/src/utils/visitor-store.test.ts +50 -0
  183. package/src/utils/visitor-store.ts +23 -5
  184. package/src/voice/__fixtures__/voice-wire/README.md +1 -0
  185. package/src/voice/__fixtures__/voice-wire/client/cancel.json +3 -0
  186. package/src/voice/__fixtures__/voice-wire/client/context.json +4 -0
  187. package/src/voice/__fixtures__/voice-wire/client/delegation_result.json +6 -0
  188. package/src/voice/__fixtures__/voice-wire/client/delegation_update.json +6 -0
  189. package/src/voice/__fixtures__/voice-wire/client/ping.json +3 -0
  190. package/src/voice/__fixtures__/voice-wire/client/playback_progress.json +5 -0
  191. package/src/voice/__fixtures__/voice-wire/client/start.json +3 -0
  192. package/src/voice/__fixtures__/voice-wire/server/artifact.json +11 -0
  193. package/src/voice/__fixtures__/voice-wire/server/attached.json +4 -0
  194. package/src/voice/__fixtures__/voice-wire/server/audio_clear.json +4 -0
  195. package/src/voice/__fixtures__/voice-wire/server/audio_end.json +3 -0
  196. package/src/voice/__fixtures__/voice-wire/server/delegation_cancelled.json +5 -0
  197. package/src/voice/__fixtures__/voice-wire/server/delegation_completed.json +8 -0
  198. package/src/voice/__fixtures__/voice-wire/server/delegation_started.json +15 -0
  199. package/src/voice/__fixtures__/voice-wire/server/error.json +6 -0
  200. package/src/voice/__fixtures__/voice-wire/server/metrics.json +10 -0
  201. package/src/voice/__fixtures__/voice-wire/server/pong.json +3 -0
  202. package/src/voice/__fixtures__/voice-wire/server/session_config.json +16 -0
  203. package/src/voice/__fixtures__/voice-wire/server/transcript_final.json +7 -0
  204. package/src/voice/__fixtures__/voice-wire/server/transcript_interim.json +4 -0
  205. package/src/voice/__fixtures__/voice-wire/server/transcript_partial.json +6 -0
  206. package/src/voice/__fixtures__/voice-wire/server/transcript_update.json +10 -0
  207. package/src/voice/__fixtures__/voice-wire/server/warning.json +5 -0
  208. package/src/voice/audio-playback-manager.test.ts +57 -0
  209. package/src/voice/audio-playback-manager.ts +120 -98
  210. package/src/voice/browser-voice-provider.ts +37 -37
  211. package/src/voice/fallback-speech-engine.ts +22 -16
  212. package/src/voice/keyed-voice-transcript.ts +314 -0
  213. package/src/voice/mic-state-styles.ts +182 -0
  214. package/src/voice/read-aloud-controller.ts +46 -44
  215. package/src/voice/runtype-speech-engine.ts +35 -33
  216. package/src/voice/runtype-voice-provider.ts +856 -164
  217. package/src/voice/session-voice-wiring.ts +266 -0
  218. package/src/voice/voice-delegation.test.ts +71 -0
  219. package/src/voice/voice-delegation.ts +364 -0
  220. package/src/voice/voice-turn-tracker.ts +6 -6
  221. package/src/voice/voice.test.ts +1250 -7
  222. package/src/voice/worklet-playback-engine.test.ts +81 -0
  223. package/src/voice/worklet-playback-engine.ts +21 -3
  224. package/src/voice-runtime-loader.ts +4 -1
  225. package/src/voice-runtime.ts +7 -0
  226. package/src/webmcp-runtime-entry.ts +41 -39
  227. package/dist/approval-ui-3NMYNXBD.js +0 -1
  228. package/dist/chunk-5EIIHQLQ.js +0 -1
  229. package/dist/chunk-7CO4T5KB.js +0 -1
  230. package/dist/chunk-EFJBUH5F.js +0 -1
  231. package/dist/chunk-R2TMIMDI.js +0 -3
  232. package/dist/chunk-UTVLOTWB.js +0 -9
  233. package/dist/context-mentions-45MODWOY.js +0 -161
  234. package/dist/event-stream-view-GL3ADRY2.js +0 -1
  235. package/dist/runtype-tts-entry-UJAEF7NZ.js +0 -1
  236. package/dist/voice-runtime-EFR3R5ES.js +0 -1
  237. package/dist/webmcp-runtime-JI7T4QYF.js +0 -6
@@ -95,16 +95,16 @@ const mentionKey = (sourceId: string, itemId: string) => `${sourceId}\u0000${ite
95
95
  * that's the controller's job.
96
96
  */
97
97
  export class ContextMentionManager {
98
- private readonly opts: ContextMentionManagerOptions;
98
+ readonly #opts: ContextMentionManagerOptions;
99
99
  private readonly mentions: PendingMention[] = [];
100
100
 
101
101
  constructor(opts: ContextMentionManagerOptions) {
102
- this.opts = opts;
103
- this.updateRowVisibility();
102
+ this.#opts = opts;
103
+ this.#updateRowVisibility();
104
104
  }
105
105
 
106
- private get maxMentions(): number {
107
- return this.opts.mentionConfig.maxMentions ?? 8;
106
+ get #maxMentions(): number {
107
+ return this.#opts.mentionConfig.maxMentions ?? 8;
108
108
  }
109
109
 
110
110
  hasMentions(): boolean {
@@ -123,9 +123,9 @@ export class ContextMentionManager {
123
123
  ): boolean {
124
124
  const key = mentionKey(source.id, item.id);
125
125
  if (this.mentions.some((m) => m.key === key)) {
126
- return this.reject(source, item, "duplicate");
126
+ return this.#reject(source, item, "duplicate");
127
127
  }
128
- if (this.atLimit()) return this.reject(source, item, "limit");
128
+ if (this.atLimit()) return this.#reject(source, item, "limit");
129
129
 
130
130
  const ref = refFromItem(source, item);
131
131
  const pending: PendingMention = {
@@ -138,7 +138,7 @@ export class ContextMentionManager {
138
138
  };
139
139
  pending.chip = createMentionChip({
140
140
  ref,
141
- config: this.opts.mentionConfig,
141
+ config: this.#opts.mentionConfig,
142
142
  onRemove: () => this.remove(key),
143
143
  });
144
144
  // Mention chips are always new when appended (a rebuild clears them with
@@ -149,17 +149,17 @@ export class ContextMentionManager {
149
149
  () => pending.chip?.el.removeAttribute("data-persona-chip-enter"),
150
150
  { once: true }
151
151
  );
152
- this.opts.contextRow.appendChild(pending.chip.el);
152
+ this.#opts.contextRow.appendChild(pending.chip.el);
153
153
  // startPending pushes into this.mentions, which updateRowVisibility reads —
154
154
  // reversed order left the row hidden until the second chip.
155
- this.startPending(pending);
156
- this.updateRowVisibility();
155
+ this.#startPending(pending);
156
+ this.#updateRowVisibility();
157
157
  return true;
158
158
  }
159
159
 
160
160
  /** True when the mention limit is already reached (inline pre-insert gate). */
161
161
  atLimit(): boolean {
162
- return this.mentions.length >= this.maxMentions;
162
+ return this.mentions.length >= this.#maxMentions;
163
163
  }
164
164
 
165
165
  /**
@@ -177,7 +177,7 @@ export class ContextMentionManager {
177
177
  source: AgentWidgetContextMentionSource,
178
178
  item: AgentWidgetContextMentionItem
179
179
  ): boolean {
180
- if (this.atLimit()) return this.reject(source, item, "limit");
180
+ if (this.atLimit()) return this.#reject(source, item, "limit");
181
181
  return true;
182
182
  }
183
183
 
@@ -204,17 +204,17 @@ export class ContextMentionManager {
204
204
  args,
205
205
  reportStatus,
206
206
  };
207
- this.startPending(pending);
207
+ this.#startPending(pending);
208
208
  }
209
209
 
210
210
  /** Fire the rejection hooks for a blocked mention and return false (add's sentinel). */
211
- private reject(
211
+ #reject(
212
212
  source: AgentWidgetContextMentionSource,
213
213
  item: AgentWidgetContextMentionItem,
214
214
  reason: "duplicate" | "limit"
215
215
  ): false {
216
- this.opts.mentionConfig.onMentionRejected?.(item, reason);
217
- this.opts.emit?.("rejected", { sourceId: source.id, itemId: item.id, reason });
216
+ this.#opts.mentionConfig.onMentionRejected?.(item, reason);
217
+ this.#opts.emit?.("rejected", { sourceId: source.id, itemId: item.id, reason });
218
218
  return false;
219
219
  }
220
220
 
@@ -224,39 +224,39 @@ export class ContextMentionManager {
224
224
  * else starts the select-time resolve, keeping the promise so `finalize()`
225
225
  * awaits this exact resolve rather than firing a second one.
226
226
  */
227
- private startPending(pending: PendingMention): void {
227
+ #startPending(pending: PendingMention): void {
228
228
  this.mentions.push(pending);
229
229
  if (pending.source.resolveOn === "submit") {
230
230
  pending.status = "ready";
231
231
  pending.chip?.setStatus("ready");
232
232
  pending.reportStatus?.("resolved");
233
233
  } else {
234
- pending.resolvePromise = this.resolvePending(pending);
234
+ pending.resolvePromise = this.#resolvePending(pending);
235
235
  }
236
- this.opts.announce(`Added ${pending.ref.label} to context`);
236
+ this.#opts.announce(`Added ${pending.ref.label} to context`);
237
237
  }
238
238
 
239
- private buildResolveContext(
239
+ #buildResolveContext(
240
240
  signal: AbortSignal,
241
241
  args: string,
242
- composerText = this.opts.getComposerText()
242
+ composerText = this.#opts.getComposerText()
243
243
  ) {
244
244
  return {
245
- messages: this.opts.getMessages(),
246
- config: this.opts.getConfig(),
245
+ messages: this.#opts.getMessages(),
246
+ config: this.#opts.getConfig(),
247
247
  composerText,
248
248
  args,
249
249
  signal,
250
250
  };
251
251
  }
252
252
 
253
- private async resolvePending(pending: PendingMention): Promise<void> {
253
+ async #resolvePending(pending: PendingMention): Promise<void> {
254
254
  const abort = new AbortController();
255
255
  pending.abort = abort;
256
256
  try {
257
257
  const payload = await pending.source.resolve(
258
258
  pending.item,
259
- this.buildResolveContext(abort.signal, pending.args)
259
+ this.#buildResolveContext(abort.signal, pending.args)
260
260
  );
261
261
  if (abort.signal.aborted) return;
262
262
  pending.payload = payload;
@@ -272,11 +272,11 @@ export class ContextMentionManager {
272
272
  pending.reportStatus?.("error");
273
273
  // Speak the failure through the ASSERTIVE region — the visual error state
274
274
  // (chip/token color) is otherwise silent for screen-reader users.
275
- (this.opts.announceError ?? this.opts.announce)(
275
+ (this.#opts.announceError ?? this.#opts.announce)(
276
276
  `Couldn't attach ${pending.ref.label} to context`
277
277
  );
278
- this.opts.mentionConfig.onMentionResolveError?.(pending.item, error);
279
- this.opts.emit?.("resolve-error", {
278
+ this.#opts.mentionConfig.onMentionResolveError?.(pending.item, error);
279
+ this.#opts.emit?.("resolve-error", {
280
280
  sourceId: pending.source.id,
281
281
  itemId: pending.item.id,
282
282
  });
@@ -289,8 +289,8 @@ export class ContextMentionManager {
289
289
  const [pending] = this.mentions.splice(index, 1);
290
290
  pending.abort?.abort();
291
291
  pending.chip?.el.remove();
292
- this.updateRowVisibility();
293
- this.opts.announce(`Removed ${pending.ref.label} from context`);
292
+ this.#updateRowVisibility();
293
+ this.#opts.announce(`Removed ${pending.ref.label} from context`);
294
294
  }
295
295
 
296
296
  /** Currently tracked refs, in order. Read-only: does not detach anything. */
@@ -312,7 +312,7 @@ export class ContextMentionManager {
312
312
  m.chip?.el.remove();
313
313
  }
314
314
  this.mentions.length = 0;
315
- this.updateRowVisibility();
315
+ this.#updateRowVisibility();
316
316
  }
317
317
 
318
318
  /**
@@ -330,12 +330,12 @@ export class ContextMentionManager {
330
330
  collectForSubmit(): { refs: AgentWidgetContextMentionRef[]; finalize: () => Promise<MentionSubmitBundle> } {
331
331
  const snapshot = [...this.mentions];
332
332
  const refs = snapshot.map((m) => m.ref);
333
- const composerText = this.opts.getComposerText();
333
+ const composerText = this.#opts.getComposerText();
334
334
 
335
335
  // Detach: empty the composer chip row without aborting resolves.
336
336
  for (const m of snapshot) m.chip?.el.remove();
337
337
  this.mentions.length = 0;
338
- this.updateRowVisibility();
338
+ this.#updateRowVisibility();
339
339
 
340
340
  const finalize = async (): Promise<MentionSubmitBundle> => {
341
341
  // Resolve every mention concurrently: submit-deferred sources resolve now;
@@ -346,7 +346,7 @@ export class ContextMentionManager {
346
346
  if (m.source.resolveOn === "submit") {
347
347
  return await m.source.resolve(
348
348
  m.item,
349
- this.buildResolveContext(new AbortController().signal, m.args, composerText)
349
+ this.#buildResolveContext(new AbortController().signal, m.args, composerText)
350
350
  );
351
351
  }
352
352
  if (m.resolvePromise) await m.resolvePromise;
@@ -355,13 +355,13 @@ export class ContextMentionManager {
355
355
  // A throwing host `onMentionResolveError` must not reject the whole
356
356
  // bundle — guard the callback so one bad item just drops.
357
357
  try {
358
- this.opts.mentionConfig.onMentionResolveError?.(m.item, error);
358
+ this.#opts.mentionConfig.onMentionResolveError?.(m.item, error);
359
359
  } catch (cbError) {
360
360
  if (typeof console !== "undefined") {
361
361
  console.warn("[Persona] onMentionResolveError callback threw", cbError);
362
362
  }
363
363
  }
364
- this.opts.emit?.("resolve-error", {
364
+ this.#opts.emit?.("resolve-error", {
365
365
  sourceId: m.source.id,
366
366
  itemId: m.item.id,
367
367
  });
@@ -398,7 +398,7 @@ export class ContextMentionManager {
398
398
  formatMentionBlock(
399
399
  { label: m.ref.label, text: payload.llmAppend, ref: m.ref, item: m.item },
400
400
  blocks.length,
401
- this.opts.mentionConfig.llmFormat
401
+ this.#opts.mentionConfig.llmFormat
402
402
  )
403
403
  );
404
404
  } catch (error) {
@@ -425,7 +425,7 @@ export class ContextMentionManager {
425
425
  * The row is shared with the composer's mode chips, so visibility follows the
426
426
  * row's children, never this manager's mention count.
427
427
  */
428
- private updateRowVisibility(): void {
429
- syncComposerChipRow(this.opts.contextRow);
428
+ #updateRowVisibility(): void {
429
+ syncComposerChipRow(this.#opts.contextRow);
430
430
  }
431
431
  }
@@ -76,6 +76,22 @@ export interface ContextMentionOrchestrator {
76
76
  destroy: () => void;
77
77
  }
78
78
 
79
+ export type ContextMentionOrchestratorDeps = {
80
+ loadContextMentions: typeof loadContextMentions;
81
+ loadContextMentionsInline: typeof loadContextMentionsInline;
82
+ renderLucideIcon: typeof renderLucideIcon;
83
+ createMentionButton: typeof createMentionButton;
84
+ };
85
+
86
+ // Call through the live module bindings (not captured references) so spies
87
+ // and late re-registration keep working.
88
+ const defaultDeps: ContextMentionOrchestratorDeps = {
89
+ loadContextMentions: () => loadContextMentions(),
90
+ loadContextMentionsInline: () => loadContextMentionsInline(),
91
+ renderLucideIcon: (...args) => renderLucideIcon(...args),
92
+ createMentionButton: (...args) => createMentionButton(...args),
93
+ };
94
+
79
95
  export function createContextMentionOrchestrator(opts: {
80
96
  config: AgentWidgetConfig;
81
97
  textarea: HTMLTextAreaElement;
@@ -94,7 +110,19 @@ export function createContextMentionOrchestrator(opts: {
94
110
  * Omit and the orchestrator creates a standalone row the caller must place.
95
111
  */
96
112
  chipRow?: HTMLElement;
113
+ /**
114
+ * Stateful collaborators (chunk loaders, icon registry, tooltip-backed
115
+ * button). The IIFE/CDN build loads this module in the lazy `ui-extras`
116
+ * chunk and passes core's instances so the chunk never uses its own copies.
117
+ */
118
+ deps?: ContextMentionOrchestratorDeps;
97
119
  }): ContextMentionOrchestrator | null {
120
+ const {
121
+ loadContextMentions,
122
+ loadContextMentionsInline,
123
+ renderLucideIcon,
124
+ createMentionButton,
125
+ } = opts.deps ?? defaultDeps;
98
126
  const mentionConfig = opts.config.contextMentions;
99
127
  if (!mentionConfig?.enabled) return null;
100
128
 
@@ -2,70 +2,70 @@ import type { SSEEventRecord } from "../types";
2
2
  import type { EventStreamStore } from "./event-stream-store";
3
3
 
4
4
  export class EventStreamBuffer {
5
- private buffer: SSEEventRecord[];
6
- private head = 0;
7
- private count = 0;
8
- private totalCaptured = 0;
9
- private eventTypesSet = new Set<string>();
10
- private readonly maxSize: number;
11
- private readonly store: EventStreamStore | null;
5
+ #buffer: SSEEventRecord[];
6
+ #head = 0;
7
+ #count = 0;
8
+ #totalCaptured = 0;
9
+ #eventTypesSet = new Set<string>();
10
+ readonly #maxSize: number;
11
+ readonly #store: EventStreamStore | null;
12
12
 
13
13
  constructor(maxSize = 2000, store: EventStreamStore | null = null) {
14
- this.maxSize = maxSize;
15
- this.buffer = new Array(maxSize);
16
- this.store = store;
14
+ this.#maxSize = maxSize;
15
+ this.#buffer = new Array(maxSize);
16
+ this.#store = store;
17
17
  }
18
18
 
19
19
  push(event: SSEEventRecord): void {
20
- this.buffer[this.head] = event;
21
- this.head = (this.head + 1) % this.maxSize;
22
- if (this.count < this.maxSize) {
23
- this.count++;
20
+ this.#buffer[this.#head] = event;
21
+ this.#head = (this.#head + 1) % this.#maxSize;
22
+ if (this.#count < this.#maxSize) {
23
+ this.#count++;
24
24
  }
25
- this.totalCaptured++;
26
- this.eventTypesSet.add(event.type);
27
- this.store?.put(event);
25
+ this.#totalCaptured++;
26
+ this.#eventTypesSet.add(event.type);
27
+ this.#store?.put(event);
28
28
  }
29
29
 
30
30
  getAll(): SSEEventRecord[] {
31
- if (this.count === 0) return [];
32
- if (this.count < this.maxSize) {
33
- return this.buffer.slice(0, this.count);
31
+ if (this.#count === 0) return [];
32
+ if (this.#count < this.#maxSize) {
33
+ return this.#buffer.slice(0, this.#count);
34
34
  }
35
35
  // Buffer is full, items wrap around
36
36
  return [
37
- ...this.buffer.slice(this.head, this.maxSize),
38
- ...this.buffer.slice(0, this.head)
37
+ ...this.#buffer.slice(this.#head, this.#maxSize),
38
+ ...this.#buffer.slice(0, this.#head)
39
39
  ];
40
40
  }
41
41
 
42
42
  async restore(): Promise<number> {
43
- if (!this.store) return 0;
44
- const events = await this.store.getAll();
43
+ if (!this.#store) return 0;
44
+ const events = await this.#store.getAll();
45
45
  if (events.length === 0) return 0;
46
46
 
47
47
  // Take the most recent maxSize events (store returns sorted by timestamp)
48
- const toLoad = events.length > this.maxSize
49
- ? events.slice(events.length - this.maxSize)
48
+ const toLoad = events.length > this.#maxSize
49
+ ? events.slice(events.length - this.#maxSize)
50
50
  : events;
51
51
 
52
52
  // Populate the ring buffer without writing back to the store
53
53
  for (const event of toLoad) {
54
- this.buffer[this.head] = event;
55
- this.head = (this.head + 1) % this.maxSize;
56
- if (this.count < this.maxSize) {
57
- this.count++;
54
+ this.#buffer[this.#head] = event;
55
+ this.#head = (this.#head + 1) % this.#maxSize;
56
+ if (this.#count < this.#maxSize) {
57
+ this.#count++;
58
58
  }
59
- this.eventTypesSet.add(event.type);
59
+ this.#eventTypesSet.add(event.type);
60
60
  }
61
- this.totalCaptured = events.length;
61
+ this.#totalCaptured = events.length;
62
62
 
63
63
  return toLoad.length;
64
64
  }
65
65
 
66
66
  getAllFromStore(): Promise<SSEEventRecord[]> {
67
- if (this.store) {
68
- return this.store.getAll();
67
+ if (this.#store) {
68
+ return this.#store.getAll();
69
69
  }
70
70
  return Promise.resolve(this.getAll());
71
71
  }
@@ -77,36 +77,36 @@ export class EventStreamBuffer {
77
77
  }
78
78
 
79
79
  getSize(): number {
80
- return this.count;
80
+ return this.#count;
81
81
  }
82
82
 
83
83
  getTotalCaptured(): number {
84
- return this.totalCaptured;
84
+ return this.#totalCaptured;
85
85
  }
86
86
 
87
87
  getEvictedCount(): number {
88
- return this.totalCaptured - this.count;
88
+ return this.#totalCaptured - this.#count;
89
89
  }
90
90
 
91
91
  clear(): void {
92
- this.buffer = new Array(this.maxSize);
93
- this.head = 0;
94
- this.count = 0;
95
- this.totalCaptured = 0;
96
- this.eventTypesSet.clear();
97
- this.store?.clear();
92
+ this.#buffer = new Array(this.#maxSize);
93
+ this.#head = 0;
94
+ this.#count = 0;
95
+ this.#totalCaptured = 0;
96
+ this.#eventTypesSet.clear();
97
+ this.#store?.clear();
98
98
  }
99
99
 
100
100
  destroy(): void {
101
- this.buffer = [];
102
- this.head = 0;
103
- this.count = 0;
104
- this.totalCaptured = 0;
105
- this.eventTypesSet.clear();
106
- this.store?.destroy();
101
+ this.#buffer = [];
102
+ this.#head = 0;
103
+ this.#count = 0;
104
+ this.#totalCaptured = 0;
105
+ this.#eventTypesSet.clear();
106
+ this.#store?.destroy();
107
107
  }
108
108
 
109
109
  getEventTypes(): string[] {
110
- return Array.from(this.eventTypesSet);
110
+ return Array.from(this.#eventTypesSet);
111
111
  }
112
112
  }
@@ -1,33 +1,33 @@
1
1
  import type { SSEEventRecord } from "../types";
2
2
 
3
3
  export class EventStreamStore {
4
- private db: IDBDatabase | null = null;
5
- private pendingWrites: SSEEventRecord[] = [];
6
- private flushScheduled = false;
7
- private isDestroyed = false;
8
- private readonly dbName: string;
9
- private readonly storeName: string;
4
+ #db: IDBDatabase | null = null;
5
+ #pendingWrites: SSEEventRecord[] = [];
6
+ #flushScheduled = false;
7
+ #isDestroyed = false;
8
+ readonly #dbName: string;
9
+ readonly #storeName: string;
10
10
 
11
11
  constructor(dbName = "persona-event-stream", storeName = "events") {
12
- this.dbName = dbName;
13
- this.storeName = storeName;
12
+ this.#dbName = dbName;
13
+ this.#storeName = storeName;
14
14
  }
15
15
 
16
16
  open(): Promise<void> {
17
17
  return new Promise((resolve, reject) => {
18
18
  try {
19
- const request = indexedDB.open(this.dbName, 1);
19
+ const request = indexedDB.open(this.#dbName, 1);
20
20
 
21
21
  request.onupgradeneeded = () => {
22
22
  const db = request.result;
23
- if (!db.objectStoreNames.contains(this.storeName)) {
24
- const store = db.createObjectStore(this.storeName, { keyPath: "id" });
23
+ if (!db.objectStoreNames.contains(this.#storeName)) {
24
+ const store = db.createObjectStore(this.#storeName, { keyPath: "id" });
25
25
  store.createIndex("timestamp", "timestamp", { unique: false });
26
26
  }
27
27
  };
28
28
 
29
29
  request.onsuccess = () => {
30
- this.db = request.result;
30
+ this.#db = request.result;
31
31
  resolve();
32
32
  };
33
33
 
@@ -41,19 +41,19 @@ export class EventStreamStore {
41
41
  }
42
42
 
43
43
  put(event: SSEEventRecord): void {
44
- if (!this.db || this.isDestroyed) return;
45
- this.pendingWrites.push(event);
46
- if (!this.flushScheduled) {
47
- this.flushScheduled = true;
48
- queueMicrotask(() => this.flushWrites());
44
+ if (!this.#db || this.#isDestroyed) return;
45
+ this.#pendingWrites.push(event);
46
+ if (!this.#flushScheduled) {
47
+ this.#flushScheduled = true;
48
+ queueMicrotask(() => this.#flushWrites());
49
49
  }
50
50
  }
51
51
 
52
52
  putBatch(events: SSEEventRecord[]): void {
53
- if (!this.db || this.isDestroyed || events.length === 0) return;
53
+ if (!this.#db || this.#isDestroyed || events.length === 0) return;
54
54
  try {
55
- const tx = this.db.transaction(this.storeName, "readwrite");
56
- const store = tx.objectStore(this.storeName);
55
+ const tx = this.#db.transaction(this.#storeName, "readwrite");
56
+ const store = tx.objectStore(this.#storeName);
57
57
  for (const event of events) {
58
58
  store.put(event);
59
59
  }
@@ -64,13 +64,13 @@ export class EventStreamStore {
64
64
 
65
65
  getAll(): Promise<SSEEventRecord[]> {
66
66
  return new Promise((resolve, reject) => {
67
- if (!this.db) {
67
+ if (!this.#db) {
68
68
  resolve([]);
69
69
  return;
70
70
  }
71
71
  try {
72
- const tx = this.db.transaction(this.storeName, "readonly");
73
- const store = tx.objectStore(this.storeName);
72
+ const tx = this.#db.transaction(this.#storeName, "readonly");
73
+ const store = tx.objectStore(this.#storeName);
74
74
  const index = store.index("timestamp");
75
75
  const request = index.getAll();
76
76
 
@@ -89,13 +89,13 @@ export class EventStreamStore {
89
89
 
90
90
  getCount(): Promise<number> {
91
91
  return new Promise((resolve, reject) => {
92
- if (!this.db) {
92
+ if (!this.#db) {
93
93
  resolve(0);
94
94
  return;
95
95
  }
96
96
  try {
97
- const tx = this.db.transaction(this.storeName, "readonly");
98
- const store = tx.objectStore(this.storeName);
97
+ const tx = this.#db.transaction(this.#storeName, "readonly");
98
+ const store = tx.objectStore(this.#storeName);
99
99
  const request = store.count();
100
100
 
101
101
  request.onsuccess = () => {
@@ -113,14 +113,14 @@ export class EventStreamStore {
113
113
 
114
114
  clear(): Promise<void> {
115
115
  return new Promise((resolve, reject) => {
116
- if (!this.db) {
116
+ if (!this.#db) {
117
117
  resolve();
118
118
  return;
119
119
  }
120
- this.pendingWrites = [];
120
+ this.#pendingWrites = [];
121
121
  try {
122
- const tx = this.db.transaction(this.storeName, "readwrite");
123
- const store = tx.objectStore(this.storeName);
122
+ const tx = this.#db.transaction(this.#storeName, "readwrite");
123
+ const store = tx.objectStore(this.#storeName);
124
124
  const request = store.clear();
125
125
 
126
126
  request.onsuccess = () => {
@@ -137,19 +137,19 @@ export class EventStreamStore {
137
137
  }
138
138
 
139
139
  close(): void {
140
- if (this.db) {
141
- this.db.close();
142
- this.db = null;
140
+ if (this.#db) {
141
+ this.#db.close();
142
+ this.#db = null;
143
143
  }
144
144
  }
145
145
 
146
146
  destroy(): Promise<void> {
147
- this.isDestroyed = true;
148
- this.pendingWrites = [];
147
+ this.#isDestroyed = true;
148
+ this.#pendingWrites = [];
149
149
  this.close();
150
150
  return new Promise((resolve, reject) => {
151
151
  try {
152
- const request = indexedDB.deleteDatabase(this.dbName);
152
+ const request = indexedDB.deleteDatabase(this.#dbName);
153
153
 
154
154
  request.onsuccess = () => {
155
155
  resolve();
@@ -164,14 +164,14 @@ export class EventStreamStore {
164
164
  });
165
165
  }
166
166
 
167
- private flushWrites(): void {
168
- this.flushScheduled = false;
169
- if (!this.db || this.isDestroyed || this.pendingWrites.length === 0) return;
170
- const toWrite = this.pendingWrites;
171
- this.pendingWrites = [];
167
+ #flushWrites(): void {
168
+ this.#flushScheduled = false;
169
+ if (!this.#db || this.#isDestroyed || this.#pendingWrites.length === 0) return;
170
+ const toWrite = this.#pendingWrites;
171
+ this.#pendingWrites = [];
172
172
  try {
173
- const tx = this.db.transaction(this.storeName, "readwrite");
174
- const store = tx.objectStore(this.storeName);
173
+ const tx = this.#db.transaction(this.#storeName, "readwrite");
174
+ const store = tx.objectStore(this.#storeName);
175
175
  for (const event of toWrite) {
176
176
  store.put(event);
177
177
  }