@runtypelabs/persona 3.29.1 → 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.
- package/README.md +55 -0
- package/dist/codegen.cjs +6 -6
- package/dist/codegen.js +4 -4
- package/dist/index.cjs +47 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +112 -7
- package/dist/index.d.ts +112 -7
- package/dist/index.global.js +59 -59
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +47 -47
- package/dist/index.js.map +1 -1
- package/dist/launcher.global.js +2 -2
- package/dist/launcher.global.js.map +1 -1
- package/dist/plugin-kit.cjs +1 -0
- package/dist/plugin-kit.d.cts +98 -0
- package/dist/plugin-kit.d.ts +98 -0
- package/dist/plugin-kit.js +1 -0
- package/dist/smart-dom-reader.d.cts +107 -4
- package/dist/smart-dom-reader.d.ts +107 -4
- package/dist/theme-editor.cjs +38 -38
- package/dist/theme-editor.d.cts +111 -6
- package/dist/theme-editor.d.ts +111 -6
- package/dist/theme-editor.js +38 -38
- package/dist/theme-reference.cjs +1 -1
- package/dist/theme-reference.d.cts +2 -0
- package/dist/theme-reference.d.ts +2 -0
- package/dist/theme-reference.js +1 -1
- package/package.json +8 -2
- package/src/components/approval-bubble.test.ts +268 -0
- package/src/components/approval-bubble.ts +170 -20
- package/src/components/launcher.ts +6 -3
- package/src/components/tool-bubble.test.ts +39 -0
- package/src/components/tool-bubble.ts +4 -0
- package/src/index-core.ts +1 -0
- package/src/plugin-kit.test.ts +230 -0
- package/src/plugin-kit.ts +294 -0
- package/src/plugins/types.ts +49 -2
- package/src/session.test.ts +99 -0
- package/src/session.ts +14 -3
- package/src/theme-editor/preview-utils.test.ts +10 -0
- package/src/theme-editor/preview-utils.ts +29 -1
- package/src/theme-editor/sections.test.ts +17 -0
- package/src/theme-editor/sections.ts +31 -0
- package/src/theme-reference.ts +1 -1
- package/src/types/theme.ts +2 -0
- package/src/types.ts +59 -2
- package/src/ui.approval-plugin.test.ts +204 -0
- package/src/ui.ts +149 -16
- package/src/utils/theme.test.ts +6 -2
- package/src/utils/theme.ts +0 -8
- package/src/utils/tokens.ts +6 -1
- package/src/webmcp-bridge.test.ts +66 -0
- 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
|
-
*
|
|
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;
|
|
@@ -1715,6 +1763,12 @@ type WebMcpConfirmInfo = {
|
|
|
1715
1763
|
toolName: string;
|
|
1716
1764
|
args: unknown;
|
|
1717
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;
|
|
1718
1772
|
annotations?: {
|
|
1719
1773
|
readOnlyHint?: boolean;
|
|
1720
1774
|
untrustedContentHint?: boolean;
|
|
@@ -3029,6 +3083,22 @@ type TextToSpeechConfig = {
|
|
|
3029
3083
|
/** Speech pitch (0 - 2). Default: 1 */
|
|
3030
3084
|
pitch?: number;
|
|
3031
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
|
+
};
|
|
3032
3102
|
/**
|
|
3033
3103
|
* Configuration for tool approval bubbles.
|
|
3034
3104
|
* Controls styling, labels, and behavior of the approval UI.
|
|
@@ -3038,6 +3108,8 @@ type AgentWidgetApprovalConfig = {
|
|
|
3038
3108
|
backgroundColor?: string;
|
|
3039
3109
|
/** Border color of the approval bubble */
|
|
3040
3110
|
borderColor?: string;
|
|
3111
|
+
/** Box-shadow for the approval bubble; pass `"none"` to remove it. Overrides the default `persona-shadow-sm`. */
|
|
3112
|
+
shadow?: string;
|
|
3041
3113
|
/** Color for the title text */
|
|
3042
3114
|
titleColor?: string;
|
|
3043
3115
|
/** Color for the description text */
|
|
@@ -3060,20 +3132,51 @@ type AgentWidgetApprovalConfig = {
|
|
|
3060
3132
|
approveLabel?: string;
|
|
3061
3133
|
/** Label for the deny button */
|
|
3062
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;
|
|
3063
3161
|
/**
|
|
3064
3162
|
* Custom handler for approval decisions.
|
|
3065
3163
|
* Return void to let the SDK auto-resolve via the API,
|
|
3066
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.
|
|
3067
3170
|
*/
|
|
3068
3171
|
onDecision?: (data: {
|
|
3069
3172
|
approvalId: string;
|
|
3070
3173
|
executionId: string;
|
|
3071
3174
|
agentId: string;
|
|
3072
3175
|
toolName: string;
|
|
3073
|
-
}, decision: 'approved' | 'denied') => Promise<Response | ReadableStream<Uint8Array> | void>;
|
|
3176
|
+
}, decision: 'approved' | 'denied', options?: AgentWidgetApprovalDecisionOptions) => Promise<Response | ReadableStream<Uint8Array> | void>;
|
|
3074
3177
|
};
|
|
3075
3178
|
type AgentWidgetToolCallConfig = {
|
|
3076
|
-
/** Box-shadow for tool-call bubbles;
|
|
3179
|
+
/** Box-shadow for tool-call bubbles; pass `"none"` to remove it. Overrides the `components.toolBubble.shadow` token / `--persona-tool-bubble-shadow`. */
|
|
3077
3180
|
shadow?: string;
|
|
3078
3181
|
/** Background color of the tool call bubble container. */
|
|
3079
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
|
-
*
|
|
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;
|
|
@@ -1715,6 +1763,12 @@ type WebMcpConfirmInfo = {
|
|
|
1715
1763
|
toolName: string;
|
|
1716
1764
|
args: unknown;
|
|
1717
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;
|
|
1718
1772
|
annotations?: {
|
|
1719
1773
|
readOnlyHint?: boolean;
|
|
1720
1774
|
untrustedContentHint?: boolean;
|
|
@@ -3029,6 +3083,22 @@ type TextToSpeechConfig = {
|
|
|
3029
3083
|
/** Speech pitch (0 - 2). Default: 1 */
|
|
3030
3084
|
pitch?: number;
|
|
3031
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
|
+
};
|
|
3032
3102
|
/**
|
|
3033
3103
|
* Configuration for tool approval bubbles.
|
|
3034
3104
|
* Controls styling, labels, and behavior of the approval UI.
|
|
@@ -3038,6 +3108,8 @@ type AgentWidgetApprovalConfig = {
|
|
|
3038
3108
|
backgroundColor?: string;
|
|
3039
3109
|
/** Border color of the approval bubble */
|
|
3040
3110
|
borderColor?: string;
|
|
3111
|
+
/** Box-shadow for the approval bubble; pass `"none"` to remove it. Overrides the default `persona-shadow-sm`. */
|
|
3112
|
+
shadow?: string;
|
|
3041
3113
|
/** Color for the title text */
|
|
3042
3114
|
titleColor?: string;
|
|
3043
3115
|
/** Color for the description text */
|
|
@@ -3060,20 +3132,51 @@ type AgentWidgetApprovalConfig = {
|
|
|
3060
3132
|
approveLabel?: string;
|
|
3061
3133
|
/** Label for the deny button */
|
|
3062
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;
|
|
3063
3161
|
/**
|
|
3064
3162
|
* Custom handler for approval decisions.
|
|
3065
3163
|
* Return void to let the SDK auto-resolve via the API,
|
|
3066
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.
|
|
3067
3170
|
*/
|
|
3068
3171
|
onDecision?: (data: {
|
|
3069
3172
|
approvalId: string;
|
|
3070
3173
|
executionId: string;
|
|
3071
3174
|
agentId: string;
|
|
3072
3175
|
toolName: string;
|
|
3073
|
-
}, decision: 'approved' | 'denied') => Promise<Response | ReadableStream<Uint8Array> | void>;
|
|
3176
|
+
}, decision: 'approved' | 'denied', options?: AgentWidgetApprovalDecisionOptions) => Promise<Response | ReadableStream<Uint8Array> | void>;
|
|
3074
3177
|
};
|
|
3075
3178
|
type AgentWidgetToolCallConfig = {
|
|
3076
|
-
/** Box-shadow for tool-call bubbles;
|
|
3179
|
+
/** Box-shadow for tool-call bubbles; pass `"none"` to remove it. Overrides the `components.toolBubble.shadow` token / `--persona-tool-bubble-shadow`. */
|
|
3077
3180
|
shadow?: string;
|
|
3078
3181
|
/** Background color of the tool call bubble container. */
|
|
3079
3182
|
backgroundColor?: string;
|