@runtypelabs/persona 4.7.0 → 4.9.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 (185) hide show
  1. package/README.md +1 -0
  2. package/dist/animations/glyph-cycle.cjs +2 -2
  3. package/dist/animations/glyph-cycle.d.cts +1 -1
  4. package/dist/animations/glyph-cycle.d.ts +1 -1
  5. package/dist/animations/glyph-cycle.js +2 -2
  6. package/dist/animations/{types-CSmiKRVa.d.cts → types-4ROVJ1gA.d.cts} +85 -3
  7. package/dist/animations/{types-CSmiKRVa.d.ts → types-4ROVJ1gA.d.ts} +85 -3
  8. package/dist/animations/wipe.cjs +2 -2
  9. package/dist/animations/wipe.d.cts +1 -1
  10. package/dist/animations/wipe.d.ts +1 -1
  11. package/dist/chunk-5EIIHQLQ.js +1 -0
  12. package/dist/chunk-IO5VVUKP.js +3 -0
  13. package/dist/chunk-IPVK3KOM.js +1 -0
  14. package/dist/chunk-UPO4GUFC.js +1 -0
  15. package/dist/codegen.cjs +13 -13
  16. package/dist/codegen.js +14 -14
  17. package/dist/context-mentions-7S5KVUTG.js +169 -0
  18. package/dist/context-mentions-inline-TTCN7ZM2.js +4 -0
  19. package/dist/context-mentions-inline.cjs +4 -0
  20. package/dist/context-mentions-inline.d.cts +203 -0
  21. package/dist/context-mentions-inline.d.ts +203 -0
  22. package/dist/context-mentions-inline.js +4 -0
  23. package/dist/context-mentions.cjs +295 -0
  24. package/dist/context-mentions.d.cts +7025 -0
  25. package/dist/context-mentions.d.ts +7025 -0
  26. package/dist/context-mentions.js +295 -0
  27. package/dist/index.cjs +99 -68
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +1507 -182
  30. package/dist/index.d.ts +1507 -182
  31. package/dist/index.global.js +88 -56
  32. package/dist/index.global.js.map +1 -1
  33. package/dist/index.js +99 -68
  34. package/dist/index.js.map +1 -1
  35. package/dist/install.global.js +1 -1
  36. package/dist/install.global.js.map +1 -1
  37. package/dist/launcher.global.js +3 -2
  38. package/dist/launcher.global.js.map +1 -1
  39. package/dist/markdown-parsers.js +24 -24
  40. package/dist/plugin-kit.cjs +1 -1
  41. package/dist/plugin-kit.d.cts +17 -0
  42. package/dist/plugin-kit.d.ts +17 -0
  43. package/dist/plugin-kit.js +1 -1
  44. package/dist/runtype-tts-entry-UJAEF7NZ.js +1 -0
  45. package/dist/runtype-tts.js +1 -1
  46. package/dist/session-reconnect-JKIJBHS5.js +1 -0
  47. package/dist/smart-dom-reader.cjs +18 -17
  48. package/dist/smart-dom-reader.d.cts +1260 -16
  49. package/dist/smart-dom-reader.d.ts +1260 -16
  50. package/dist/smart-dom-reader.js +18 -17
  51. package/dist/testing.cjs +3 -3
  52. package/dist/testing.js +3 -3
  53. package/dist/theme-editor-preview.cjs +260 -58
  54. package/dist/theme-editor-preview.d.cts +1234 -15
  55. package/dist/theme-editor-preview.d.ts +1234 -15
  56. package/dist/theme-editor-preview.js +87 -58
  57. package/dist/theme-editor.cjs +10 -10
  58. package/dist/theme-editor.d.cts +1226 -15
  59. package/dist/theme-editor.d.ts +1226 -15
  60. package/dist/theme-editor.js +10 -10
  61. package/dist/theme-reference.cjs +1 -1
  62. package/dist/theme-reference.d.cts +76 -0
  63. package/dist/theme-reference.d.ts +76 -0
  64. package/dist/theme-reference.js +1 -1
  65. package/dist/voice-worklet-player.cjs +2 -2
  66. package/dist/voice-worklet-player.js +2 -2
  67. package/dist/webmcp-polyfill.js +2 -2
  68. package/dist/widget.css +1 -1
  69. package/package.json +16 -5
  70. package/src/artifacts-session.test.ts +178 -0
  71. package/src/client.test.ts +255 -1
  72. package/src/client.ts +157 -60
  73. package/src/components/artifact-card.test.ts +333 -0
  74. package/src/components/artifact-card.ts +75 -28
  75. package/src/components/artifact-inline.test.ts +1328 -0
  76. package/src/components/artifact-inline.ts +920 -0
  77. package/src/components/artifact-pane.test.ts +1042 -0
  78. package/src/components/artifact-pane.ts +440 -131
  79. package/src/components/artifact-preview.test.ts +1155 -0
  80. package/src/components/artifact-preview.ts +994 -0
  81. package/src/components/composer-parts.ts +3 -12
  82. package/src/components/context-mention-button.test.ts +70 -0
  83. package/src/components/context-mention-button.ts +82 -0
  84. package/src/components/context-mention-chip.ts +134 -0
  85. package/src/components/context-mention-menu.test.ts +508 -0
  86. package/src/components/context-mention-menu.ts +0 -0
  87. package/src/components/message-bubble.test.ts +175 -0
  88. package/src/components/message-bubble.ts +177 -19
  89. package/src/components/panel.ts +7 -10
  90. package/src/components/pill-composer-builder.test.ts +6 -2
  91. package/src/components/pill-composer-builder.ts +6 -6
  92. package/src/components/reasoning-bubble.ts +1 -13
  93. package/src/components/registry.ts +38 -3
  94. package/src/components/tool-bubble.ts +1 -13
  95. package/src/context-mentions-bundle.test.ts +163 -0
  96. package/src/context-mentions-entry.ts +185 -0
  97. package/src/context-mentions-inline-entry.test.ts +136 -0
  98. package/src/context-mentions-inline-entry.ts +226 -0
  99. package/src/context-mentions-inline-loader.test.ts +30 -0
  100. package/src/context-mentions-inline-loader.ts +36 -0
  101. package/src/context-mentions-inline.ts +15 -0
  102. package/src/context-mentions-loader.ts +32 -0
  103. package/src/context-mentions.ts +16 -0
  104. package/src/defaults.ts +1 -0
  105. package/src/generated/runtype-openapi-contract.ts +55 -3
  106. package/src/index-core.ts +46 -1
  107. package/src/index-global.ts +51 -0
  108. package/src/index.ts +8 -0
  109. package/src/markdown-parsers-loader.test.ts +158 -0
  110. package/src/markdown-parsers-loader.ts +83 -27
  111. package/src/plugin-kit.test.ts +40 -0
  112. package/src/plugin-kit.ts +39 -5
  113. package/src/runtime/host-layout.test.ts +163 -0
  114. package/src/runtime/host-layout.ts +110 -7
  115. package/src/runtime/init.ts +18 -61
  116. package/src/session.mentions.test.ts +175 -0
  117. package/src/session.test.ts +52 -4
  118. package/src/session.ts +197 -27
  119. package/src/smart-dom-reader.test.ts +129 -2
  120. package/src/smart-dom-reader.ts +127 -1
  121. package/src/styles/context-mention-menu-css.ts +176 -0
  122. package/src/styles/widget.css +1002 -152
  123. package/src/theme-editor/preview.ts +2 -0
  124. package/src/theme-editor/sections.test.ts +26 -1
  125. package/src/theme-editor/sections.ts +13 -5
  126. package/src/theme-reference.ts +2 -2
  127. package/src/tool-call-display-defaults.test.ts +1 -0
  128. package/src/types/theme.ts +79 -0
  129. package/src/types.ts +1041 -17
  130. package/src/ui.artifact-pane-gating.test.ts +636 -0
  131. package/src/ui.component-directive.test.ts +104 -0
  132. package/src/ui.composer-bar.test.ts +60 -2
  133. package/src/ui.detached-panel.test.ts +1049 -0
  134. package/src/ui.mention-submit.test.ts +235 -0
  135. package/src/ui.tool-display.test.ts +51 -0
  136. package/src/ui.ts +998 -159
  137. package/src/utils/artifact-custom-actions.ts +128 -0
  138. package/src/utils/artifact-display.test.ts +42 -0
  139. package/src/utils/artifact-display.ts +84 -0
  140. package/src/utils/artifact-file.test.ts +116 -0
  141. package/src/utils/artifact-file.ts +117 -0
  142. package/src/utils/artifact-gate.test.ts +112 -5
  143. package/src/utils/artifact-gate.ts +39 -14
  144. package/src/utils/artifact-loading-status.ts +55 -0
  145. package/src/utils/artifact-status-label.ts +190 -0
  146. package/src/utils/buttons.ts +7 -1
  147. package/src/utils/chunk-loader.test.ts +97 -0
  148. package/src/utils/chunk-loader.ts +88 -0
  149. package/src/utils/code-highlight.test.ts +186 -0
  150. package/src/utils/code-highlight.ts +400 -0
  151. package/src/utils/composer-contenteditable.test.ts +507 -0
  152. package/src/utils/composer-contenteditable.ts +626 -0
  153. package/src/utils/composer-document.test.ts +280 -0
  154. package/src/utils/composer-document.ts +293 -0
  155. package/src/utils/composer-history.test.ts +35 -7
  156. package/src/utils/composer-history.ts +30 -20
  157. package/src/utils/composer-input.ts +159 -0
  158. package/src/utils/context-mention-controller.test.ts +1215 -0
  159. package/src/utils/context-mention-controller.ts +1186 -0
  160. package/src/utils/context-mention-manager.test.ts +422 -0
  161. package/src/utils/context-mention-manager.ts +410 -0
  162. package/src/utils/context-mention-orchestrator.test.ts +538 -0
  163. package/src/utils/context-mention-orchestrator.ts +348 -0
  164. package/src/utils/icons.ts +2 -0
  165. package/src/utils/live-region.test.ts +108 -0
  166. package/src/utils/live-region.ts +94 -0
  167. package/src/utils/mention-channels.ts +63 -0
  168. package/src/utils/mention-llm-format.test.ts +91 -0
  169. package/src/utils/mention-llm-format.ts +79 -0
  170. package/src/utils/mention-matcher.test.ts +86 -0
  171. package/src/utils/mention-matcher.ts +221 -0
  172. package/src/utils/mention-token.ts +72 -0
  173. package/src/utils/mention-trigger.test.ts +155 -0
  174. package/src/utils/mention-trigger.ts +156 -0
  175. package/src/utils/roving-tablist.test.ts +152 -0
  176. package/src/utils/roving-tablist.ts +111 -0
  177. package/src/utils/spinner.ts +45 -0
  178. package/src/utils/theme.test.ts +76 -4
  179. package/src/utils/theme.ts +7 -0
  180. package/src/utils/tokens.ts +118 -11
  181. package/src/utils/tool-loading-animation.test.ts +32 -0
  182. package/src/utils/tool-loading-animation.ts +24 -0
  183. package/dist/chunk-DFBSCFYN.js +0 -1
  184. package/dist/runtype-tts-entry-HFUV2UF7.js +0 -1
  185. package/dist/session-reconnect-U77QFUR7.js +0 -1
