@runtypelabs/persona 3.29.0 → 3.30.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 (63) hide show
  1. package/README.md +55 -0
  2. package/dist/animations/glyph-cycle.d.cts +1 -1
  3. package/dist/animations/glyph-cycle.d.ts +1 -1
  4. package/dist/animations/{types-CxvHw0X6.d.cts → types-B_Qazlm4.d.cts} +6 -0
  5. package/dist/animations/{types-CxvHw0X6.d.ts → types-B_Qazlm4.d.ts} +6 -0
  6. package/dist/animations/wipe.d.cts +1 -1
  7. package/dist/animations/wipe.d.ts +1 -1
  8. package/dist/codegen.cjs +6 -6
  9. package/dist/codegen.js +4 -4
  10. package/dist/index.cjs +47 -47
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +127 -7
  13. package/dist/index.d.ts +127 -7
  14. package/dist/index.global.js +60 -60
  15. package/dist/index.global.js.map +1 -1
  16. package/dist/index.js +47 -47
  17. package/dist/index.js.map +1 -1
  18. package/dist/launcher.global.js +2 -2
  19. package/dist/launcher.global.js.map +1 -1
  20. package/dist/plugin-kit.cjs +1 -0
  21. package/dist/plugin-kit.d.cts +98 -0
  22. package/dist/plugin-kit.d.ts +98 -0
  23. package/dist/plugin-kit.js +1 -0
  24. package/dist/smart-dom-reader.d.cts +113 -4
  25. package/dist/smart-dom-reader.d.ts +113 -4
  26. package/dist/theme-editor.cjs +38 -38
  27. package/dist/theme-editor.d.cts +117 -6
  28. package/dist/theme-editor.d.ts +117 -6
  29. package/dist/theme-editor.js +38 -38
  30. package/dist/theme-reference.cjs +1 -1
  31. package/dist/theme-reference.d.cts +2 -0
  32. package/dist/theme-reference.d.ts +2 -0
  33. package/dist/theme-reference.js +1 -1
  34. package/package.json +8 -2
  35. package/src/client.test.ts +40 -0
  36. package/src/client.ts +18 -4
  37. package/src/components/approval-bubble.test.ts +268 -0
  38. package/src/components/approval-bubble.ts +170 -20
  39. package/src/components/launcher.ts +6 -3
  40. package/src/components/tool-bubble.test.ts +39 -0
  41. package/src/components/tool-bubble.ts +4 -0
  42. package/src/generated/runtype-openapi-contract.ts +12 -0
  43. package/src/index-core.ts +1 -0
  44. package/src/plugin-kit.test.ts +230 -0
  45. package/src/plugin-kit.ts +294 -0
  46. package/src/plugins/types.ts +49 -2
  47. package/src/session.test.ts +99 -0
  48. package/src/session.ts +204 -32
  49. package/src/session.webmcp.test.ts +326 -6
  50. package/src/theme-editor/preview-utils.test.ts +10 -0
  51. package/src/theme-editor/preview-utils.ts +29 -1
  52. package/src/theme-editor/sections.test.ts +17 -0
  53. package/src/theme-editor/sections.ts +31 -0
  54. package/src/theme-reference.ts +1 -1
  55. package/src/types/theme.ts +2 -0
  56. package/src/types.ts +59 -2
  57. package/src/ui.approval-plugin.test.ts +204 -0
  58. package/src/ui.ts +149 -16
  59. package/src/utils/theme.test.ts +6 -2
  60. package/src/utils/theme.ts +0 -8
  61. package/src/utils/tokens.ts +6 -1
  62. package/src/webmcp-bridge.test.ts +66 -0
  63. package/src/webmcp-bridge.ts +49 -0