package/src/types.ts CHANGED
@@ -6,6 +6,7 @@ import type {
6
6
  RuntypeStopReasonKind,
7
7
  } from "./generated/runtype-openapi-contract";
8
8
  import type { TargetResolver } from "./utils/target";
9
+ import type { IconName } from "./utils/icons";
9
10
 
10
11
  export type { TargetResolver, ResolvedTarget } from "./utils/target";
11
12
 
@@ -94,6 +95,500 @@ export type AgentWidgetContextProvider = (
94
95
  | void
95
96
  | Promise<Record<string, unknown> | void>;
96
97
 
98
+ // ============================================================================
99
+ // Context Mentions (composer @-mentions / context attachment)
100
+ // ============================================================================
101
+ //
102
+ // Two entry points (a visible affordance button and the `@` trigger) over one
103
+ // shared menu. Selecting a mention strips the typed `@query` and adds a compact
104
+ // removable pill chip to the composer context row; the resolved content reaches
105
+ // the model via `llmAppend`/`contentParts` (default, no backend changes) or the
106
+ // opt-in structured `context` channel. See `docs/context-mentions-plan.md`.
107
+
108
+ /** A single selectable item in the mention menu (host-provided). */
109
+ export type AgentWidgetContextMentionItem = {
110
+ /** Stable key within its source. */
111
+ id: string;
112
+ /** Shown in the menu and on the chip (e.g. "App.tsx"). */
113
+ label: string;
114
+ /** Subtitle line in the menu. */
115
+ description?: string;
116
+ /** Lucide icon name. */
117
+ iconName?: string;
118
+ /**
119
+ * Accent color for this item's inline token (`display: "inline"`), any CSS
120
+ * color. Sets `--persona-mention-token-accent` on the token, tinting its pill
121
+ * background + icon/label — the per-item/per-type color knob (Slack/Linear
122
+ * style). Prefer setting the same color across a source's items for a "color
123
+ * per type" look, or theme by `[data-mention-source]` in CSS instead. Ignored
124
+ * in chip mode.
125
+ */
126
+ color?: string;
127
+ /** Group header override; defaults to the source's `label`. */
128
+ group?: string;
129
+ /** Optional recency/popularity hint the default matcher boosts on. */
130
+ recencyScore?: number;
131
+ /**
132
+ * SKILLS / SLASH-COMMANDS. When set, this item is a COMMAND (a verb) rather
133
+ * than a context mention (a noun), so selecting it dispatches a behavior
134
+ * instead of adding a context chip. Absent for ordinary mentions.
135
+ *
136
+ * - `"prompt"`: a prompt macro. `resolve()` returns `insertText` (or
137
+ * `llmAppend`) which is written into the composer (see `insertMode`);
138
+ * `submitOnSelect` optionally sends it immediately. No chip.
139
+ * - `"action"`: a client action. `action()` runs in the browser; no resolve,
140
+ * no chip, no message sent (e.g. `/clear`, `/theme dark`).
141
+ * - `"server"`: a server-invoked skill. Routed like a `resolveOn:"submit"`
142
+ * mention whose `resolve().context` reaches the backend via request
143
+ * `context.mentions.<sourceId>.<itemId>` (a flow/agent reads it).
144
+ */
145
+ command?: "prompt" | "action" | "server";
146
+ /**
147
+ * `command:"action"` handler. Receives the parsed `args` (text after the
148
+ * command name), plus a small `composer` capability + `config`/`messages`.
149
+ * Short-circuits the mention flow (no chip, no send). Ignored otherwise.
150
+ */
151
+ action?: (ctx: AgentWidgetContextMentionCommandContext) => void | Promise<void>;
152
+ /**
153
+ * `command:"prompt"` insertion mode. `"replace"` (default) swaps the whole
154
+ * composer value for the resolved text; `"insert-at-caret"` replaces only the
155
+ * `/token` span, keeping surrounding prose. Ignored otherwise.
156
+ */
157
+ insertMode?: "replace" | "insert-at-caret";
158
+ /**
159
+ * When true, a `command:"prompt"` submits the composer immediately after
160
+ * inserting its text (a one-shot macro). Ignored for other kinds.
161
+ */
162
+ submitOnSelect?: boolean;
163
+ /**
164
+ * COMMANDS only. Ghost hint for a free-text argument (e.g. "order id" →
165
+ * `/lookup ‹order id›` in the menu). Its presence switches the command to
166
+ * INLINE COMPLETION (Slack-style): selecting it fills `/name ` into the
167
+ * composer for inline arg entry instead of dispatching on select, and the
168
+ * command is executed at SUBMIT with the typed args. `command:"server"` items
169
+ * always use inline completion (their chip had no way to add an argument);
170
+ * for `"prompt"`/`"action"` items this is opt-in. Ignored for non-commands.
171
+ */
172
+ commandArgsPlaceholder?: string;
173
+ };
174
+
175
+ /**
176
+ * Capability object handed to a `command:"action"` handler. The widget exposes
177
+ * only composer-local operations; broader actions (clear the transcript, change
178
+ * theme) are wired by the host via closures over its own controller.
179
+ */
180
+ export type AgentWidgetContextMentionComposerCapability = {
181
+ /** Current composer text. */
182
+ getValue: () => string;
183
+ /** Replace composer text (fires `input`, moves caret to end, refocuses). */
184
+ setValue: (value: string) => void;
185
+ /** Submit the composer (same path as pressing send). */
186
+ submit: () => void;
187
+ };
188
+
189
+ /** Context passed to a `command:"action"` handler. */
190
+ export type AgentWidgetContextMentionCommandContext = {
191
+ /** Text typed after the command name (e.g. `staging` for `/deploy staging`). */
192
+ args: string;
193
+ config: AgentWidgetConfig;
194
+ messages: AgentWidgetMessage[];
195
+ composer: AgentWidgetContextMentionComposerCapability;
196
+ };
197
+
198
+ /** Reference stored on a sent message for transcript fidelity + chip rendering. */
199
+ export type AgentWidgetContextMentionRef = {
200
+ sourceId: string;
201
+ itemId: string;
202
+ label: string;
203
+ iconName?: string;
204
+ /** Accent color for the inline token (see `AgentWidgetContextMentionItem.color`). */
205
+ color?: string;
206
+ };
207
+
208
+ /**
209
+ * One ordered segment of a sent message's prose, for inline mention rendering
210
+ * (`contextMentions.display: "inline"`). Text runs and atomic mention tokens
211
+ * interleave in document order so the sent bubble re-renders `@tokens` in place
212
+ * and composer history can round-trip them. Display/transcript concern only —
213
+ * the model still sees resolved bodies via `llmContent`/`contentParts`.
214
+ */
215
+ export type AgentWidgetContentSegment =
216
+ | { kind: "text"; text: string }
217
+ | { kind: "mention"; ref: AgentWidgetContextMentionRef };
218
+
219
+ /**
220
+ * The payload a source returns from `resolve()` for one selected item.
221
+ * Lead with `llmAppend` — it is the only channel guaranteed to reach the model
222
+ * with no backend changes.
223
+ */
224
+ export type AgentWidgetContextMentionPayload = {
225
+ /**
226
+ * PRIMARY, model-visible channel. Appended to this user message's LLM content
227
+ * so the model actually sees the context with no backend changes.
228
+ */
229
+ llmAppend?: string;
230
+ /**
231
+ * `command:"prompt"` macros only. Text written into the COMPOSER on select
232
+ * (see `AgentWidgetContextMentionItem.insertMode`) — distinct from the hidden
233
+ * `llmAppend` context channel. Falls back to `llmAppend` when omitted.
234
+ */
235
+ insertText?: string;
236
+ /** Extra content parts (e.g. file text / image) via the multi-modal path. */
237
+ contentParts?: ContentPart[];
238
+ /**
239
+ * OPT-IN structured channel. Merged into request `context` under
240
+ * `mentions.<sourceId>.<itemId>` for flows/agents that read it. NOTE: ambient
241
+ * `context` is NOT guaranteed to reach the model — use `llmAppend` if you need
242
+ * the model to see it.
243
+ */
244
+ context?: Record<string, unknown>;
245
+ };
246
+
247
+ /**
248
+ * One resolved mention, handed to `contextMentions.llmFormat` to build the
249
+ * model-visible block. `text` is the source's `llmAppend`; `ref`/`item` carry
250
+ * the selection identity/metadata for a custom formatter (e.g. a source-specific
251
+ * wrapper or a path from `item`).
252
+ */
253
+ export type AgentWidgetMentionLlmEntry = {
254
+ /** The mention's display label (from `ref.label`). */
255
+ label: string;
256
+ /** The resolved model-visible body (the source's `llmAppend`). */
257
+ text: string;
258
+ ref: AgentWidgetContextMentionRef;
259
+ item: AgentWidgetContextMentionItem;
260
+ };
261
+
262
+ export type AgentWidgetContextMentionSearchContext = {
263
+ messages: AgentWidgetMessage[];
264
+ config: AgentWidgetConfig;
265
+ /** Aborts when the next keystroke supersedes this in-flight search. */
266
+ signal: AbortSignal;
267
+ };
268
+
269
+ export type AgentWidgetContextMentionResolveContext =
270
+ AgentWidgetContextMentionSearchContext & {
271
+ /** Plain-text composer value at resolve time. */
272
+ composerText: string;
273
+ /**
274
+ * For `command` items: the text typed after the command name (e.g.
275
+ * `"123"` for `/lookup 123`). Empty string for ordinary mentions.
276
+ */
277
+ args: string;
278
+ };
279
+
280
+ export type AgentWidgetContextMentionSource = {
281
+ id: string;
282
+ /** Group header shown in the menu. */
283
+ label: string;
284
+ /**
285
+ * Filter items for the current `@query` (empty query → recent/popular).
286
+ * Implement server-side search for large/remote sets, or return all items and
287
+ * let the widget rank them with `defaultMentionFilter`
288
+ * (see `createStaticMentionSource` for the common case).
289
+ */
290
+ search: (
291
+ query: string,
292
+ ctx: AgentWidgetContextMentionSearchContext
293
+ ) =>
294
+ | AgentWidgetContextMentionItem[]
295
+ | Promise<AgentWidgetContextMentionItem[]>;
296
+ /**
297
+ * Fetch the payload for dispatch. Called once when the user SELECTS the item
298
+ * (eagerly, cached on the chip, abortable on removal) unless `resolveOn` is
299
+ * `"submit"`. A throw/abort drops the mention via `onMentionResolveError`.
300
+ */
301
+ resolve: (
302
+ item: AgentWidgetContextMentionItem,
303
+ ctx: AgentWidgetContextMentionResolveContext
304
+ ) =>
305
+ | AgentWidgetContextMentionPayload
306
+ | Promise<AgentWidgetContextMentionPayload>;
307
+ /**
308
+ * When to call `resolve()`:
309
+ * - `"select"` (default): eager on pick; parallelizes the fetch with the user
310
+ * finishing their sentence so submit stays instant.
311
+ * - `"submit"`: defer to send time, for time-sensitive sources (e.g. current
312
+ * page state that may change between select and send).
313
+ * @default "select"
314
+ */
315
+ resolveOn?: "select" | "submit";
316
+ /**
317
+ * COMMAND sources only. Resolve a `/name` (the first token of a command line)
318
+ * to its menu item, for submit-time inline-command dispatch and the menu's
319
+ * "typing args now" suppression. `createSlashCommandsSource` sets this; plain
320
+ * `@`-mention sources leave it undefined.
321
+ */
322
+ matchCommand?: (name: string) => AgentWidgetContextMentionItem | undefined;
323
+ };
324
+
325
+ /** Context passed to the optional `renderMentionMenu` override. */
326
+ export type AgentWidgetContextMentionMenuRenderContext = {
327
+ /** Current trigger query ("" when freshly opened). */
328
+ query: string;
329
+ /** Grouped, ranked, capped results ready to paint. */
330
+ groups: {
331
+ source: AgentWidgetContextMentionSource;
332
+ items: AgentWidgetContextMentionItem[];
333
+ }[];
334
+ /** Per-source async state for loading/empty/error rendering, keyed by source id. */
335
+ status: Record<string, "loading" | "ready" | "empty" | "error">;
336
+ /** Index into the flat keyboard-traversal order. */
337
+ activeIndex: number;
338
+ /** Commit a selection (strips `@query`, adds chip, resolves on select). */
339
+ select: (item: AgentWidgetContextMentionItem) => void;
340
+ /** Close the menu, keeping a literal `@`. */
341
+ close: () => void;
342
+ };
343
+
344
+ /** Context passed to the optional `renderMentionItem` per-row override. */
345
+ export type AgentWidgetContextMentionItemRenderContext = {
346
+ /** The item to render. */
347
+ item: AgentWidgetContextMentionItem;
348
+ /** The source this item came from (its group). */
349
+ source: AgentWidgetContextMentionSource;
350
+ /** Current trigger query, for highlighting matches. */
351
+ query: string;
352
+ /** True when this row is the keyboard-highlighted one at render time. */
353
+ active: boolean;
354
+ /** Position in the flat keyboard-traversal order. */
355
+ index: number;
356
+ };
357
+
358
+ /** Context passed to the optional `renderMentionChip` override. */
359
+ export type AgentWidgetContextMentionChipRenderContext = {
360
+ ref: AgentWidgetContextMentionRef;
361
+ /** Resolve-on-select lifecycle for spinner→ready→error UI. */
362
+ status: "resolving" | "ready" | "error";
363
+ /**
364
+ * The resolved payload, once available (select-resolved sources only). Use it
365
+ * to preview already-fetched content on hover. Undefined while `status` is
366
+ * `"resolving"`, on `"error"`, and for `resolveOn:"submit"` sources (which
367
+ * resolve at send time, not while the chip sits in the composer — for those,
368
+ * `ref.itemId` carries the source key you can re-read on demand, e.g. a CSS
369
+ * selector for the smart-dom source).
370
+ */
371
+ payload?: AgentWidgetContextMentionPayload;
372
+ /** Remove the chip; aborts any in-flight resolve. */
373
+ remove: () => void;
374
+ };
375
+
376
+ /**
377
+ * Where a trigger char is allowed to open the menu:
378
+ * - `"anywhere"` (default): after any whitespace or start (the `@` rule).
379
+ * - `"line-start"`: only when the char before the trigger is a newline or the
380
+ * start of input — the natural rule for `/` slash-commands.
381
+ * - `"input-start"`: only at index 0 of the composer.
382
+ */
383
+ export type AgentWidgetMentionTriggerPosition =
384
+ | "anywhere"
385
+ | "line-start"
386
+ | "input-start";
387
+
388
+ /**
389
+ * A SECOND (or Nth) trigger channel sharing the same menu engine — e.g. `/` for
390
+ * slash-commands alongside `@` for context. The legacy `trigger`/`sources`
391
+ * fields form the implicit first channel; `triggers[]` adds more. At any caret
392
+ * at most one channel is active, so all channels drive one menu/controller.
393
+ */
394
+ export type AgentWidgetMentionTriggerChannel = {
395
+ /** Trigger character (e.g. "/"). */
396
+ trigger: string;
397
+ /** Sources for this channel (searched only when this trigger is active). */
398
+ sources: AgentWidgetContextMentionSource[];
399
+ /** Where the trigger may open. @default "anywhere" */
400
+ triggerPosition?: AgentWidgetMentionTriggerPosition;
401
+ /**
402
+ * Let the query span spaces so a command can take ARGS (`/deploy staging`).
403
+ * A newline still ends it. Pair with `triggerPosition: "line-start"` for
404
+ * slash-commands. @default false
405
+ */
406
+ allowSpaces?: boolean;
407
+ /** Show a composer affordance button for this channel. @default false */
408
+ showButton?: boolean;
409
+ /** Icon for this channel's affordance button. */
410
+ buttonIconName?: string;
411
+ /** Tooltip / aria-label for this channel's affordance button. */
412
+ buttonTooltipText?: string;
413
+ /** Placeholder for this channel's picker search field. */
414
+ searchPlaceholder?: string;
415
+ };
416
+
417
+ export type AgentWidgetContextMentionConfig = {
418
+ /** @default false */
419
+ enabled?: boolean;
420
+ /**
421
+ * Show the visible composer affordance button (the discoverable entry point).
422
+ * Strongly recommended; the bare `@` trigger alone is hard to discover for
423
+ * non-technical users. @default true
424
+ */
425
+ showButton?: boolean;
426
+ /**
427
+ * Icon for the affordance button (any registered Lucide name). Defaults to a
428
+ * "+" signifier — the consumer-recognized "add context" affordance — rather
429
+ * than an "@" glyph, which reads as power-user. Use `"at-sign"` on a
430
+ * developer/power-user surface. @default "plus"
431
+ */
432
+ buttonIconName?: string;
433
+ /** Tooltip / aria-label for the affordance button. @default "Add context" */
434
+ buttonTooltipText?: string;
435
+ /**
436
+ * Placeholder for the picker's search field, shown when the menu is opened
437
+ * from the affordance button (a Cursor/Copilot-style picker that inserts no
438
+ * trigger character). The typed-trigger path keeps its query in the textarea
439
+ * and never shows this field. @default "Search context…"
440
+ */
441
+ searchPlaceholder?: string;
442
+ /** Trigger character. @default "@" */
443
+ trigger?: string;
444
+ /**
445
+ * Where the primary `trigger` may open the menu. @default "anywhere"
446
+ */
447
+ triggerPosition?: AgentWidgetMentionTriggerPosition;
448
+ /**
449
+ * ADDITIONAL trigger channels sharing this engine — e.g. a `/` slash-command
450
+ * channel next to `@` context. Each channel has its own trigger char,
451
+ * sources, and position rule. See `AgentWidgetMentionTriggerChannel`.
452
+ */
453
+ triggers?: AgentWidgetMentionTriggerChannel[];
454
+ /** Max mentions per message. @default 8 */
455
+ maxMentions?: number;
456
+ /** Max items shown per source group before "keep typing to narrow". @default 6 */
457
+ maxItemsPerGroup?: number;
458
+ /**
459
+ * Debounce for ASYNC source search only (ms). Synchronous sources and the
460
+ * menu's first paint are never debounced. @default 150
461
+ */
462
+ searchDebounceMs?: number;
463
+ /** Registered mention sources (search + resolve). */
464
+ sources: AgentWidgetContextMentionSource[];
465
+ /** Chip icon fallback when a source/item omits one. @default "at-sign" */
466
+ chipIconName?: string;
467
+ /**
468
+ * How each resolved mention's `llmAppend` text is wrapped into a delimited
469
+ * block before it is prepended to the model-visible message content. Blocks
470
+ * are joined with a blank line and the typed prose follows last.
471
+ *
472
+ * - `"fenced"` (default): a fenced code block with the label in the info
473
+ * string, e.g. selecting `App.tsx` emits:
474
+ * ```text
475
+ * ```App.tsx
476
+ * <file body>
477
+ * ```
478
+ * ```
479
+ * The fence auto-escalates (four+ backticks) when the body itself contains a
480
+ * fence, so a body can never terminate its own wrapper.
481
+ * - `"document"`: Anthropic's long-context document shape, e.g.:
482
+ * ```text
483
+ * <document index="1">
484
+ * <source>App.tsx</source>
485
+ * <document_content>
486
+ * <file body>
487
+ * </document_content>
488
+ * </document>
489
+ * ```
490
+ * Prefer this for prose/document-shaped sources per Anthropic's long-context
491
+ * guidance (indexed `<document>` blocks improve grounding and citation). A
492
+ * body containing the literal `</document_content>` closing tag falls back to
493
+ * the fenced block for that entry to avoid a broken XML boundary.
494
+ * - function form: fully owns the block for one mention — its return string is
495
+ * used verbatim (no extra wrapping), joined with the other blocks. `index` is
496
+ * the 0-based position among the message's mention blocks.
497
+ *
498
+ * @default "fenced"
499
+ */
500
+ llmFormat?:
501
+ | "fenced"
502
+ | "document"
503
+ | ((entry: AgentWidgetMentionLlmEntry, index: number) => string);
504
+ /**
505
+ * How `@` selections appear in the composer and sent user bubble.
506
+ *
507
+ * - `"chip"` (default): strip `@query` on select; show a compact pill in the
508
+ * composer context row; `content` is prose-only.
509
+ * - `"inline"`: insert an atomic styled token in the sentence (Slack/Linear/
510
+ * Cursor style), backed by a contenteditable composer. Slash-command channels
511
+ * (`triggers[]` with `/`) stay inline-text regardless — they already use the
512
+ * `/name args` model, not chips. `renderMentionChip` is ignored for `@`
513
+ * mentions in this mode.
514
+ *
515
+ * The resolved-context channel (`llmContent`/`contentParts`) is unchanged
516
+ * across both modes — inline tokens are a display concern.
517
+ *
518
+ * @default "chip"
519
+ */
520
+ display?: "chip" | "inline";
521
+ /**
522
+ * Fired when a pick is rejected: it duplicates an existing chip in chip display
523
+ * (`"duplicate"` — inline display allows the same item as multiple tokens, so it
524
+ * never rejects for this reason), hits `maxMentions` (`"limit"`), or (inline
525
+ * display) the composer text changed between parse and commit so the token could
526
+ * not be inserted (`"stale"`).
527
+ */
528
+ onMentionRejected?: (
529
+ item: AgentWidgetContextMentionItem,
530
+ reason: "duplicate" | "limit" | "stale"
531
+ ) => void;
532
+ /**
533
+ * A `resolve()` that throws/aborts: the mention is dropped and the message
534
+ * still sends. Surface a non-blocking notice here if desired.
535
+ */
536
+ onMentionResolveError?: (
537
+ item: AgentWidgetContextMentionItem,
538
+ error: unknown
539
+ ) => void;
540
+ /**
541
+ * MID-LEVEL render override for the autocomplete menu. The widget still owns
542
+ * trigger detection, search, debounce/abort, keyboard nav, and positioning;
543
+ * you only supply the menu markup and drive selection through `ctx`. Omit to
544
+ * use the built-in grouped menu. @default built-in
545
+ */
546
+ renderMentionMenu?: (
547
+ ctx: AgentWidgetContextMentionMenuRenderContext
548
+ ) => HTMLElement;
549
+ /**
550
+ * NARROW render override for a single result row's INNER content, keeping the
551
+ * built-in menu chrome (group headers, loading/empty/error, keyboard nav, the
552
+ * "keep typing" hint). The widget keeps the `role="option"` wrapper and its
553
+ * click/hover wiring; you return only the row's contents. For full menu
554
+ * control use `renderMentionMenu` instead. Highlight is CSS-driven via the
555
+ * wrapper's `[data-active]` attribute, so this is called once per render, not
556
+ * on every arrow key. Ignored when `renderMentionMenu` is set. @default built-in
557
+ */
558
+ renderMentionItem?: (
559
+ ctx: AgentWidgetContextMentionItemRenderContext
560
+ ) => HTMLElement;
561
+ /**
562
+ * MID-LEVEL render override for a single context chip. Return your own pill
563
+ * (must include an accessible remove control wired to `ctx.remove`); reflect
564
+ * `ctx.status` for resolve-on-select. Omit to use the built-in compact pill.
565
+ * @default built-in
566
+ */
567
+ renderMentionChip?: (
568
+ ctx: AgentWidgetContextMentionChipRenderContext
569
+ ) => HTMLElement;
570
+ /**
571
+ * INLINE display only (`display: "inline"`). Render override for a single inline
572
+ * mention TOKEN — the atomic styled node that sits in the sentence, in both the
573
+ * contenteditable composer and the read-only sent bubble. Return your own
574
+ * element (e.g. a fully custom colored pill); the composer marks it
575
+ * `contenteditable="false"` and stamps `data-mention-id` so it stays atomic.
576
+ * Omit to use the built-in pill (icon + `@label`, tinted by
577
+ * `--persona-mention-token-accent` / `ref.color`, themeable per source via the
578
+ * `[data-mention-source]` attribute). Ignored in chip mode.
579
+ * @default built-in
580
+ */
581
+ renderMentionToken?: (
582
+ ctx: AgentWidgetContextMentionTokenRenderContext
583
+ ) => HTMLElement;
584
+ };
585
+
586
+ /** Render context for `renderMentionToken`. `readonly` is true in the sent bubble. */
587
+ export type AgentWidgetContextMentionTokenRenderContext = {
588
+ ref: AgentWidgetContextMentionRef;
589
+ readonly: boolean;
590
+ };
591
+
97
592
  export type AgentWidgetRequestPayloadMessage = {
98
593
  role: AgentWidgetMessageRole;
99
594
  content: MessageContent;
@@ -727,7 +1222,13 @@ export type AgentWidgetControllerEventMap = {
727
1222
  * new artifact content arrives or the host calls `showArtifacts()` on the widget handle.
728
1223
  */
729
1224
  export type AgentWidgetArtifactsLayoutConfig = {
730
- /** Flex gap between chat column and artifact pane. @default 0.5rem */
1225
+ /**
1226
+ * Flex gap between chat column and artifact pane.
1227
+ * @default 0 (welded); detached uses the panel inset
1228
+ * @remarks In welded appearances (`panel` / `seamless`) a nonzero gap opens a
1229
+ * visible seam in the single card. It is honored geometrically (resize clamps
1230
+ * and the seam handle account for it) but is usually undesired.
1231
+ */
731
1232
  splitGap?: string;
732
1233
  /** Artifact column width in split mode. @default 40% */
733
1234
  paneWidth?: string;
@@ -758,16 +1259,39 @@ export type AgentWidgetArtifactsLayoutConfig = {
758
1259
  /** Optional max artifact width cap while resizing (`px` only). Layout still bounds by chat min width. */
759
1260
  resizableMaxWidth?: string;
760
1261
  /**
761
- * Visual treatment for the artifact column in split mode.
762
- * - `'panel'`: bordered sidebar with left border, gap, and shadow (default).
763
- * - `'seamless'`: flush with chat: no border or shadow, container background, zero gap.
1262
+ * Visual treatment for the artifact column in a desktop split.
1263
+ * - `'panel'`: one welded card (border + radius wrap chat + pane) with a hairline
1264
+ * divider on the pane's chat-facing edge, zero gap (default).
1265
+ * - `'seamless'`: the same welded card with no internal divider or chrome, zero gap.
1266
+ * - `'detached'`: two separate elevated cards: gap from `--persona-panel-inset`,
1267
+ * all-side border and radius on each, and canvas background behind the split.
1268
+ * In an inline embed this also insets the split from the container edges on all
1269
+ * sides (the page canvas shows through), so the all-side margin no longer requires
1270
+ * `launcher.detachedPanel`.
1271
+ *
1272
+ * Default is `'panel'` unless `launcher.detachedPanel` is true, in which case an unset
1273
+ * `paneAppearance` resolves to `'detached'`. An explicit value always wins.
764
1274
  * @default 'panel'
765
1275
  */
766
- paneAppearance?: "panel" | "seamless";
1276
+ paneAppearance?: "panel" | "seamless" | "detached";
767
1277
  /** Border radius on the artifact pane (CSS length). Works with any `paneAppearance`. */
768
1278
  paneBorderRadius?: string;
769
1279
  /** CSS `box-shadow` on the artifact pane. Set `"none"` to suppress the default shadow. */
770
1280
  paneShadow?: string;
1281
+ /**
1282
+ * CSS `box-shadow` for the chat column card in a detached split. Defaults to the same
1283
+ * elevation as the artifact pane; set to `"none"` to keep the chat flat while the pane stays raised.
1284
+ */
1285
+ chatShadow?: string;
1286
+ /**
1287
+ * Chat column surface in a detached split. `"card"` (default) renders the chat as an inset card
1288
+ * matching the artifact pane; `"flush"` renders the chat flush with the container (no border, radius,
1289
+ * or shadow) and insets only the artifact pane, so the chat is flat background and the pane floats.
1290
+ * Only affects an inline-embedded detached split (the container-filling case); in floating, docked,
1291
+ * or sidebar modes it falls back to the card look.
1292
+ * @default 'card'
1293
+ */
1294
+ chatSurface?: "card" | "flush";
771
1295
  /**
772
1296
  * Full `border` shorthand for the artifact `<aside>` (all sides). Overrides default pane borders.
773
1297
  * Example: `"1px solid #cccccc"`.
@@ -779,14 +1303,12 @@ export type AgentWidgetArtifactsLayoutConfig = {
779
1303
  */
780
1304
  paneBorderLeft?: string;
781
1305
  /**
782
- * Desktop split only (not narrow-host drawer / not ≤640px): square the **main chat card’s**
783
- * top-right and bottom-right radii, and round the **artifact pane’s** top-right and bottom-right
784
- * to match `persona-rounded-2xl` (`--persona-radius-lg`) so the two columns read as one shell.
1306
+ * @deprecated No-op. Panel and seamless splits now weld into one card by default.
785
1307
  */
786
1308
  unifiedSplitChrome?: boolean;
787
1309
  /**
788
- * When `unifiedSplitChrome` is true, outer-right corner radius on the artifact column (CSS length).
789
- * @default matches theme large radius (`--persona-radius-lg`)
1310
+ * Desktop welded split only: override the artifact pane's outer-right corner radius (CSS length).
1311
+ * @default matches the panel radius (`--persona-panel-radius`)
790
1312
  */
791
1313
  unifiedSplitOuterRadius?: string;
792
1314
  /**
@@ -806,6 +1328,10 @@ export type AgentWidgetArtifactsLayoutConfig = {
806
1328
  * @default 'default'
807
1329
  */
808
1330
  toolbarPreset?: "default" | "document";
1331
+ /** Visible title in the artifact toolbar. @default "Artifacts" */
1332
+ toolbarTitle?: string;
1333
+ /** Label for the artifact dismiss control. @default "Close" */
1334
+ closeButtonLabel?: string;
809
1335
  /**
810
1336
  * When `toolbarPreset` is `document`, show a visible "Copy" label next to the copy icon.
811
1337
  */
@@ -820,6 +1346,14 @@ export type AgentWidgetArtifactsLayoutConfig = {
820
1346
  documentToolbarToggleActiveBackground?: string;
821
1347
  /** Active view/source toggle border color. Sets `--persona-artifact-doc-toggle-active-border`. */
822
1348
  documentToolbarToggleActiveBorderColor?: string;
1349
+ /** Show an expand/collapse toggle in the artifact toolbar (both presets). Expanded fills the widget with the pane and hides the chat column. Default: false. */
1350
+ showExpandToggle?: boolean;
1351
+ /** Show the copy control in the `default` toolbar preset (the `document` preset always shows it). Default: false. */
1352
+ showCopyButton?: boolean;
1353
+ /** Edge fade on the scrollable tab strip. true = both ends (dynamic). @default true */
1354
+ tabFade?: boolean | { start?: boolean; end?: boolean };
1355
+ /** Tab fade width (CSS length). @default 24px */
1356
+ tabFadeSize?: string;
823
1357
  /**
824
1358
  * Invoked when the document toolbar Refresh control is used (before the pane re-renders).
825
1359
  * Use to replay `connectStream`, refetch, etc.
@@ -841,21 +1375,357 @@ export type AgentWidgetArtifactsLayoutConfig = {
841
1375
  }) => void | Promise<void>;
842
1376
  };
843
1377
 
1378
+ export type PersonaArtifactDisplayMode =
1379
+ | "card" // reference card in transcript; pane opens on click
1380
+ | "panel" // reference card in transcript; pane also auto-opens on artifact_start (current default behavior)
1381
+ | "inline"; // artifact preview renders directly in the transcript; no pane involvement
1382
+
1383
+ /** Context handed to custom artifact actions. Content fields are best-effort: resolved from live session state or, after a refresh, from the persisted reference-card payload. */
1384
+ export type PersonaArtifactActionContext = {
1385
+ artifactId: string | null;
1386
+ title: string;
1387
+ artifactType: string;
1388
+ markdown?: string;
1389
+ file?: PersonaArtifactFileMeta;
1390
+ /** Component artifacts: pretty-printed { component, props } JSON. */
1391
+ jsonPayload?: string;
1392
+ };
1393
+
1394
+ /**
1395
+ * Context passed to a `features.artifacts.statusLabel` function on every
1396
+ * artifact streaming update, once per visible surface. Treat it as read-only:
1397
+ * the function should be pure and fast (it runs per streaming delta, per
1398
+ * surface).
1399
+ */
1400
+ export type PersonaArtifactStatusLabelContext = {
1401
+ artifactId: string;
1402
+ artifactType: "markdown" | "component";
1403
+ title?: string;
1404
+ /** The default subject, e.g. "document", "component", or a file type label like "HTML file". */
1405
+ typeLabel: string;
1406
+ file?: PersonaArtifactFileMeta;
1407
+ /** Accumulated streamed content length so far (0 for component artifacts). */
1408
+ chars: number;
1409
+ /** Accumulated line count so far (0 for component artifacts). */
1410
+ lines: number;
1411
+ /** Milliseconds since this artifact's first streaming update was seen. */
1412
+ elapsedMs: number;
1413
+ /** Lazy accessor for the accumulated markdown source (empty string for component artifacts). Call only if needed; parsing cost is opt-in. */
1414
+ content: () => string;
1415
+ /** Which surface is asking, so hosts can shorten for cramped spots. */
1416
+ surface: "card" | "inline-chrome" | "status-body";
1417
+ };
1418
+
1419
+ /** A custom action button rendered in an artifact toolbar/card slot. */
1420
+ export type PersonaArtifactCustomAction = {
1421
+ /** Stable id, used for DOM wiring and keyed re-renders. */
1422
+ id: string;
1423
+ /** Accessible label; also the visible text when showLabel is true. */
1424
+ label: string;
1425
+ /** Registry icon name, or a factory returning your own element (e.g. a brand SVG). The factory result is author-owned code, not sanitized wire data. */
1426
+ icon?: IconName | (() => HTMLElement | SVGElement);
1427
+ /** Render icon + text instead of icon-only. Default: false. */
1428
+ showLabel?: boolean;
1429
+ /** Per-artifact gate, evaluated on each render. Omitted = always visible. */
1430
+ visible?: (ctx: PersonaArtifactActionContext) => boolean;
1431
+ /** May be async; rejections are swallowed like built-in action errors. */
1432
+ onClick: (ctx: PersonaArtifactActionContext) => void | Promise<void>;
1433
+ };
1434
+
844
1435
  export type AgentWidgetArtifactsFeature = {
845
1436
  /** When true, Persona shows the artifact pane and handles artifact_* SSE events */
846
1437
  enabled?: boolean;
847
1438
  /** If set, artifact events for other types are ignored */
848
1439
  allowedTypes?: PersonaArtifactKind[];
1440
+ /**
1441
+ * Where artifact bodies render. A single mode applies to all artifacts;
1442
+ * the object form sets a default plus per-type overrides.
1443
+ * Defaults to "panel" (current behavior).
1444
+ */
1445
+ display?:
1446
+ | PersonaArtifactDisplayMode
1447
+ | {
1448
+ default?: PersonaArtifactDisplayMode;
1449
+ byType?: Partial<Record<PersonaArtifactKind, PersonaArtifactDisplayMode>>;
1450
+ };
849
1451
  /** Split / drawer dimensions and launcher widen behavior */
850
1452
  layout?: AgentWidgetArtifactsLayoutConfig;
851
1453
  /**
852
- * Called when an artifact card action is triggered (open, download).
853
- * Return `true` to prevent the default behavior.
1454
+ * Controls inline preview of previewable file artifacts (HTML/SVG written by
1455
+ * a Claude Managed agent). When a markdown artifact carries `file` metadata,
1456
+ * the pane can render an isolated `<iframe srcdoc>` sandbox instead of a code
1457
+ * block.
1458
+ */
1459
+ filePreview?: {
1460
+ /** Enable rendered preview for html/svg files. Defaults to enabled. Set false to force source view. */
1461
+ enabled?: boolean;
1462
+ /**
1463
+ * `sandbox` attribute for the preview iframe. Defaults to `"allow-scripts"`.
1464
+ * `allow-same-origin` is stripped (with a console warning) unless
1465
+ * `dangerouslyAllowSameOrigin` is set: the opaque origin is what isolates
1466
+ * the preview from the host page. Embedder-trusted config.
1467
+ */
1468
+ iframeSandbox?: string;
1469
+ /**
1470
+ * Allows `allow-same-origin` to pass through `iframeSandbox`. Off by
1471
+ * default because it lets agent-written file content run with the host
1472
+ * page's origin (DOM, storage, credentialed requests). Only set this when
1473
+ * every previewed file source is fully trusted.
1474
+ */
1475
+ dangerouslyAllowSameOrigin?: boolean;
1476
+ /**
1477
+ * Loading treatment for the preview iframe. Default on.
1478
+ *
1479
+ * A sandboxed `iframe srcdoc` (opaque origin, `allow-scripts` only) paints
1480
+ * blank until its content renders — seconds for artifacts that pull CDN
1481
+ * scripts (React/Babel). The default shows a themed "Loading preview…"
1482
+ * overlay over the iframe, dismissed the instant the content signals it has
1483
+ * painted (an injected `postMessage` reporter) or a hard timeout elapses.
1484
+ *
1485
+ * `false` disables the overlay and the injected ready signal entirely (the
1486
+ * srcdoc becomes the raw file source, the prior behavior). An object tunes
1487
+ * the timing; omitted / `true` uses all defaults.
1488
+ */
1489
+ loading?:
1490
+ | boolean
1491
+ | {
1492
+ /** Show the overlay only if the preview is not ready after this many ms. Default 200. */
1493
+ delayMs?: number;
1494
+ /** Once shown, keep the overlay visible at least this long (anti-flicker). Default 300. */
1495
+ minVisibleMs?: number;
1496
+ /** Give up waiting and reveal the iframe regardless after this many ms. Default 8000. */
1497
+ timeoutMs?: number;
1498
+ /**
1499
+ * Append the `postMessage` ready reporter to the srcdoc so the overlay
1500
+ * dismisses on first paint rather than the iframe `load` event (which
1501
+ * fires before post-DOMContentLoaded rendering like Babel compilation).
1502
+ * Default true. When false, the overlay dismisses on `load` + a double
1503
+ * `requestAnimationFrame`, or the timeout.
1504
+ */
1505
+ injectReadySignal?: boolean;
1506
+ /**
1507
+ * Escalation text shown beside the spinner after `labelDelayMs`.
1508
+ * Default `"Starting preview..."`. `false` renders an icon-only
1509
+ * indicator forever (no text ever). A short work-naming label is
1510
+ * added only after a delay by design — icon-first, with text reserved
1511
+ * for genuinely slow loads (per Geist/Sandpack guidance; HIG warns
1512
+ * against the vague word "loading").
1513
+ */
1514
+ label?: string | false;
1515
+ /**
1516
+ * Delay before the escalation label fades in, measured from when the
1517
+ * overlay actually becomes visible (i.e. after `delayMs`), not from
1518
+ * iframe creation. Default 2000.
1519
+ */
1520
+ labelDelayMs?: number;
1521
+ /**
1522
+ * Full replacement for the default indicator (spinner + escalation
1523
+ * label). Called once when the overlay is built; return any element
1524
+ * (a brand mark, skeleton, custom animation) to own the indicator
1525
+ * content entirely (the escalation-label logic is then skipped), or
1526
+ * return `null` to fall back to the default. A thrown error also falls
1527
+ * back. Mirrors `renderInline` / `renderCard`'s null-falls-back
1528
+ * contract. The overlay backdrop, timing, and dismissal stay
1529
+ * widget-owned either way.
1530
+ */
1531
+ renderIndicator?: (ctx: {
1532
+ artifactId: string;
1533
+ config: AgentWidgetConfig;
1534
+ }) => HTMLElement | null;
1535
+ };
1536
+ };
1537
+ /**
1538
+ * Custom action buttons for the artifact pane toolbar, rendered between the
1539
+ * refresh and expand/close controls in both toolbar presets. Re-read on live
1540
+ * config updates.
1541
+ */
1542
+ toolbarActions?: PersonaArtifactCustomAction[];
1543
+ /**
1544
+ * Custom action buttons for the artifact reference card, rendered before the
1545
+ * Download button on complete artifacts. Clicks are delegated, so they
1546
+ * survive re-renders and page refresh.
854
1547
  */
855
- onArtifactAction?: (action: {
856
- type: 'open' | 'download';
857
- artifactId: string;
858
- }) => boolean | void;
1548
+ cardActions?: PersonaArtifactCustomAction[];
1549
+ /**
1550
+ * File chrome (title bar + toolbar) around inline preview blocks
1551
+ * (`display: "inline"`). Default when undefined: chrome on, with the built-in
1552
+ * Copy and Expand controls. Pass `false` to render the bare preview body
1553
+ * (frame → padded body, no chrome bar), or an object to toggle the built-in
1554
+ * controls individually.
1555
+ *
1556
+ * Note: default-on chrome adds a title bar and buttons to every existing
1557
+ * inline host on upgrade; set `inlineChrome: false` to opt out.
1558
+ */
1559
+ inlineChrome?:
1560
+ | boolean
1561
+ | {
1562
+ /** Show the built-in Copy button (complete artifacts only). Default true. */
1563
+ showCopy?: boolean;
1564
+ /** Show the built-in Expand button (opens the pane for this artifact). Default true. */
1565
+ showExpand?: boolean;
1566
+ /**
1567
+ * Show the built-in rendered/source view toggle (complete artifacts
1568
+ * only). Default true. The toggle is availability-gated: it appears only
1569
+ * for file-backed markdown artifacts that have a rendered alternative to
1570
+ * their source (previewable HTML/SVG, or markdown-kind files), and never
1571
+ * when `inlineBody.viewMode: "source"` forces a source-only body. Plain
1572
+ * markdown and component artifacts don't get it (the pane toggle covers
1573
+ * those).
1574
+ */
1575
+ showViewToggle?: boolean;
1576
+ };
1577
+ /**
1578
+ * Custom action buttons for the inline chrome toolbar, rendered before the
1579
+ * built-in Copy/Expand controls on complete artifacts. Same shape as
1580
+ * `cardActions`; each action's `visible` gate is respected and clicks are
1581
+ * delegated, so they survive re-renders and page refresh.
1582
+ */
1583
+ inlineActions?: PersonaArtifactCustomAction[];
1584
+ /**
1585
+ * Replace the artifact tab strip entirely (Seam A). Return an element mounted in
1586
+ * place of the built-in `.persona-artifact-list`; the pane still owns the toolbar,
1587
+ * preview body, iframe reuse, and lazy-render gate. Re-invoked when the records or
1588
+ * selection change. Custom bars own their own accessibility; use `createRovingTablist`
1589
+ * to keep keyboard/tablist behavior. When set, the built-in strip (scroll, fade,
1590
+ * tablist) is not rendered.
1591
+ *
1592
+ * Return either a fresh element per call, or the SAME element updated in place.
1593
+ * Returning a stable element preserves internal state across selection changes:
1594
+ * the pane skips remounting an unchanged node, so a roving tablist keeps keyboard
1595
+ * focus on the selected tab (a fresh element per call drops focus and breaks
1596
+ * Arrow-key nav). See the reference bars in `apps/web/src/artifact-demo.ts`.
1597
+ */
1598
+ renderTabBar?: (ctx: {
1599
+ records: PersonaArtifactRecord[];
1600
+ selectedId: string | null;
1601
+ onSelect: (id: string) => void;
1602
+ }) => HTMLElement;
1603
+ /**
1604
+ * Body layout for inline artifact blocks (`display: "inline"`). Controls how
1605
+ * the block reserves height and behaves while a file/document artifact
1606
+ * streams, so the streaming source no longer grows an unbounded `<pre>` and
1607
+ * then snaps to the completed preview. Affects only the inline block; the
1608
+ * artifact pane is unchanged.
1609
+ *
1610
+ * Defaults (a behavior change from prior versions, which grew with content
1611
+ * during streaming): a fixed 320px streaming source window with tail-follow,
1612
+ * a top edge fade, and an animated streaming→complete swap. Pass
1613
+ * `height: "auto"` to restore the old grow-with-content streaming behavior.
1614
+ */
1615
+ inlineBody?: {
1616
+ /** What the body shows while streaming. Default `"source"`. */
1617
+ streamingView?: "source" | "status";
1618
+ /**
1619
+ * How the completed body renders. `"rendered"` (default) keeps the current
1620
+ * behavior: file previews in a sandboxed iframe, markdown through the
1621
+ * markdown pipeline. `"source"` always shows the raw syntax-highlighted
1622
+ * source instead — for hosts where the artifact is input to the host system
1623
+ * (a code editor, a query runner) rather than something to preview. Unlike
1624
+ * `filePreview.enabled: false`, this also covers markdown-kind files and
1625
+ * plain markdown artifacts. Component artifacts always render through the
1626
+ * registry.
1627
+ */
1628
+ viewMode?: "rendered" | "source";
1629
+ /**
1630
+ * Body height per state. A number reserves that many px for the whole body
1631
+ * region (border-box, padding included) with internal scroll — the
1632
+ * zero-layout-shift path; `"auto"` means content-sized. A
1633
+ * scalar applies to both the streaming and complete states; the object form
1634
+ * sets them independently. Default `320` for both states.
1635
+ *
1636
+ * Note: with `"auto"` on the complete state, non-iframe bodies (rendered
1637
+ * markdown, source view, component) are content-sized, but a file-preview
1638
+ * iframe cannot be content-sized without postMessage plumbing, so it falls
1639
+ * back to the CSS-var default (`--persona-artifact-inline-frame-height`,
1640
+ * 320px).
1641
+ *
1642
+ * Numeric heights are applied via the CSS var
1643
+ * `--persona-artifact-inline-body-height` on the block root. The older
1644
+ * `--persona-artifact-inline-frame-height` var still overrides the iframe
1645
+ * height when this one is unset, so themes pinned to it keep working.
1646
+ */
1647
+ height?:
1648
+ | number
1649
+ | "auto"
1650
+ | { streaming?: number | "auto"; complete?: number | "auto" };
1651
+ /**
1652
+ * Tail-follow the newest streamed lines in a fixed-height source window: if
1653
+ * the viewport is already at the bottom, keep it pinned as content arrives;
1654
+ * if the reader scrolled up, don't fight them. Only meaningful for a numeric
1655
+ * streaming height. Default `true`.
1656
+ *
1657
+ * Precedence: ignored when `overflow: "clip"` (a clipped window shows the
1658
+ * top of the document and never scrolls, so there is no tail to follow).
1659
+ */
1660
+ followOutput?: boolean;
1661
+ /**
1662
+ * How a fixed-height source window handles content taller than the window.
1663
+ *
1664
+ * - `"scroll"` (default) — the current behavior: an internally scrollable
1665
+ * window that tail-follows the newest streamed lines (see `followOutput`),
1666
+ * so the reader sees the growing tail of the document.
1667
+ * - `"clip"` — a fixed-height window that shows the TOP of the document,
1668
+ * `overflow: hidden` with no internal scroll and no tail-follow
1669
+ * (`followOutput` is ignored in this mode). When content overflows the
1670
+ * window a bottom edge fade signals there is more below (the top is always
1671
+ * visible, so a top fade never applies); an explicit `fadeMask` still wins
1672
+ * over this clip-mode default. When the inline chrome's Expand control is
1673
+ * enabled the whole body doubles as an expand hitbox (click / Enter / Space
1674
+ * opens the artifact in the pane), matching the industry inline-card
1675
+ * pattern; otherwise the clip is purely visual.
1676
+ *
1677
+ * Only meaningful for a numeric streaming/complete height. Default
1678
+ * `"scroll"`.
1679
+ */
1680
+ overflow?: "scroll" | "clip";
1681
+ /**
1682
+ * Edge fade masks on the fixed-height streaming window (a top fade signals
1683
+ * clipped content above, a bottom fade signals clipped content below). Each
1684
+ * fade renders only when content is actually clipped on that edge, so with
1685
+ * tail-follow pinned at the bottom the bottom fade stays inert until the
1686
+ * reader scrolls up mid-stream. `true` = `{ top: true, bottom: true }`,
1687
+ * `false` = none. Default `{ top: true, bottom: true }`.
1688
+ */
1689
+ fadeMask?: boolean | { top?: boolean; bottom?: boolean };
1690
+ /**
1691
+ * Animate the streaming→complete body swap. `"auto"` uses the View
1692
+ * Transitions API when supported and `prefers-reduced-motion` is off;
1693
+ * `"none"` swaps instantly. Default `"auto"`.
1694
+ */
1695
+ transition?: "auto" | "none";
1696
+ /**
1697
+ * What the block becomes once the artifact completes.
1698
+ *
1699
+ * - `"inline"` (default) — the streamed body stays inline forever, as
1700
+ * configured by the other `inlineBody` options.
1701
+ * - `"card"` — the block streams inline as configured, then collapses to the
1702
+ * compact artifact reference card once the artifact completes (the Copilot
1703
+ * "chiclet" / Claude side-panel handoff pattern), keeping the thread
1704
+ * scannable. The collapse plays as a short CSS height transition (skipped
1705
+ * under `prefers-reduced-motion`); it does not use the View Transitions
1706
+ * API, so `transition` does not affect it. After the collapse the card's
1707
+ * normal actions apply (click opens the artifact pane). `viewMode`, the
1708
+ * inline rendered/source view toggle, `inlineActions`, and inline copy are
1709
+ * streaming-phase-only in this mode and inert once the block is a card
1710
+ * (there is nothing to toggle or copy on a collapsed card). Blocks that are
1711
+ * already complete when they first render (page-refresh hydration) render
1712
+ * the card directly, with no flash of the inline body and no animation.
1713
+ *
1714
+ * Default `"inline"`.
1715
+ */
1716
+ completeDisplay?: "inline" | "card";
1717
+ };
1718
+ /**
1719
+ * Called when an artifact action is triggered (open, download, expand).
1720
+ * Return `true` to intercept: the widget then does not change state.
1721
+ * For `expand`, `expanded` is the state the toggle is about to enter and
1722
+ * `artifactId` is the currently selected artifact (or `null` when none).
1723
+ */
1724
+ onArtifactAction?: (
1725
+ action:
1726
+ | { type: 'open' | 'download'; artifactId: string }
1727
+ | { type: 'expand'; artifactId: string | null; expanded: boolean }
1728
+ ) => boolean | void;
859
1729
  /**
860
1730
  * Custom renderer for artifact reference cards shown in the message thread.
861
1731
  * Return an HTMLElement to replace the default card, or `null` to use the default.
@@ -870,6 +1740,73 @@ export type AgentWidgetArtifactsFeature = {
870
1740
  config: AgentWidgetConfig;
871
1741
  defaultRenderer: () => HTMLElement;
872
1742
  }) => HTMLElement | null;
1743
+ /**
1744
+ * Custom renderer for inline artifact blocks (display: "inline"), mirroring
1745
+ * `renderCard`. Return an element to replace the default inline preview,
1746
+ * or null to use the default.
1747
+ */
1748
+ renderInline?: (context: {
1749
+ artifact: {
1750
+ artifactId: string;
1751
+ title: string;
1752
+ artifactType: string;
1753
+ status: string;
1754
+ };
1755
+ config: AgentWidgetConfig;
1756
+ defaultRenderer: () => HTMLElement;
1757
+ }) => HTMLElement | null;
1758
+ /**
1759
+ * Text shown while an artifact streams, in place of the default
1760
+ * `Generating <type>...` status. Applies to every streaming surface: the
1761
+ * reference card status line, the inline chrome meta label, and the inline
1762
+ * streaming status body (`inlineBody.streamingView: "status"`).
1763
+ *
1764
+ * - A plain string replaces the default label everywhere (localization or
1765
+ * brand voice).
1766
+ * - A function is called on every artifact update for each visible surface;
1767
+ * it must be pure and fast. Returning a string sets the animated label
1768
+ * only. Returning `{ label, detail }` splits the status into an animated
1769
+ * label (re-applied only when its text changes, so the loading animation
1770
+ * stays stable) and a plain un-animated detail span that may update freely
1771
+ * per delta (live counters).
1772
+ *
1773
+ * Default when unset: the current behavior, `Generating <type>...` with no
1774
+ * detail. If the function throws, the default label is used for that update
1775
+ * (a bad host callback must never break rendering). There is deliberately no
1776
+ * percent progress: streams do not announce total length, so counts and
1777
+ * elapsed time are the only truthful signals.
1778
+ */
1779
+ statusLabel?:
1780
+ | string
1781
+ | ((ctx: PersonaArtifactStatusLabelContext) =>
1782
+ | string
1783
+ | { label: string; detail?: string });
1784
+ /**
1785
+ * Animation applied to the artifact card's "Generating …" status text while
1786
+ * the artifact streams. Character-by-character modes (`shimmer`,
1787
+ * `shimmer-color`, `rainbow`) wrap each character in a span with a staggered
1788
+ * `animation-delay`; `pulse` fades the whole status text; `none` disables the
1789
+ * animation. Honors `prefers-reduced-motion`.
1790
+ * @default "shimmer"
1791
+ */
1792
+ loadingAnimation?: AgentWidgetToolCallLoadingAnimation;
1793
+ /**
1794
+ * Duration of one full animation cycle in milliseconds.
1795
+ * Applies to pulse, shimmer, shimmer-color, and rainbow modes.
1796
+ * @default 2000
1797
+ */
1798
+ loadingAnimationDuration?: number;
1799
+ /**
1800
+ * Primary color for shimmer-color animation mode.
1801
+ * Defaults to the current text color.
1802
+ */
1803
+ loadingAnimationColor?: string;
1804
+ /**
1805
+ * Secondary/end color for shimmer-color animation mode.
1806
+ * Creates a gradient sweep between `loadingAnimationColor` and this color.
1807
+ * @default "#3b82f6"
1808
+ */
1809
+ loadingAnimationSecondaryColor?: string;
873
1810
  };
874
1811
 
875
1812
  /**
@@ -1012,6 +1949,13 @@ export type AgentWidgetToolCallDisplayFeature = {
1012
1949
  * @default false
1013
1950
  */
1014
1951
  grouped?: boolean;
1952
+ /**
1953
+ * Controls how a grouped tool sequence is rendered.
1954
+ * - `"stack"`: show the group summary and each child tool row.
1955
+ * - `"summary"`: show one consolidated summary row only.
1956
+ * @default "stack"
1957
+ */
1958
+ groupedMode?: "stack" | "summary";
1015
1959
  /**
1016
1960
  * When false, tool call bubbles show only the collapsed summary with no
1017
1961
  * expand/collapse toggle. Users see tool awareness without full details.
@@ -1740,6 +2684,24 @@ export type AgentWidgetLauncherConfig = {
1740
2684
  * @default false
1741
2685
  */
1742
2686
  sidebarMode?: boolean;
2687
+ /**
2688
+ * When true, the widget panel renders as a detached card inset within its region,
2689
+ * with rounded corners on all sides and elevation (shadow plus hairline border),
2690
+ * instead of sitting flush against the region edges.
2691
+ *
2692
+ * The gap around the card is themed via `theme.components.panel.inset`. The
2693
+ * background revealed behind it is themed via `theme.components.panel.canvasBackground`
2694
+ * in docked and inline embed modes. In sidebar mode the gap stays click-through, so the
2695
+ * host page shows through it the same way it does in floating mode, and canvasBackground
2696
+ * is a no-op there.
2697
+ *
2698
+ * Applies to sidebar, docked, and inline embed modes. Ignored in mobile fullscreen,
2699
+ * where chrome is already suppressed. Floating mode is unchanged, since it already
2700
+ * renders as a detached card.
2701
+ *
2702
+ * @default false
2703
+ */
2704
+ detachedPanel?: boolean;
1743
2705
  /**
1744
2706
  * Width of the sidebar panel when sidebarMode is true.
1745
2707
  * @default "420px"
@@ -4005,6 +4967,18 @@ export type AgentWidgetConfig = {
4005
4967
  }) => string;
4006
4968
  plugins?: AgentWidgetPlugin[];
4007
4969
  contextProviders?: AgentWidgetContextProvider[];
4970
+ /**
4971
+ * Context mentions: let users explicitly pull external context into a turn by
4972
+ * typing `@` or clicking a visible composer button, both opening one shared
4973
+ * searchable menu of host-provided sources. Selecting a mention adds a
4974
+ * removable pill chip; resolved content reaches the model via `llmAppend`
4975
+ * (default) or the opt-in structured `context` channel.
4976
+ *
4977
+ * Disabled by default. When enabled, the mention runtime is lazy-loaded from a
4978
+ * sibling chunk on first use, so sites that leave it off pay no bundle cost.
4979
+ * See `docs/context-mentions-plan.md`.
4980
+ */
4981
+ contextMentions?: AgentWidgetContextMentionConfig;
4008
4982
  requestMiddleware?: AgentWidgetRequestMiddleware;
4009
4983
  actionParsers?: AgentWidgetActionParser[];
4010
4984
  actionHandlers?: AgentWidgetActionHandler[];
@@ -4556,6 +5530,25 @@ export type AgentWidgetMessage = {
4556
5530
  * The `content` field contains the text-only representation for display.
4557
5531
  */
4558
5532
  contentParts?: ContentPart[];
5533
+ /**
5534
+ * Context mentions attached to this (user) message, for transcript fidelity +
5535
+ * chip rendering in the sent bubble. Refs only — the resolved payloads are
5536
+ * merged into `llmContent`/`contentParts` (model-visible) at send time.
5537
+ */
5538
+ contextMentions?: AgentWidgetContextMentionRef[];
5539
+ /**
5540
+ * Ordered prose + mention blocks for inline-mode rendering and composer history
5541
+ * recall (`contextMentions.display: "inline"`). Present only on inline-mode user
5542
+ * messages; when omitted the sent bubble falls back to the chip row derived from
5543
+ * `contextMentions`. Display/transcript concern only — not the model channel.
5544
+ */
5545
+ contentSegments?: AgentWidgetContentSegment[];
5546
+ /**
5547
+ * Resolved opt-in structured mention context for this (user) message, merged
5548
+ * into the request `context` under `mentions.<sourceId>.<itemId>` by the
5549
+ * client. Internal: set at send time from each source's `resolve().context`.
5550
+ */
5551
+ mentionContext?: Record<string, unknown>;
4559
5552
  streaming?: boolean;
4560
5553
  variant?: AgentWidgetMessageVariant;
4561
5554
  sequence?: number;
@@ -4801,6 +5794,22 @@ export type InjectComponentDirectiveOptions = {
4801
5794
  sequence?: number;
4802
5795
  };
4803
5796
 
5797
+ /**
5798
+ * Optional file metadata attached to a markdown artifact that represents a
5799
+ * concrete file written by a Claude Managed agent (e.g. `/mnt/session/outputs/cat.html`).
5800
+ * Additive: the artifact content stays a fenced code block on the wire for
5801
+ * backward compatibility; new widgets use this to recover the raw source and
5802
+ * offer a preview. Mirrors the `file` field on the core `artifact_start` event.
5803
+ */
5804
+ export type PersonaArtifactFileMeta = {
5805
+ /** Full path of the written file (also used as the artifact title). */
5806
+ path: string;
5807
+ /** MIME type of the file (used for download and preview decisions). */
5808
+ mimeType: string;
5809
+ /** Fence language the content was encoded with (html/xml/md/text). */
5810
+ language?: string;
5811
+ };
5812
+
4804
5813
  export type PersonaArtifactRecord = {
4805
5814
  id: string;
4806
5815
  artifactType: PersonaArtifactKind;
@@ -4809,17 +5818,30 @@ export type PersonaArtifactRecord = {
4809
5818
  markdown?: string;
4810
5819
  component?: string;
4811
5820
  props?: Record<string, unknown>;
5821
+ /** Present when this markdown artifact is a previewable file (see PersonaArtifactFileMeta). */
5822
+ file?: PersonaArtifactFileMeta;
4812
5823
  };
4813
5824
 
4814
5825
  /** Programmatic artifact upsert (controller / window API) */
4815
5826
  export type PersonaArtifactManualUpsert =
4816
- | { id?: string; artifactType: "markdown"; title?: string; content: string }
5827
+ | {
5828
+ id?: string;
5829
+ artifactType: "markdown";
5830
+ title?: string;
5831
+ content: string;
5832
+ /** Optional file metadata for previewable file artifacts. */
5833
+ file?: PersonaArtifactFileMeta;
5834
+ /** Set false to update the registry/pane without a transcript block (pre-4.x behavior). */
5835
+ transcript?: boolean;
5836
+ }
4817
5837
  | {
4818
5838
  id?: string;
4819
5839
  artifactType: "component";
4820
5840
  title?: string;
4821
5841
  component: string;
4822
5842
  props?: Record<string, unknown>;
5843
+ /** Set false to update the registry/pane without a transcript block (pre-4.x behavior). */
5844
+ transcript?: boolean;
4823
5845
  };
4824
5846
 
4825
5847
  export type AgentWidgetEvent =
@@ -4850,6 +5872,8 @@ export type AgentWidgetEvent =
4850
5872
  artifactType: PersonaArtifactKind;
4851
5873
  title?: string;
4852
5874
  component?: string;
5875
+ /** Present when this markdown artifact is a previewable file. */
5876
+ file?: PersonaArtifactFileMeta;
4853
5877
  }
4854
5878
  | { type: "artifact_delta"; id: string; artDelta: string }
4855
5879
  | {