@@ -0,0 +1 @@
1
+ "use strict";var w=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var H=Object.getOwnPropertyNames;var M=Object.prototype.hasOwnProperty;var N=(e,t)=>{for(var o in t)w(e,o,{get:t[o],enumerable:!0})},I=(e,t,o,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of H(t))!M.call(e,r)&&r!==o&&w(e,r,{get:()=>t[r],enumerable:!(n=C(t,r))||n.enumerable});return e};var O=e=>I(w({},"__esModule",{value:!0}),e);var B={};N(B,{createPopover:()=>$,getStyleRoot:()=>g,injectStyles:()=>z,isEditableEventTarget:()=>W});module.exports=O(B);function g(e){var o,n;let t=(o=e.getRootNode)==null?void 0:o.call(e);return t instanceof ShadowRoot||t instanceof Document?t:(n=e.ownerDocument)!=null?n:document}function v(e,t,o){var s;let n=e instanceof Document?e.head:e,r=t.replace(/["\\]/g,"\\$&");if(n.querySelector(`style[data-persona-plugin-style="${r}"]`))return;let c=(e instanceof Document?e:(s=e.ownerDocument)!=null?s:document).createElement("style");c.setAttribute("data-persona-plugin-style",t),c.textContent=o,n.appendChild(c)}function z(e,t,o){var l;if(e instanceof Document||e instanceof ShadowRoot){v(e,t,o);return}let n=e;if(n.isConnected){v(g(n),t,o);return}let r=(l=n.ownerDocument)!=null?l:document;v(r,t,o),queueMicrotask(()=>{let c=g(n);c!==r&&v(c,t,o)})}function A(e){var o,n;let t=(o=e.getRootNode)==null?void 0:o.call(e);return t instanceof ShadowRoot?t:((n=e.ownerDocument)!=null?n:document).body}function $(e){var y;let{anchor:t,content:o,placement:n="bottom-start",offset:r=6,matchAnchorWidth:l=!1,zIndex:c=2147483e3,onOpen:s,onDismiss:a}=e,S=(y=e.container)!=null?y:A(t),d=!1,m=null,h=()=>{if(!d)return;let i=t.getBoundingClientRect();o.style.position="fixed",l&&(o.style.minWidth=`${i.width}px`);let f=n==="top-start"||n==="top-end"?i.top-r-o.getBoundingClientRect().height:i.bottom+r,u=n==="bottom-end"||n==="top-end"?i.right-o.getBoundingClientRect().width:i.left;o.style.top=`${f}px`,o.style.left=`${u}px`},p=()=>{d&&(d=!1,m&&(m(),m=null),o.remove())},E=()=>{var x,b,P;if(d)return;d=!0,c!=null&&(o.style.zIndex=String(c)),S.appendChild(o),h();let i=(b=((x=t.ownerDocument)!=null?x:document).defaultView)!=null?b:window,f=(P=t.ownerDocument)!=null?P:document,u=()=>{if(!t.isConnected){p(),a==null||a("anchor-removed");return}h()},R=D=>{let L=typeof D.composedPath=="function"?D.composedPath():[];L.includes(o)||L.includes(t)||(p(),a==null||a("outside"))},T=i.setTimeout(()=>{f.addEventListener("pointerdown",R,!0)},0);i.addEventListener("scroll",u,!0),i.addEventListener("resize",u),m=()=>{i.clearTimeout(T),f.removeEventListener("pointerdown",R,!0),i.removeEventListener("scroll",u,!0),i.removeEventListener("resize",u)},s==null||s()};return{get isOpen(){return d},open:E,close:p,toggle:()=>d?p():E(),reposition:h,destroy:p}}function W(e){return(typeof e.composedPath=="function"?e.composedPath():[]).some(o=>o instanceof HTMLElement&&(o.tagName==="INPUT"||o.tagName==="TEXTAREA"||o.isContentEditable))}0&&(module.exports={createPopover,getStyleRoot,injectStyles,isEditableEventTarget});
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Resolve the root a node's styles should live in.
3
+ *
4
+ * Returns the node's `ShadowRoot` when it (or an ancestor) is shadowed, the
5
+ * owning `Document` otherwise. A `<style>` placed in this root reaches the node
6
+ * regardless of whether the widget runs in light or shadow DOM. For a detached
7
+ * node (one not yet mounted) this falls back to the owning document.
8
+ */
9
+ declare function getStyleRoot(node: Node): Document | ShadowRoot;
10
+ /**
11
+ * Inject a plugin's CSS once into the correct root — the widget's shadow root
12
+ * when shadowed, the document head otherwise. Idempotent: keyed by `id`, so it
13
+ * is safe to call on every render.
14
+ *
15
+ * Pass the element you're about to return from a render hook. While building, an
16
+ * element is detached and its eventual root is unknown, so this injects into the
17
+ * owning document immediately (covering the default light-DOM case with no
18
+ * flash) and then, on the next microtask — after the widget has mounted the
19
+ * element — re-resolves and also injects into its shadow root if it landed in
20
+ * one. You may also pass an explicit `Document` or `ShadowRoot`.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * renderApproval: ({ message, approve, deny }) => {
25
+ * const card = buildCard(message.approval, approve, deny);
26
+ * injectStyles(card, "my-approval-plugin", CSS);
27
+ * return card;
28
+ * }
29
+ * ```
30
+ */
31
+ declare function injectStyles(target: Node | Document | ShadowRoot, id: string, css: string): void;
32
+ type PopoverPlacement = "bottom-start" | "bottom-end" | "top-start" | "top-end";
33
+ interface PopoverOptions {
34
+ /** Element the popover is positioned against. */
35
+ anchor: HTMLElement;
36
+ /** The floating element (menu, tooltip, panel). Built detached; mounted on `open`. */
37
+ content: HTMLElement;
38
+ /** Where to place `content` relative to `anchor`. Default `"bottom-start"`. */
39
+ placement?: PopoverPlacement;
40
+ /** Gap in px between anchor and content. Default `6`. */
41
+ offset?: number;
42
+ /** Set `content`'s `min-width` to the anchor's width. Default `false`. */
43
+ matchAnchorWidth?: boolean;
44
+ /**
45
+ * Inline `z-index` for `content`. Default `2147483000` so it overlays the rest
46
+ * of the widget. Pass `null` to leave z-index to your own CSS.
47
+ */
48
+ zIndex?: number | null;
49
+ /**
50
+ * Where to mount `content`. Defaults to the anchor's shadow root (when
51
+ * shadowed) or `document.body` — keeping it inside the same style + stacking
52
+ * scope as the anchor while escaping the transcript's scroll clipping.
53
+ */
54
+ container?: HTMLElement | ShadowRoot;
55
+ onOpen?: () => void;
56
+ /** Fired when the popover closes by itself (outside click or the anchor leaving the DOM). */
57
+ onDismiss?: (reason: "outside" | "anchor-removed") => void;
58
+ }
59
+ interface PopoverHandle {
60
+ readonly isOpen: boolean;
61
+ open(): void;
62
+ close(): void;
63
+ toggle(): void;
64
+ /** Recompute position from the current anchor rect (called automatically on scroll/resize). */
65
+ reposition(): void;
66
+ /** Close and release all listeners; the handle is inert afterward. */
67
+ destroy(): void;
68
+ }
69
+ /**
70
+ * A floating popover anchored to an element: `fixed`-positioned (so it overlays
71
+ * the rest of the widget and isn't clipped by scroll containers), dismissed on
72
+ * outside pointerdown, repositioned on scroll/resize, and auto-closed if the
73
+ * anchor leaves the DOM. Mount your styles with {@link injectStyles}.
74
+ *
75
+ * @example
76
+ * ```ts
77
+ * const popover = createPopover({
78
+ * anchor: splitButton,
79
+ * content: menu,
80
+ * placement: "bottom-start",
81
+ * matchAnchorWidth: true,
82
+ * });
83
+ * caret.addEventListener("click", () => popover.toggle());
84
+ * // on teardown: popover.destroy();
85
+ * ```
86
+ */
87
+ declare function createPopover(options: PopoverOptions): PopoverHandle;
88
+ /**
89
+ * Whether an event originated from an editable element (`<input>`, `<textarea>`,
90
+ * or `contenteditable`). Use it to avoid hijacking keys like Enter/Escape while
91
+ * the user is typing in the composer.
92
+ *
93
+ * Inspects the composed path, so it works for events that cross the widget's
94
+ * Shadow-DOM boundary (where `event.target` is retargeted to the host).
95
+ */
96
+ declare function isEditableEventTarget(event: Event): boolean;
97
+
98
+ export { type PopoverHandle, type PopoverOptions, type PopoverPlacement, createPopover, getStyleRoot, injectStyles, isEditableEventTarget };
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Resolve the root a node's styles should live in.
3
+ *
4
+ * Returns the node's `ShadowRoot` when it (or an ancestor) is shadowed, the
5
+ * owning `Document` otherwise. A `<style>` placed in this root reaches the node
6
+ * regardless of whether the widget runs in light or shadow DOM. For a detached
7
+ * node (one not yet mounted) this falls back to the owning document.
8
+ */
9
+ declare function getStyleRoot(node: Node): Document | ShadowRoot;
10
+ /**
11
+ * Inject a plugin's CSS once into the correct root — the widget's shadow root
12
+ * when shadowed, the document head otherwise. Idempotent: keyed by `id`, so it
13
+ * is safe to call on every render.
14
+ *
15
+ * Pass the element you're about to return from a render hook. While building, an
16
+ * element is detached and its eventual root is unknown, so this injects into the
17
+ * owning document immediately (covering the default light-DOM case with no
18
+ * flash) and then, on the next microtask — after the widget has mounted the
19
+ * element — re-resolves and also injects into its shadow root if it landed in
20
+ * one. You may also pass an explicit `Document` or `ShadowRoot`.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * renderApproval: ({ message, approve, deny }) => {
25
+ * const card = buildCard(message.approval, approve, deny);
26
+ * injectStyles(card, "my-approval-plugin", CSS);
27
+ * return card;
28
+ * }
29
+ * ```
30
+ */
31
+ declare function injectStyles(target: Node | Document | ShadowRoot, id: string, css: string): void;
32
+ type PopoverPlacement = "bottom-start" | "bottom-end" | "top-start" | "top-end";
33
+ interface PopoverOptions {
34
+ /** Element the popover is positioned against. */
35
+ anchor: HTMLElement;
36
+ /** The floating element (menu, tooltip, panel). Built detached; mounted on `open`. */
37
+ content: HTMLElement;
38
+ /** Where to place `content` relative to `anchor`. Default `"bottom-start"`. */
39
+ placement?: PopoverPlacement;
40
+ /** Gap in px between anchor and content. Default `6`. */
41
+ offset?: number;
42
+ /** Set `content`'s `min-width` to the anchor's width. Default `false`. */
43
+ matchAnchorWidth?: boolean;
44
+ /**
45
+ * Inline `z-index` for `content`. Default `2147483000` so it overlays the rest
46
+ * of the widget. Pass `null` to leave z-index to your own CSS.
47
+ */
48
+ zIndex?: number | null;
49
+ /**
50
+ * Where to mount `content`. Defaults to the anchor's shadow root (when
51
+ * shadowed) or `document.body` — keeping it inside the same style + stacking
52
+ * scope as the anchor while escaping the transcript's scroll clipping.
53
+ */
54
+ container?: HTMLElement | ShadowRoot;
55
+ onOpen?: () => void;
56
+ /** Fired when the popover closes by itself (outside click or the anchor leaving the DOM). */
57
+ onDismiss?: (reason: "outside" | "anchor-removed") => void;
58
+ }
59
+ interface PopoverHandle {
60
+ readonly isOpen: boolean;
61
+ open(): void;
62
+ close(): void;
63
+ toggle(): void;
64
+ /** Recompute position from the current anchor rect (called automatically on scroll/resize). */
65
+ reposition(): void;
66
+ /** Close and release all listeners; the handle is inert afterward. */
67
+ destroy(): void;
68
+ }
69
+ /**
70
+ * A floating popover anchored to an element: `fixed`-positioned (so it overlays
71
+ * the rest of the widget and isn't clipped by scroll containers), dismissed on
72
+ * outside pointerdown, repositioned on scroll/resize, and auto-closed if the
73
+ * anchor leaves the DOM. Mount your styles with {@link injectStyles}.
74
+ *
75
+ * @example
76
+ * ```ts
77
+ * const popover = createPopover({
78
+ * anchor: splitButton,
79
+ * content: menu,
80
+ * placement: "bottom-start",
81
+ * matchAnchorWidth: true,
82
+ * });
83
+ * caret.addEventListener("click", () => popover.toggle());
84
+ * // on teardown: popover.destroy();
85
+ * ```
86
+ */
87
+ declare function createPopover(options: PopoverOptions): PopoverHandle;
88
+ /**
89
+ * Whether an event originated from an editable element (`<input>`, `<textarea>`,
90
+ * or `contenteditable`). Use it to avoid hijacking keys like Enter/Escape while
91
+ * the user is typing in the composer.
92
+ *
93
+ * Inspects the composed path, so it works for events that cross the widget's
94
+ * Shadow-DOM boundary (where `event.target` is retargeted to the host).
95
+ */
96
+ declare function isEditableEventTarget(event: Event): boolean;
97
+
98
+ export { type PopoverHandle, type PopoverOptions, type PopoverPlacement, createPopover, getStyleRoot, injectStyles, isEditableEventTarget };
@@ -0,0 +1 @@
1
+ function D(t){var e,n;let o=(e=t.getRootNode)==null?void 0:e.call(t);return o instanceof ShadowRoot||o instanceof Document?o:(n=t.ownerDocument)!=null?n:document}function v(t,o,e){var s;let n=t instanceof Document?t.head:t,c=o.replace(/["\\]/g,"\\$&");if(n.querySelector(`style[data-persona-plugin-style="${c}"]`))return;let i=(t instanceof Document?t:(s=t.ownerDocument)!=null?s:document).createElement("style");i.setAttribute("data-persona-plugin-style",o),i.textContent=e,n.appendChild(i)}function C(t,o,e){var l;if(t instanceof Document||t instanceof ShadowRoot){v(t,o,e);return}let n=t;if(n.isConnected){v(D(n),o,e);return}let c=(l=n.ownerDocument)!=null?l:document;v(c,o,e),queueMicrotask(()=>{let i=D(n);i!==c&&v(i,o,e)})}function T(t){var e,n;let o=(e=t.getRootNode)==null?void 0:e.call(t);return o instanceof ShadowRoot?o:((n=t.ownerDocument)!=null?n:document).body}function H(t){var g;let{anchor:o,content:e,placement:n="bottom-start",offset:c=6,matchAnchorWidth:l=!1,zIndex:i=2147483e3,onOpen:s,onDismiss:a}=t,L=(g=t.container)!=null?g:T(o),d=!1,m=null,h=()=>{if(!d)return;let r=o.getBoundingClientRect();e.style.position="fixed",l&&(e.style.minWidth=`${r.width}px`);let f=n==="top-start"||n==="top-end"?r.top-c-e.getBoundingClientRect().height:r.bottom+c,u=n==="bottom-end"||n==="top-end"?r.right-e.getBoundingClientRect().width:r.left;e.style.top=`${f}px`,e.style.left=`${u}px`},p=()=>{d&&(d=!1,m&&(m(),m=null),e.remove())},w=()=>{var y,R,x;if(d)return;d=!0,i!=null&&(e.style.zIndex=String(i)),L.appendChild(e),h();let r=(R=((y=o.ownerDocument)!=null?y:document).defaultView)!=null?R:window,f=(x=o.ownerDocument)!=null?x:document,u=()=>{if(!o.isConnected){p(),a==null||a("anchor-removed");return}h()},E=b=>{let P=typeof b.composedPath=="function"?b.composedPath():[];P.includes(e)||P.includes(o)||(p(),a==null||a("outside"))},S=r.setTimeout(()=>{f.addEventListener("pointerdown",E,!0)},0);r.addEventListener("scroll",u,!0),r.addEventListener("resize",u),m=()=>{r.clearTimeout(S),f.removeEventListener("pointerdown",E,!0),r.removeEventListener("scroll",u,!0),r.removeEventListener("resize",u)},s==null||s()};return{get isOpen(){return d},open:w,close:p,toggle:()=>d?p():w(),reposition:h,destroy:p}}function M(t){return(typeof t.composedPath=="function"?t.composedPath():[]).some(e=>e instanceof HTMLElement&&(e.tagName==="INPUT"||e.tagName==="TEXTAREA"||e.isContentEditable))}export{H as createPopover,D as getStyleRoot,C as injectStyles,M as isEditableEventTarget};
@@ -416,13 +416,59 @@ interface AgentWidgetPlugin {
416
416
  config: AgentWidgetConfig;
417
417
  }) => HTMLElement | null;
418
418
  /**
419
- * Custom renderer for approval bubbles
420
- * Return null to use default renderer
419
+ * Custom renderer for approval bubbles.
420
+ *
421
+ * Return an `HTMLElement` to fully own the approval UI, `defaultRenderer()`
422
+ * to render (or wrap) the built-in bubble, or `null` to fall through to the
423
+ * default. Unlike the built-in bubble — whose Approve/Deny buttons are wired
424
+ * via delegation — a fully custom element resolves the approval by calling
425
+ * the `approve`/`deny` callbacks. Both route through the same path the
426
+ * built-in buttons use (optimistic update, `onDecision`, in-place anchoring).
427
+ *
428
+ * An approval is a single binary gate, so there are exactly two outcomes.
429
+ * Pass `{ remember: true }` to flag a "remember this" affordance (e.g. an
430
+ * "Always allow" button); the current approval resolves identically, but the
431
+ * flag is forwarded to `config.approval.onDecision` so you can persist a
432
+ * don't-ask-again policy for future approvals.
433
+ *
434
+ * `renderApproval` is called again whenever the approval's status changes, so
435
+ * branch on `message.approval?.status` to render the resolved state (and tear
436
+ * down any global listeners you added while pending).
437
+ *
438
+ * @example
439
+ * ```typescript
440
+ * // An alternative prompt: "Always allow" / "Allow once" / "Deny".
441
+ * renderApproval: ({ message, approve, deny }) => {
442
+ * const approval = message.approval;
443
+ * if (!approval || approval.status !== "pending") return null; // default renders resolved state
444
+ * const root = document.createElement("div");
445
+ * root.textContent = `${approval.toolName} requires approval`;
446
+ *
447
+ * const always = document.createElement("button");
448
+ * always.textContent = "Always allow";
449
+ * always.addEventListener("click", () => approve({ remember: true }));
450
+ *
451
+ * const once = document.createElement("button");
452
+ * once.textContent = "Allow once";
453
+ * once.addEventListener("click", () => approve());
454
+ *
455
+ * const no = document.createElement("button");
456
+ * no.textContent = "Deny";
457
+ * no.addEventListener("click", () => deny());
458
+ *
459
+ * root.append(always, once, no);
460
+ * return root;
461
+ * }
462
+ * ```
421
463
  */
422
464
  renderApproval?: (context: {
423
465
  message: AgentWidgetMessage;
424
466
  defaultRenderer: () => HTMLElement;
425
467
  config: AgentWidgetConfig;
468
+ /** Resolve this approval as approved. Pass `{ remember: true }` for an "Always allow" affordance. */
469
+ approve: (options?: AgentWidgetApprovalDecisionOptions) => void;
470
+ /** Resolve this approval as denied. Pass `{ remember: true }` for an "Always deny" affordance. */
471
+ deny: (options?: AgentWidgetApprovalDecisionOptions) => void;
426
472
  }) => HTMLElement | null;
427
473
  /**
428
474
  * Custom renderer for loading indicator
@@ -797,6 +843,8 @@ interface ApprovalTokens {
797
843
  background: TokenReference<'color'>;
798
844
  border: TokenReference<'color'>;
799
845
  text: TokenReference<'color'>;
846
+ /** Box-shadow for the approval bubble (token ref or raw CSS, e.g. `none`). */
847
+ shadow: string;
800
848
  };
801
849
  approve: ComponentTokenSet;
802
850
  deny: ComponentTokenSet;
@@ -1169,6 +1217,7 @@ type RuntypeAgentSSEEvent = {
1169
1217
  iteration: number;
1170
1218
  reflection?: string;
1171
1219
  seq: number;
1220
+ timestamp?: string;
1172
1221
  type: "agent_reflection";
1173
1222
  } | {
1174
1223
  activatedCapabilities: Array<string>;
@@ -1176,6 +1225,7 @@ type RuntypeAgentSSEEvent = {
1176
1225
  iteration: number;
1177
1226
  seq: number;
1178
1227
  skill: string;
1228
+ timestamp?: string;
1179
1229
  toolCallId: string;
1180
1230
  type: "agent_skill_loaded";
1181
1231
  } | ({
@@ -1185,6 +1235,7 @@ type RuntypeAgentSSEEvent = {
1185
1235
  proposalId?: string;
1186
1236
  seq: number;
1187
1237
  skill: string;
1238
+ timestamp?: string;
1188
1239
  toolCallId: string;
1189
1240
  type: "agent_skill_proposed";
1190
1241
  }) | ({
@@ -1218,6 +1269,7 @@ type RuntypeAgentSSEEvent = {
1218
1269
  iteration?: number;
1219
1270
  recoverable: boolean;
1220
1271
  seq: number;
1272
+ timestamp?: string;
1221
1273
  type: "agent_error";
1222
1274
  } | {
1223
1275
  executionId: string;
@@ -1251,6 +1303,7 @@ type RuntypeFlowSSEEvent = {
1251
1303
  failedSteps?: number;
1252
1304
  finalOutput?: string;
1253
1305
  flowId?: string;
1306
+ flowName?: string;
1254
1307
  output?: unknown;
1255
1308
  seq?: number;
1256
1309
  source?: string;
@@ -1302,6 +1355,7 @@ type RuntypeFlowSSEEvent = {
1302
1355
  id?: string;
1303
1356
  index?: number;
1304
1357
  name?: string;
1358
+ outputVariable?: string;
1305
1359
  seq?: number;
1306
1360
  startedAt: string;
1307
1361
  stepId?: string;
@@ -1709,6 +1763,12 @@ type WebMcpConfirmInfo = {
1709
1763
  toolName: string;
1710
1764
  args: unknown;
1711
1765
  description?: string;
1766
+ /**
1767
+ * Display title the tool declared via the WebMCP spec's
1768
+ * `ToolDescriptor.title` (e.g. `"Add to Cart"`). Absent when the tool
1769
+ * didn't declare one.
1770
+ */
1771
+ title?: string;
1712
1772
  annotations?: {
1713
1773
  readOnlyHint?: boolean;
1714
1774
  untrustedContentHint?: boolean;
@@ -3023,6 +3083,22 @@ type TextToSpeechConfig = {
3023
3083
  /** Speech pitch (0 - 2). Default: 1 */
3024
3084
  pitch?: number;
3025
3085
  };
3086
+ /**
3087
+ * Extra context for an approval decision, surfaced to `onDecision` and to the
3088
+ * `approve`/`deny` callbacks passed to the `renderApproval` plugin hook.
3089
+ */
3090
+ type AgentWidgetApprovalDecisionOptions = {
3091
+ /**
3092
+ * The user chose a "remember this" affordance (e.g. an "Always allow"
3093
+ * button) rather than a one-time decision. The widget resolves the *current*
3094
+ * approval identically whether or not this is set — an approval bubble is a
3095
+ * single binary gate (`approved`/`denied`). Persisting a don't-ask-again
3096
+ * policy for *future* approvals (auto-resolving them, or not surfacing them)
3097
+ * is the integrator's responsibility, typically inside `onDecision`.
3098
+ * Defaults to absent/`false`.
3099
+ */
3100
+ remember?: boolean;
3101
+ };
3026
3102
  /**
3027
3103
  * Configuration for tool approval bubbles.
3028
3104
  * Controls styling, labels, and behavior of the approval UI.
@@ -3032,6 +3108,8 @@ type AgentWidgetApprovalConfig = {
3032
3108
  backgroundColor?: string;
3033
3109
  /** Border color of the approval bubble */
3034
3110
  borderColor?: string;
3111
+ /** Box-shadow for the approval bubble; pass `"none"` to remove it. Overrides the default `persona-shadow-sm`. */
3112
+ shadow?: string;
3035
3113
  /** Color for the title text */
3036
3114
  titleColor?: string;
3037
3115
  /** Color for the description text */
@@ -3054,20 +3132,51 @@ type AgentWidgetApprovalConfig = {
3054
3132
  approveLabel?: string;
3055
3133
  /** Label for the deny button */
3056
3134
  denyLabel?: string;
3135
+ /**
3136
+ * How the technical details (the tool's agent-facing description and the
3137
+ * raw parameters JSON) are presented:
3138
+ * - `"collapsed"` (default) — hidden behind a "Show details" toggle
3139
+ * - `"expanded"` — visible, with the toggle available to hide them
3140
+ * - `"hidden"` — never rendered
3141
+ */
3142
+ detailsDisplay?: "collapsed" | "expanded" | "hidden";
3143
+ /** Label for the toggle that reveals the technical details */
3144
+ showDetailsLabel?: string;
3145
+ /** Label for the toggle that hides the technical details */
3146
+ hideDetailsLabel?: string;
3147
+ /**
3148
+ * Build the user-facing summary line for an approval request. Overrides the
3149
+ * default "The assistant wants to use “Tool name”." copy. Return a falsy
3150
+ * value to fall back to the default for that approval. `displayTitle` is
3151
+ * the display name the tool declared via the WebMCP spec's
3152
+ * `ToolDescriptor.title`, when one exists.
3153
+ */
3154
+ formatDescription?: (approval: {
3155
+ toolName: string;
3156
+ toolType?: string;
3157
+ description: string;
3158
+ parameters?: unknown;
3159
+ displayTitle?: string;
3160
+ }) => string | undefined;
3057
3161
  /**
3058
3162
  * Custom handler for approval decisions.
3059
3163
  * Return void to let the SDK auto-resolve via the API,
3060
3164
  * or return a Response/ReadableStream for custom handling.
3165
+ *
3166
+ * `options.remember` is `true` when the decision came from a "remember this"
3167
+ * affordance (e.g. an "Always allow" button in a custom `renderApproval`
3168
+ * plugin). Use it to persist a don't-ask-again policy for future approvals;
3169
+ * the current approval resolves the same way regardless.
3061
3170
  */
3062
3171
  onDecision?: (data: {
3063
3172
  approvalId: string;
3064
3173
  executionId: string;
3065
3174
  agentId: string;
3066
3175
  toolName: string;
3067
- }, decision: 'approved' | 'denied') => Promise<Response | ReadableStream<Uint8Array> | void>;
3176
+ }, decision: 'approved' | 'denied', options?: AgentWidgetApprovalDecisionOptions) => Promise<Response | ReadableStream<Uint8Array> | void>;
3068
3177
  };
3069
3178
  type AgentWidgetToolCallConfig = {
3070
- /** Box-shadow for tool-call bubbles; overrides `theme.toolBubbleShadow` when set. */
3179
+ /** Box-shadow for tool-call bubbles; pass `"none"` to remove it. Overrides the `components.toolBubble.shadow` token / `--persona-tool-bubble-shadow`. */
3071
3180
  shadow?: string;
3072
3181
  /** Background color of the tool call bubble container. */
3073
3182
  backgroundColor?: string;
@@ -416,13 +416,59 @@ interface AgentWidgetPlugin {
416
416
  config: AgentWidgetConfig;
417
417
  }) => HTMLElement | null;
418
418
  /**
419
- * Custom renderer for approval bubbles
420
- * Return null to use default renderer
419
+ * Custom renderer for approval bubbles.
420
+ *
421
+ * Return an `HTMLElement` to fully own the approval UI, `defaultRenderer()`
422
+ * to render (or wrap) the built-in bubble, or `null` to fall through to the
423
+ * default. Unlike the built-in bubble — whose Approve/Deny buttons are wired
424
+ * via delegation — a fully custom element resolves the approval by calling
425
+ * the `approve`/`deny` callbacks. Both route through the same path the
426
+ * built-in buttons use (optimistic update, `onDecision`, in-place anchoring).
427
+ *
428
+ * An approval is a single binary gate, so there are exactly two outcomes.
429
+ * Pass `{ remember: true }` to flag a "remember this" affordance (e.g. an
430
+ * "Always allow" button); the current approval resolves identically, but the
431
+ * flag is forwarded to `config.approval.onDecision` so you can persist a
432
+ * don't-ask-again policy for future approvals.
433
+ *
434
+ * `renderApproval` is called again whenever the approval's status changes, so
435
+ * branch on `message.approval?.status` to render the resolved state (and tear
436
+ * down any global listeners you added while pending).
437
+ *
438
+ * @example
439
+ * ```typescript
440
+ * // An alternative prompt: "Always allow" / "Allow once" / "Deny".
441
+ * renderApproval: ({ message, approve, deny }) => {
442
+ * const approval = message.approval;
443
+ * if (!approval || approval.status !== "pending") return null; // default renders resolved state
444
+ * const root = document.createElement("div");
445
+ * root.textContent = `${approval.toolName} requires approval`;
446
+ *
447
+ * const always = document.createElement("button");
448
+ * always.textContent = "Always allow";
449
+ * always.addEventListener("click", () => approve({ remember: true }));
450
+ *
451
+ * const once = document.createElement("button");
452
+ * once.textContent = "Allow once";
453
+ * once.addEventListener("click", () => approve());
454
+ *
455
+ * const no = document.createElement("button");
456
+ * no.textContent = "Deny";
457
+ * no.addEventListener("click", () => deny());
458
+ *
459
+ * root.append(always, once, no);
460
+ * return root;
461
+ * }
462
+ * ```
421
463
  */
422
464
  renderApproval?: (context: {
423
465
  message: AgentWidgetMessage;
424
466
  defaultRenderer: () => HTMLElement;
425
467
  config: AgentWidgetConfig;
468
+ /** Resolve this approval as approved. Pass `{ remember: true }` for an "Always allow" affordance. */
469
+ approve: (options?: AgentWidgetApprovalDecisionOptions) => void;
470
+ /** Resolve this approval as denied. Pass `{ remember: true }` for an "Always deny" affordance. */
471
+ deny: (options?: AgentWidgetApprovalDecisionOptions) => void;
426
472
  }) => HTMLElement | null;
427
473
  /**
428
474
  * Custom renderer for loading indicator
@@ -797,6 +843,8 @@ interface ApprovalTokens {
797
843
  background: TokenReference<'color'>;
798
844
  border: TokenReference<'color'>;
799
845
  text: TokenReference<'color'>;
846
+ /** Box-shadow for the approval bubble (token ref or raw CSS, e.g. `none`). */
847
+ shadow: string;
800
848
  };
801
849
  approve: ComponentTokenSet;
802
850
  deny: ComponentTokenSet;
@@ -1169,6 +1217,7 @@ type RuntypeAgentSSEEvent = {
1169
1217
  iteration: number;
1170
1218
  reflection?: string;
1171
1219
  seq: number;
1220
+ timestamp?: string;
1172
1221
  type: "agent_reflection";
1173
1222
  } | {
1174
1223
  activatedCapabilities: Array<string>;
@@ -1176,6 +1225,7 @@ type RuntypeAgentSSEEvent = {
1176
1225
  iteration: number;
1177
1226
  seq: number;
1178
1227
  skill: string;
1228
+ timestamp?: string;
1179
1229
  toolCallId: string;
1180
1230
  type: "agent_skill_loaded";
1181
1231
  } | ({
@@ -1185,6 +1235,7 @@ type RuntypeAgentSSEEvent = {
1185
1235
  proposalId?: string;
1186
1236
  seq: number;
1187
1237
  skill: string;
1238
+ timestamp?: string;
1188
1239
  toolCallId: string;
1189
1240
  type: "agent_skill_proposed";
1190
1241
  }) | ({
@@ -1218,6 +1269,7 @@ type RuntypeAgentSSEEvent = {
1218
1269
  iteration?: number;
1219
1270
  recoverable: boolean;
1220
1271
  seq: number;
1272
+ timestamp?: string;
1221
1273
  type: "agent_error";
1222
1274
  } | {
1223
1275
  executionId: string;
@@ -1251,6 +1303,7 @@ type RuntypeFlowSSEEvent = {
1251
1303
  failedSteps?: number;
1252
1304
  finalOutput?: string;
1253
1305
  flowId?: string;
1306
+ flowName?: string;
1254
1307
  output?: unknown;
1255
1308
  seq?: number;
1256
1309
  source?: string;
@@ -1302,6 +1355,7 @@ type RuntypeFlowSSEEvent = {
1302
1355
  id?: string;
1303
1356
  index?: number;
1304
1357
  name?: string;
1358
+ outputVariable?: string;
1305
1359
  seq?: number;
1306
1360
  startedAt: string;
1307
1361
  stepId?: string;
@@ -1709,6 +1763,12 @@ type WebMcpConfirmInfo = {
1709
1763
  toolName: string;
1710
1764
  args: unknown;
1711
1765
  description?: string;
1766
+ /**
1767
+ * Display title the tool declared via the WebMCP spec's
1768
+ * `ToolDescriptor.title` (e.g. `"Add to Cart"`). Absent when the tool
1769
+ * didn't declare one.
1770
+ */
1771
+ title?: string;
1712
1772
  annotations?: {
1713
1773
  readOnlyHint?: boolean;
1714
1774
  untrustedContentHint?: boolean;
@@ -3023,6 +3083,22 @@ type TextToSpeechConfig = {
3023
3083
  /** Speech pitch (0 - 2). Default: 1 */
3024
3084
  pitch?: number;
3025
3085
  };
3086
+ /**
3087
+ * Extra context for an approval decision, surfaced to `onDecision` and to the
3088
+ * `approve`/`deny` callbacks passed to the `renderApproval` plugin hook.
3089
+ */
3090
+ type AgentWidgetApprovalDecisionOptions = {
3091
+ /**
3092
+ * The user chose a "remember this" affordance (e.g. an "Always allow"
3093
+ * button) rather than a one-time decision. The widget resolves the *current*
3094
+ * approval identically whether or not this is set — an approval bubble is a
3095
+ * single binary gate (`approved`/`denied`). Persisting a don't-ask-again
3096
+ * policy for *future* approvals (auto-resolving them, or not surfacing them)
3097
+ * is the integrator's responsibility, typically inside `onDecision`.
3098
+ * Defaults to absent/`false`.
3099
+ */
3100
+ remember?: boolean;
3101
+ };
3026
3102
  /**
3027
3103
  * Configuration for tool approval bubbles.
3028
3104
  * Controls styling, labels, and behavior of the approval UI.
@@ -3032,6 +3108,8 @@ type AgentWidgetApprovalConfig = {
3032
3108
  backgroundColor?: string;
3033
3109
  /** Border color of the approval bubble */
3034
3110
  borderColor?: string;
3111
+ /** Box-shadow for the approval bubble; pass `"none"` to remove it. Overrides the default `persona-shadow-sm`. */
3112
+ shadow?: string;
3035
3113
  /** Color for the title text */
3036
3114
  titleColor?: string;
3037
3115
  /** Color for the description text */
@@ -3054,20 +3132,51 @@ type AgentWidgetApprovalConfig = {
3054
3132
  approveLabel?: string;
3055
3133
  /** Label for the deny button */
3056
3134
  denyLabel?: string;
3135
+ /**
3136
+ * How the technical details (the tool's agent-facing description and the
3137
+ * raw parameters JSON) are presented:
3138
+ * - `"collapsed"` (default) — hidden behind a "Show details" toggle
3139
+ * - `"expanded"` — visible, with the toggle available to hide them
3140
+ * - `"hidden"` — never rendered
3141
+ */
3142
+ detailsDisplay?: "collapsed" | "expanded" | "hidden";
3143
+ /** Label for the toggle that reveals the technical details */
3144
+ showDetailsLabel?: string;
3145
+ /** Label for the toggle that hides the technical details */
3146
+ hideDetailsLabel?: string;
3147
+ /**
3148
+ * Build the user-facing summary line for an approval request. Overrides the
3149
+ * default "The assistant wants to use “Tool name”." copy. Return a falsy
3150
+ * value to fall back to the default for that approval. `displayTitle` is
3151
+ * the display name the tool declared via the WebMCP spec's
3152
+ * `ToolDescriptor.title`, when one exists.
3153
+ */
3154
+ formatDescription?: (approval: {
3155
+ toolName: string;
3156
+ toolType?: string;
3157
+ description: string;
3158
+ parameters?: unknown;
3159
+ displayTitle?: string;
3160
+ }) => string | undefined;
3057
3161
  /**
3058
3162
  * Custom handler for approval decisions.
3059
3163
  * Return void to let the SDK auto-resolve via the API,
3060
3164
  * or return a Response/ReadableStream for custom handling.
3165
+ *
3166
+ * `options.remember` is `true` when the decision came from a "remember this"
3167
+ * affordance (e.g. an "Always allow" button in a custom `renderApproval`
3168
+ * plugin). Use it to persist a don't-ask-again policy for future approvals;
3169
+ * the current approval resolves the same way regardless.
3061
3170
  */
3062
3171
  onDecision?: (data: {
3063
3172
  approvalId: string;
3064
3173
  executionId: string;
3065
3174
  agentId: string;
3066
3175
  toolName: string;
3067
- }, decision: 'approved' | 'denied') => Promise<Response | ReadableStream<Uint8Array> | void>;
3176
+ }, decision: 'approved' | 'denied', options?: AgentWidgetApprovalDecisionOptions) => Promise<Response | ReadableStream<Uint8Array> | void>;
3068
3177
  };
3069
3178
  type AgentWidgetToolCallConfig = {
3070
- /** Box-shadow for tool-call bubbles; overrides `theme.toolBubbleShadow` when set. */
3179
+ /** Box-shadow for tool-call bubbles; pass `"none"` to remove it. Overrides the `components.toolBubble.shadow` token / `--persona-tool-bubble-shadow`. */
3071
3180
  shadow?: string;
3072
3181
  /** Background color of the tool call bubble container. */
3073
3182
  backgroundColor?: string;