@runtypelabs/persona 4.32.2 → 4.33.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/dist/activity-ui.d.cts +3 -3
- package/dist/activity-ui.d.ts +3 -3
- package/dist/animations/glyph-cycle.d.cts +1 -1
- package/dist/animations/glyph-cycle.d.ts +1 -1
- package/dist/animations/wipe.d.cts +1 -1
- package/dist/animations/wipe.d.ts +1 -1
- package/dist/approval-ui.d.cts +2 -2
- package/dist/approval-ui.d.ts +2 -2
- package/dist/artifacts-ui.d.cts +3 -3
- package/dist/artifacts-ui.d.ts +3 -3
- package/dist/background-runtime.d.cts +1 -1
- package/dist/background-runtime.d.ts +1 -1
- package/dist/{chunk-BbXc58OA.js → chunk-6CUrH_jm.js} +1 -1
- package/dist/{chunk-B_H0l3u2.js → chunk-BRM3bHcJ.js} +1 -1
- package/dist/chunk-CEPM3FOp.js +1 -0
- package/dist/{chunk-D0tcIZJM.js → chunk-CXQJG7pb.js} +1 -1
- package/dist/{chunk-UWMfWW2a.js → chunk-D1G8tgYc.js} +1 -1
- package/dist/{chunk-BpjtKnGI.js → chunk-D6U0E82p.js} +1 -1
- package/dist/{chunk-2FnmZ9AA.js → chunk-DOZmcve8.js} +1 -1
- package/dist/chunk-DVgzoVCB.js +1 -0
- package/dist/{chunk-B9Zu-wrk.js → chunk-DW7Vg_l2.js} +1 -1
- package/dist/{chunk-B3l7TOOv.js → chunk-DbGLjU2P.js} +1 -1
- package/dist/{chunk-DTGvDx8o.js → chunk-Dj2a9f9w.js} +1 -1
- package/dist/{chunk-DgZZqy14.js → chunk-d54BJdKd.js} +1 -1
- package/dist/chunk-dLJWKfOb.js +1 -0
- package/dist/{chunk-DNdeIFJ6.js → chunk-gu34lr-P.js} +1 -1
- package/dist/chunk-i4qGOJAL.js +1 -0
- package/dist/{chunk-DVZ8ri1z.js → chunk-ql5N2I_t.js} +1 -1
- package/dist/client-dispatch.js +1 -1
- package/dist/client-history.js +1 -1
- package/dist/codegen.cjs +1 -1
- package/dist/codegen.js +1 -1
- package/dist/composer-actions-runtime.d.cts +2 -2
- package/dist/composer-actions-runtime.d.ts +2 -2
- package/dist/{composer-input-dwg_o9Z-.d.cts → composer-input-DnqGCXZz.d.cts} +1 -1
- package/dist/{composer-input-dwg_o9Z-.d.ts → composer-input-DnqGCXZz.d.ts} +1 -1
- package/dist/{composer-store-Dw7Zbqxr.d.cts → composer-store-6Iwy6WLI.d.cts} +1 -1
- package/dist/{composer-store-Dw7Zbqxr.d.ts → composer-store-6Iwy6WLI.d.ts} +1 -1
- package/dist/{context-mention-manager-D2p0-JH-.d.cts → context-mention-manager-B-rHF98n.d.cts} +2 -2
- package/dist/{context-mention-manager-D2p0-JH-.d.ts → context-mention-manager-B-rHF98n.d.ts} +2 -2
- package/dist/context-mentions-inline.d.cts +2 -2
- package/dist/context-mentions-inline.d.ts +2 -2
- package/dist/context-mentions.d.cts +3 -3
- package/dist/context-mentions.d.ts +3 -3
- package/dist/{defaults-CjX0kKzb.d.ts → defaults-BuOjqUAG.d.cts} +2 -1
- package/dist/{defaults-CjX0kKzb.d.cts → defaults-BuOjqUAG.d.ts} +2 -1
- package/dist/dictation-runtime.d.cts +2 -2
- package/dist/dictation-runtime.d.ts +2 -2
- package/dist/event-stream-view.d.cts +1 -1
- package/dist/event-stream-view.d.ts +1 -1
- package/dist/{fallback-speech-engine-DmF7MczI.d.cts → fallback-speech-engine-SqEfkGda.d.cts} +1 -1
- package/dist/{fallback-speech-engine-DmF7MczI.d.ts → fallback-speech-engine-SqEfkGda.d.ts} +1 -1
- package/dist/{feature-preferences-CVFDt9Xf.d.cts → feature-preferences-DqAmje-2.d.cts} +1 -1
- package/dist/{feature-preferences-CVFDt9Xf.d.ts → feature-preferences-DqAmje-2.d.ts} +1 -1
- package/dist/forms-ui.d.cts +2 -2
- package/dist/forms-ui.d.ts +2 -2
- package/dist/{history-confirm-dialog-DJEXsTyE.d.cts → history-confirm-dialog-B3L7QNcO.d.cts} +1 -1
- package/dist/{history-confirm-dialog-DJEXsTyE.d.ts → history-confirm-dialog-B3L7QNcO.d.ts} +1 -1
- package/dist/history-shell.cjs +1 -1
- package/dist/history-shell.d.cts +6 -6
- package/dist/history-shell.d.ts +6 -6
- package/dist/history-shell.js +1 -1
- package/dist/history-view.d.cts +1 -1
- package/dist/history-view.d.ts +1 -1
- package/dist/index.cjs +38 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -9
- package/dist/index.d.ts +9 -9
- package/dist/index.global.js +1 -1
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +52 -52
- package/dist/index.js.map +1 -1
- package/dist/install.global.js +1 -1
- package/dist/launcher.global.js +2 -2
- package/dist/launcher.global.js.map +1 -1
- package/dist/{loading-label-runtime-loader-CPxnoaoi.d.cts → loading-label-runtime-loader-JecjOfJS.d.cts} +1 -1
- package/dist/{loading-label-runtime-loader-CPxnoaoi.d.ts → loading-label-runtime-loader-JecjOfJS.d.ts} +1 -1
- package/dist/loading-label-runtime.d.cts +1 -1
- package/dist/loading-label-runtime.d.ts +1 -1
- package/dist/{message-bubble-CkZGlCLC.d.ts → message-bubble-CBYtpDsT.d.cts} +2 -2
- package/dist/{message-bubble-CkZGlCLC.d.cts → message-bubble-CBYtpDsT.d.ts} +2 -2
- package/dist/preferences-runtime.d.cts +1 -1
- package/dist/preferences-runtime.d.ts +1 -1
- package/dist/{preview-DdUvoLb4.d.cts → preview-_UJwwt2L.d.cts} +2 -2
- package/dist/{preview-DdUvoLb4.d.ts → preview-_UJwwt2L.d.ts} +2 -2
- package/dist/{registry-Dz4iHNST.d.cts → registry-B5ZV3K-h.d.cts} +1 -1
- package/dist/{registry-Dz4iHNST.d.ts → registry-B5ZV3K-h.d.ts} +1 -1
- package/dist/{session-Baxp9iRK.d.cts → session-Bys8_XuB.d.cts} +10 -9
- package/dist/{session-Baxp9iRK.d.ts → session-Bys8_XuB.d.ts} +10 -9
- package/dist/session-reconnect.d.cts +2 -2
- package/dist/session-reconnect.d.ts +2 -2
- package/dist/smart-dom-reader.d.cts +1 -1
- package/dist/smart-dom-reader.d.ts +1 -1
- package/dist/theme-editor-preview.cjs +31 -31
- package/dist/theme-editor-preview.d.cts +1 -1
- package/dist/theme-editor-preview.d.ts +1 -1
- package/dist/theme-editor-preview.js +19 -19
- package/dist/theme-editor.cjs +1 -1
- package/dist/theme-editor.d.cts +2 -2
- package/dist/theme-editor.d.ts +2 -2
- package/dist/theme-editor.js +1 -1
- package/dist/theme-reference.cjs +1 -1
- package/dist/theme-reference.d.cts +4 -2
- package/dist/theme-reference.d.ts +4 -2
- package/dist/theme-reference.js +1 -1
- package/dist/{tool-loading-animation-un3kA5Ur.d.cts → tool-loading-animation-d5qAQcwv.d.cts} +1 -1
- package/dist/{tool-loading-animation-un3kA5Ur.d.ts → tool-loading-animation-d5qAQcwv.d.ts} +1 -1
- package/dist/{turn-activity-ui-BGNhtMcH.d.cts → turn-activity-ui-q6fHVnx-.d.cts} +2 -2
- package/dist/{turn-activity-ui-BGNhtMcH.d.ts → turn-activity-ui-q6fHVnx-.d.ts} +2 -2
- package/dist/turn-activity-ui.d.cts +1 -1
- package/dist/turn-activity-ui.d.ts +1 -1
- package/dist/{types-BOZHQIk0.d.cts → types-B_mj21jg.d.cts} +91 -7
- package/dist/{types-BOZHQIk0.d.ts → types-B_mj21jg.d.ts} +91 -7
- package/dist/voice-client-tools.d.cts +1 -1
- package/dist/voice-client-tools.d.ts +1 -1
- package/dist/voice-runtime.cjs +1 -1
- package/dist/voice-runtime.d.cts +2 -2
- package/dist/voice-runtime.d.ts +2 -2
- package/dist/voice-runtime.js +1 -1
- package/dist/voice-worklet-player.d.cts +2 -2
- package/dist/voice-worklet-player.d.ts +2 -2
- package/dist/{webmcp-runtime-entry-DuAG1_yF.d.cts → webmcp-runtime-entry-ClEO3qdI.d.cts} +1 -1
- package/dist/{webmcp-runtime-entry-DuAG1_yF.d.ts → webmcp-runtime-entry-ClEO3qdI.d.ts} +1 -1
- package/dist/webmcp-runtime.d.cts +1 -1
- package/dist/webmcp-runtime.d.ts +1 -1
- package/dist/widget.css +1 -1
- package/package.json +1 -1
- package/src/client-dispatch.ts +69 -16
- package/src/client.operator-role.test.ts +217 -0
- package/src/components/message-bubble.test.ts +169 -0
- package/src/components/message-bubble.ts +97 -28
- package/src/defaults-parity.test.ts +14 -0
- package/src/index-core.ts +7 -0
- package/src/session.resubmit-from.test.ts +27 -0
- package/src/session.test.ts +10 -0
- package/src/session.ts +8 -5
- package/src/styles/widget.css +23 -1
- package/src/theme-reference.ts +3 -1
- package/src/types/theme.ts +21 -0
- package/src/types.ts +71 -6
- package/src/ui.ask-user-question-plugin.test.ts +154 -0
- package/src/ui.operator-role.test.ts +336 -0
- package/src/ui.ts +37 -12
- package/src/utils/__snapshots__/tokens-parity.test.ts.snap +236 -0
- package/src/utils/brand.test.ts +14 -0
- package/src/utils/brand.ts +8 -4
- package/src/utils/history-messages.test.ts +30 -0
- package/src/utils/history-messages.ts +26 -8
- package/src/utils/message-fingerprint.ts +4 -0
- package/src/utils/resubmit-turn.test.ts +13 -0
- package/src/utils/resubmit-turn.ts +12 -1
- package/src/utils/tokens.ts +31 -0
- package/src/welcome.test.ts +16 -0
- package/src/welcome.ts +10 -2
- package/dist/chunk-BNE40cEG.js +0 -1
- package/dist/chunk-Bz9jAgba.js +0 -1
- package/dist/chunk-CdpVP6LZ.js +0 -1
- package/dist/chunk-CjkechxR.js +0 -1
|
@@ -2,6 +2,7 @@ import { getLoadingLabelRuntimeSync } from "../loading-label-runtime-loader";
|
|
|
2
2
|
import { createElement, createNode } from "../utils/dom";
|
|
3
3
|
import {
|
|
4
4
|
AgentWidgetMessage,
|
|
5
|
+
AgentWidgetMessageRole,
|
|
5
6
|
AgentWidgetMessageLayoutConfig,
|
|
6
7
|
AgentWidgetAvatarConfig,
|
|
7
8
|
AgentWidgetTimestampConfig,
|
|
@@ -577,40 +578,47 @@ export const renderLoadingIndicatorWithFallback = (
|
|
|
577
578
|
*/
|
|
578
579
|
const createAvatar = (
|
|
579
580
|
avatarConfig: AgentWidgetAvatarConfig,
|
|
580
|
-
role:
|
|
581
|
+
role: AgentWidgetMessageRole
|
|
581
582
|
): HTMLElement => {
|
|
582
583
|
const avatar = createElement(
|
|
583
584
|
"div",
|
|
584
585
|
"persona-flex-shrink-0 persona-w-8 persona-h-8 persona-rounded-full persona-flex persona-items-center persona-justify-center persona-text-sm"
|
|
585
586
|
);
|
|
586
587
|
|
|
587
|
-
const
|
|
588
|
-
|
|
589
|
-
|
|
588
|
+
const isOperator = role === "operator";
|
|
589
|
+
const avatarContent = role === "user"
|
|
590
|
+
? avatarConfig.userAvatar
|
|
591
|
+
: isOperator
|
|
592
|
+
? avatarConfig.operatorAvatar
|
|
593
|
+
: avatarConfig.assistantAvatar;
|
|
594
|
+
|
|
595
|
+
// The operator avatar is a neutral outlined chip, apart from the solid
|
|
596
|
+
// user and assistant fills.
|
|
597
|
+
const applyFill = () => {
|
|
598
|
+
avatar.classList.add(
|
|
599
|
+
...(isOperator
|
|
600
|
+
? ["persona-bg-persona-container", "persona-text-persona-text", "persona-border"]
|
|
601
|
+
: [role === "user" ? "persona-bg-persona-accent" : "persona-bg-persona-primary", "persona-text-white"])
|
|
602
|
+
);
|
|
603
|
+
};
|
|
590
604
|
|
|
591
605
|
if (avatarContent) {
|
|
592
606
|
// Check if it's a URL or emoji/text
|
|
593
607
|
if (avatarContent.startsWith("http") || avatarContent.startsWith("/") || avatarContent.startsWith("data:")) {
|
|
594
608
|
const img = createElement("img") as HTMLImageElement;
|
|
595
609
|
img.src = avatarContent;
|
|
596
|
-
img.alt = role === "user" ? "User" : "Assistant";
|
|
610
|
+
img.alt = role === "user" ? "User" : isOperator ? "Operator" : "Assistant";
|
|
597
611
|
img.className = "persona-w-full persona-h-full persona-rounded-full persona-object-cover";
|
|
598
612
|
avatar.appendChild(img);
|
|
599
613
|
} else {
|
|
600
614
|
// Emoji or text
|
|
601
615
|
avatar.textContent = avatarContent;
|
|
602
|
-
|
|
603
|
-
role === "user" ? "persona-bg-persona-accent" : "persona-bg-persona-primary",
|
|
604
|
-
"persona-text-white"
|
|
605
|
-
);
|
|
616
|
+
applyFill();
|
|
606
617
|
}
|
|
607
618
|
} else {
|
|
608
619
|
// Default avatar
|
|
609
|
-
avatar.textContent = role === "user" ? "U" : "A";
|
|
610
|
-
|
|
611
|
-
role === "user" ? "persona-bg-persona-accent" : "persona-bg-persona-primary",
|
|
612
|
-
"persona-text-white"
|
|
613
|
-
);
|
|
620
|
+
avatar.textContent = role === "user" ? "U" : isOperator ? "O" : "A";
|
|
621
|
+
applyFill();
|
|
614
622
|
}
|
|
615
623
|
|
|
616
624
|
return avatar;
|
|
@@ -644,13 +652,32 @@ const createTimestamp = (
|
|
|
644
652
|
return timestamp;
|
|
645
653
|
};
|
|
646
654
|
|
|
655
|
+
/** Assistant color utilities an operator bubble replaces with its own tokens. */
|
|
656
|
+
const OPERATOR_DROPPED_CLASSES = new Set([
|
|
657
|
+
"persona-bg-persona-container",
|
|
658
|
+
"persona-text-persona-text",
|
|
659
|
+
"persona-border-persona-message-border",
|
|
660
|
+
]);
|
|
661
|
+
|
|
647
662
|
/**
|
|
648
663
|
* Get bubble classes based on layout preset
|
|
649
664
|
*/
|
|
650
665
|
export const getBubbleClasses = (
|
|
651
|
-
role:
|
|
666
|
+
role: AgentWidgetMessageRole,
|
|
652
667
|
layout: AgentWidgetMessageLayoutConfig["layout"] = "bubble"
|
|
653
668
|
): string[] => {
|
|
669
|
+
// Operator bubbles share the assistant's geometry per layout; only the role
|
|
670
|
+
// class changes, and the colors come from the operator tokens instead.
|
|
671
|
+
if (role === "operator") {
|
|
672
|
+
const classes = getBubbleClasses("assistant", layout).flatMap((name) =>
|
|
673
|
+
name === "persona-message-assistant-bubble"
|
|
674
|
+
? ["persona-message-operator-bubble"]
|
|
675
|
+
: OPERATOR_DROPPED_CLASSES.has(name) ? [] : [name]
|
|
676
|
+
);
|
|
677
|
+
// Flat drops the assistant's fill, not the operator's: the operator keeps
|
|
678
|
+
// its background, so it needs the minimal layout's side padding.
|
|
679
|
+
return layout === "flat" ? [...classes, "persona-px-3"] : classes;
|
|
680
|
+
}
|
|
654
681
|
const baseClasses = ["persona-message-bubble"];
|
|
655
682
|
|
|
656
683
|
switch (layout) {
|
|
@@ -763,7 +790,7 @@ export const applyBubbleGeometry = (
|
|
|
763
790
|
tokens?: MessageGeometryTokens
|
|
764
791
|
): void => {
|
|
765
792
|
if (!tokens) return;
|
|
766
|
-
const key = role === "user"
|
|
793
|
+
const key = role === "user" || role === "operator" ? role : "assistant";
|
|
767
794
|
const preset = BUBBLE_GEOMETRY_DEFAULTS[layout ?? "bubble"] ?? BUBBLE_GEOMETRY_DEFAULTS.bubble;
|
|
768
795
|
if (tokens.padding) {
|
|
769
796
|
bubble.style.padding = `var(--persona-message-${key}-padding, ${preset.padding})`;
|
|
@@ -787,13 +814,14 @@ export const customMessageActionDataAction = (id: string): string =>
|
|
|
787
814
|
|
|
788
815
|
/**
|
|
789
816
|
* Host-contributed actions this message's role qualifies for. Assistant-only by
|
|
790
|
-
* default, matching every built-in except edit and quote
|
|
817
|
+
* default, matching every built-in except edit and quote; an action reaches
|
|
818
|
+
* operator messages only when its `roles` lists `"operator"`.
|
|
791
819
|
*/
|
|
792
820
|
export const customMessageActionsForRole = (
|
|
793
821
|
actionsConfig: AgentWidgetMessageActionsConfig | undefined,
|
|
794
822
|
role: AgentWidgetMessage["role"]
|
|
795
823
|
): AgentWidgetCustomMessageAction[] => {
|
|
796
|
-
if (role !== "user" && role !== "assistant") return [];
|
|
824
|
+
if (role !== "user" && role !== "assistant" && role !== "operator") return [];
|
|
797
825
|
return (actionsConfig?.custom ?? []).filter((action) =>
|
|
798
826
|
(action.roles ?? ["assistant"]).includes(role)
|
|
799
827
|
);
|
|
@@ -1000,21 +1028,33 @@ export const createStandardBubble = (
|
|
|
1000
1028
|
bubble.id = `bubble-${message.id}`;
|
|
1001
1029
|
bubble.setAttribute("data-message-id", message.id);
|
|
1002
1030
|
|
|
1003
|
-
|
|
1031
|
+
const isOperator = message.role === "operator";
|
|
1032
|
+
// The V5 operator is outlined, not filled, so every layout needs the border
|
|
1033
|
+
// style that only the bubble layout's classes carry.
|
|
1034
|
+
if (isOperator && options?.widgetConfig?.future?.v5Defaults === true) bubble.classList.add("persona-border");
|
|
1035
|
+
bubble.setAttribute(
|
|
1036
|
+
"data-persona-theme-zone",
|
|
1037
|
+
message.role === "user" ? "user-message" : isOperator ? "operator-message" : "assistant-message"
|
|
1038
|
+
);
|
|
1004
1039
|
|
|
1005
1040
|
const defaultMessageTokens = resolveThemeDefaults(options?.widgetConfig?.future).components.message;
|
|
1006
1041
|
const messageTokens = options?.widgetConfig?.theme?.components?.message;
|
|
1042
|
+
const darkMessageTokens = options?.widgetConfig?.darkTheme?.components?.message;
|
|
1007
1043
|
applyBubbleGeometry(
|
|
1008
1044
|
bubble,
|
|
1009
1045
|
message.role,
|
|
1010
1046
|
layout,
|
|
1011
1047
|
message.role === "user"
|
|
1012
|
-
? { ...defaultMessageTokens.user, ...messageTokens?.user, ...
|
|
1013
|
-
:
|
|
1048
|
+
? { ...defaultMessageTokens.user, ...messageTokens?.user, ...darkMessageTokens?.user }
|
|
1049
|
+
: isOperator
|
|
1050
|
+
? { ...defaultMessageTokens.operator, ...messageTokens?.operator, ...darkMessageTokens?.operator }
|
|
1051
|
+
: { ...defaultMessageTokens.assistant, ...messageTokens?.assistant, ...darkMessageTokens?.assistant }
|
|
1014
1052
|
);
|
|
1015
1053
|
|
|
1016
|
-
// Apply component-level color overrides via CSS variables
|
|
1017
|
-
|
|
1054
|
+
// Apply component-level color overrides via CSS variables. Flat clears the
|
|
1055
|
+
// user and assistant fills; an operator keeps its own background token so it
|
|
1056
|
+
// stays apart from the assistant prose on the same side.
|
|
1057
|
+
if (layout === "flat" && !isOperator) {
|
|
1018
1058
|
bubble.style.backgroundColor = "transparent";
|
|
1019
1059
|
bubble.style.color =
|
|
1020
1060
|
message.role === "user"
|
|
@@ -1026,6 +1066,23 @@ export const createStandardBubble = (
|
|
|
1026
1066
|
} else if (message.role === "assistant") {
|
|
1027
1067
|
bubble.style.backgroundColor = 'var(--persona-message-assistant-bg, var(--persona-container))';
|
|
1028
1068
|
bubble.style.color = 'var(--persona-message-assistant-text, var(--persona-text))';
|
|
1069
|
+
} else if (isOperator) {
|
|
1070
|
+
bubble.style.backgroundColor = 'var(--persona-components-message-operator-background, var(--persona-container))';
|
|
1071
|
+
bubble.style.color = 'var(--persona-components-message-operator-text, var(--persona-text))';
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
if (isOperator) {
|
|
1075
|
+
const authorLabel = createElement("div", "persona-message-author persona-text-xs persona-font-medium persona-mb-1");
|
|
1076
|
+
const configuredLabel = options?.widgetConfig?.copy?.operatorLabel;
|
|
1077
|
+
const operatorLabel = typeof configuredLabel === "string" && configuredLabel.trim()
|
|
1078
|
+
? configuredLabel.trim() : "Human operator";
|
|
1079
|
+
const displayName = typeof message.author?.displayName === "string"
|
|
1080
|
+
? message.author.displayName.trim() : "";
|
|
1081
|
+
authorLabel.textContent = displayName ? `${displayName} · ${operatorLabel}` : operatorLabel;
|
|
1082
|
+
// A visible name is also the accessible name for the human-authored group.
|
|
1083
|
+
bubble.setAttribute("role", "group");
|
|
1084
|
+
bubble.setAttribute("aria-label", authorLabel.textContent);
|
|
1085
|
+
bubble.appendChild(authorLabel);
|
|
1029
1086
|
}
|
|
1030
1087
|
|
|
1031
1088
|
const imageParts = getMessageImageParts(message);
|
|
@@ -1293,14 +1350,15 @@ export const createStandardBubble = (
|
|
|
1293
1350
|
bubble.appendChild(createStopReasonNotice(message.stopReason, stopReasonNoticeText));
|
|
1294
1351
|
}
|
|
1295
1352
|
|
|
1296
|
-
// Add message actions for assistant messages (only when not
|
|
1353
|
+
// Add message actions for assistant and operator messages (only when not
|
|
1354
|
+
// streaming and has content). Operator rows get copy and opted-in custom
|
|
1355
|
+
// actions only: feedback, read-aloud and regenerate are about model output.
|
|
1297
1356
|
const shouldShowActions =
|
|
1298
|
-
message.role === "assistant" &&
|
|
1357
|
+
(message.role === "assistant" || isOperator) &&
|
|
1299
1358
|
!message.streaming &&
|
|
1300
1359
|
// A voice caption (the voice model's own speech) is display-only.
|
|
1301
1360
|
!message.voiceCaption &&
|
|
1302
|
-
|
|
1303
|
-
message.content.trim() &&
|
|
1361
|
+
(messageContentText.length > 0 || (isOperator && customMessageActionsForRole(actionsConfig, "operator").length > 0)) &&
|
|
1304
1362
|
actionsConfig?.enabled !== false;
|
|
1305
1363
|
|
|
1306
1364
|
// User bubbles carry actions only when this message is edit- or quote-eligible;
|
|
@@ -1321,7 +1379,18 @@ export const createStandardBubble = (
|
|
|
1321
1379
|
shouldShowUserActions
|
|
1322
1380
|
? // A user bubble never hosts the assistant-only controls.
|
|
1323
1381
|
{ ...actionsConfig, showCopy: false, showUpvote: false, showDownvote: false, showReadAloud: false }
|
|
1324
|
-
:
|
|
1382
|
+
: isOperator
|
|
1383
|
+
? {
|
|
1384
|
+
...actionsConfig,
|
|
1385
|
+
showCopy: messageContentText.length > 0 && actionsConfig.showCopy !== false,
|
|
1386
|
+
showUpvote: false,
|
|
1387
|
+
showDownvote: false,
|
|
1388
|
+
showReadAloud: false,
|
|
1389
|
+
showRegenerate: false,
|
|
1390
|
+
showEdit: false,
|
|
1391
|
+
showQuote: false,
|
|
1392
|
+
}
|
|
1393
|
+
: actionsConfig,
|
|
1325
1394
|
actionCallbacks,
|
|
1326
1395
|
eligibility,
|
|
1327
1396
|
options?.widgetConfig?.future?.v5Defaults === true && actionsConfig.visibility === undefined
|
|
@@ -160,6 +160,20 @@ describe("4.22.0 defaults parity (flag off)", () => {
|
|
|
160
160
|
expect(actual.themeCssVariables[key], key).toBe(value);
|
|
161
161
|
delete resolvedActual.themeCssVariables[key];
|
|
162
162
|
}
|
|
163
|
+
// Operator bubbles only render for messages that use the operator role,
|
|
164
|
+
// so every token they emit is additive and none may exist in the baseline.
|
|
165
|
+
const additivePrefixes = [
|
|
166
|
+
"--persona-components-message-operator-",
|
|
167
|
+
"--persona-message-operator-",
|
|
168
|
+
];
|
|
169
|
+
expect(actual.themeCssVariables["--persona-components-message-operator-background"]).toBe(
|
|
170
|
+
"color-mix(in srgb, var(--persona-primary) 6%, var(--persona-container))"
|
|
171
|
+
);
|
|
172
|
+
for (const key of Object.keys(resolvedActual.themeCssVariables)) {
|
|
173
|
+
if (!additivePrefixes.some((prefix) => key.startsWith(prefix))) continue;
|
|
174
|
+
expect(baseline.themeCssVariables).not.toHaveProperty(key);
|
|
175
|
+
delete resolvedActual.themeCssVariables[key];
|
|
176
|
+
}
|
|
163
177
|
// Deliberately lengthen hover timing in both V4 and V5 without rewriting
|
|
164
178
|
// the historical fixture. Keyboard focus and the scan window are unchanged.
|
|
165
179
|
expect(baseline.mergedDefaultConfig.tooltip.delayMs).toBe(200);
|
package/src/index-core.ts
CHANGED
|
@@ -5,6 +5,9 @@ import {
|
|
|
5
5
|
} from "./runtime/init";
|
|
6
6
|
|
|
7
7
|
export type {
|
|
8
|
+
AgentWidgetMessageAuthor,
|
|
9
|
+
AgentWidgetModelMessageTransform,
|
|
10
|
+
AgentWidgetModelMessageProjection,
|
|
8
11
|
AgentWidgetDictationConfig,
|
|
9
12
|
DictationProviderConfig,
|
|
10
13
|
DictationPhase,
|
|
@@ -46,6 +49,8 @@ export type {
|
|
|
46
49
|
ArtifactConfigPayload,
|
|
47
50
|
AgentWidgetInitOptions,
|
|
48
51
|
AgentWidgetMessage,
|
|
52
|
+
AgentWidgetMessageRole,
|
|
53
|
+
AgentWidgetModelMessageRole,
|
|
49
54
|
AgentWidgetTurnActivity,
|
|
50
55
|
AgentWidgetTurnActivityConfig,
|
|
51
56
|
AgentWidgetLauncherConfig,
|
|
@@ -594,6 +599,8 @@ export type {
|
|
|
594
599
|
SemanticSpacing,
|
|
595
600
|
SemanticTypography,
|
|
596
601
|
ComponentTokens,
|
|
602
|
+
MessageTokens,
|
|
603
|
+
MessageGeometryTokens,
|
|
597
604
|
ComposerOverflowMenuTokens,
|
|
598
605
|
TextStyleTokens,
|
|
599
606
|
AttachmentTokens,
|
|
@@ -327,3 +327,30 @@ describe("resubmitFrom retry payload fidelity per transport", () => {
|
|
|
327
327
|
expect(bodies[0].submitMode).toBeUndefined();
|
|
328
328
|
});
|
|
329
329
|
});
|
|
330
|
+
|
|
331
|
+
describe("human operator resubmission boundary", () => {
|
|
332
|
+
beforeEach(() => {
|
|
333
|
+
vi.restoreAllMocks();
|
|
334
|
+
global.fetch = vi.fn(async () => streamResponse()) as unknown as typeof fetch;
|
|
335
|
+
});
|
|
336
|
+
it.each(["retry", "edit"] as const)("refuses %s before a human contribution without cancelling or truncating", reason => {
|
|
337
|
+
const { session, emits } = makeSession();
|
|
338
|
+
seedTurn(session);
|
|
339
|
+
session.injectMessage({ id: "human", role: "operator", content: "Refund confirmed", author: { id: "sam", displayName: "Sam", kind: "human" } });
|
|
340
|
+
const snapshot = session.getMessages();
|
|
341
|
+
const cancel = vi.spyOn(session, "cancel");
|
|
342
|
+
emits.length = 0;
|
|
343
|
+
expect(session.resubmitFrom("u1", { reason, ...(reason === "edit" && { replacement: { text: "Changed", mentionRefs: [], options: {} } }) })).toBe(false);
|
|
344
|
+
expect(session.getMessages()).toEqual(snapshot);
|
|
345
|
+
expect(cancel).not.toHaveBeenCalled();
|
|
346
|
+
expect(emits).toHaveLength(0);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it("allows a fresh user turn after the human contribution", () => {
|
|
350
|
+
const { session } = makeSession();
|
|
351
|
+
session.injectMessage({ id: "human", role: "operator", content: "Refund confirmed" });
|
|
352
|
+
session.injectUserMessage({ id: "after", content: "When will it arrive?" });
|
|
353
|
+
expect(session.resubmitFrom("after", { reason: "retry" })).toBe(true);
|
|
354
|
+
expect(session.getMessages().some(message => message.id === "human")).toBe(true);
|
|
355
|
+
});
|
|
356
|
+
});
|
package/src/session.test.ts
CHANGED
|
@@ -182,6 +182,16 @@ describe('AgentWidgetSession - Message Injection', () => {
|
|
|
182
182
|
});
|
|
183
183
|
});
|
|
184
184
|
|
|
185
|
+
describe('operator role', () => {
|
|
186
|
+
it('injects an operator message with its own id prefix', () => {
|
|
187
|
+
const result = session.injectMessage({ role: 'operator', content: 'Sam from support here.' });
|
|
188
|
+
|
|
189
|
+
expect(result.role).toBe('operator');
|
|
190
|
+
expect(result.id).toMatch(/^operator-/);
|
|
191
|
+
expect(messages.map((message) => message.role)).toEqual(['operator']);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
185
195
|
describe('message ordering', () => {
|
|
186
196
|
it('should maintain chronological order', () => {
|
|
187
197
|
session.injectMessage({
|
package/src/session.ts
CHANGED
|
@@ -68,7 +68,7 @@ import type {
|
|
|
68
68
|
ComposerResubmitOptions
|
|
69
69
|
} from "./types";
|
|
70
70
|
import { applyQuoteToContent } from "./utils/composer-quote";
|
|
71
|
-
import { findUserTurnBoundary } from "./utils/resubmit-turn";
|
|
71
|
+
import { canResubmitUserMessage, findUserTurnBoundary } from "./utils/resubmit-turn";
|
|
72
72
|
import { extractComponentDirectiveFromMessage } from "./utils/component-middleware";
|
|
73
73
|
import type { MentionSubmitBundle } from "./utils/context-mention-manager";
|
|
74
74
|
import type { VisitorStore, VisitorStoreChange } from "./utils/visitor-store";
|
|
@@ -156,6 +156,7 @@ const CONNECTION_CONFIG_KEYS = [
|
|
|
156
156
|
"parserType",
|
|
157
157
|
"contextProviders",
|
|
158
158
|
"requestMiddleware",
|
|
159
|
+
"modelMessageTransform",
|
|
159
160
|
"customFetch",
|
|
160
161
|
"backgroundConversations",
|
|
161
162
|
"parseSSEEvent",
|
|
@@ -2500,7 +2501,7 @@ export class AgentWidgetSession {
|
|
|
2500
2501
|
? generateUserMessageId()
|
|
2501
2502
|
: role === "assistant"
|
|
2502
2503
|
? generateAssistantMessageId()
|
|
2503
|
-
:
|
|
2504
|
+
: `${role === "operator" ? "operator" : "system"}-${Date.now()}-${Math.random().toString(16).slice(2)}`);
|
|
2504
2505
|
|
|
2505
2506
|
return {
|
|
2506
2507
|
id: messageId,
|
|
@@ -2515,7 +2516,8 @@ export class AgentWidgetSession {
|
|
|
2515
2516
|
...(voiceProcessing !== undefined && { voiceProcessing }),
|
|
2516
2517
|
...(voiceCaption && { voiceCaption }),
|
|
2517
2518
|
...(rawContent !== undefined && { rawContent }),
|
|
2518
|
-
...(options.dispatchError !== undefined && { dispatchError: options.dispatchError })
|
|
2519
|
+
...(options.dispatchError !== undefined && { dispatchError: options.dispatchError }),
|
|
2520
|
+
...(options.author !== undefined && { author: { ...options.author } })
|
|
2519
2521
|
};
|
|
2520
2522
|
|
|
2521
2523
|
}
|
|
@@ -3015,14 +3017,15 @@ export class AgentWidgetSession {
|
|
|
3015
3017
|
* the new attempt gets fresh message ids.
|
|
3016
3018
|
*
|
|
3017
3019
|
* Returns false when the id is unknown, is not a user message, or the rebuilt
|
|
3018
|
-
* turn would carry no content
|
|
3020
|
+
* turn would carry no content, or truncation would remove an operator reply;
|
|
3021
|
+
* nothing is truncated in those cases.
|
|
3019
3022
|
*/
|
|
3020
3023
|
public resubmitFrom(
|
|
3021
3024
|
messageId: string,
|
|
3022
3025
|
options: ComposerResubmitOptions
|
|
3023
3026
|
): boolean {
|
|
3024
3027
|
const boundary = findUserTurnBoundary(this.messages, messageId);
|
|
3025
|
-
if (!boundary) return false;
|
|
3028
|
+
if (!boundary || !canResubmitUserMessage(this.messages, messageId)) return false;
|
|
3026
3029
|
|
|
3027
3030
|
// Operate on the STORED message, never a caller-held object: appendMessage
|
|
3028
3031
|
// keeps a copy (see `ensureSequence`), so the array entry is the only ref
|
package/src/styles/widget.css
CHANGED
|
@@ -2171,6 +2171,13 @@
|
|
|
2171
2171
|
);
|
|
2172
2172
|
}
|
|
2173
2173
|
|
|
2174
|
+
.persona-message-row[data-message-role="operator"] > * {
|
|
2175
|
+
max-width: var(
|
|
2176
|
+
--persona-message-row-max-width,
|
|
2177
|
+
var(--persona-message-operator-max-width, var(--persona-message-assistant-max-width, 85%))
|
|
2178
|
+
);
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2174
2181
|
.persona-message-row[data-message-width="content"] > * {
|
|
2175
2182
|
flex: 0 1 auto;
|
|
2176
2183
|
}
|
|
@@ -3086,6 +3093,18 @@
|
|
|
3086
3093
|
box-shadow: var(--persona-message-assistant-shadow, 0 1px 2px 0 rgb(0 0 0 / 0.05));
|
|
3087
3094
|
}
|
|
3088
3095
|
|
|
3096
|
+
/* Operator bubble: a human on the agent's side (role "operator"). Renders on
|
|
3097
|
+
the assistant's side; V4 tints the container with the primary, V5 outlines it. */
|
|
3098
|
+
.persona-message-operator-bubble {
|
|
3099
|
+
border-radius: var(--persona-components-message-operator-borderRadius, var(--persona-radius-lg, 0.5rem));
|
|
3100
|
+
border-color: var(--persona-components-message-operator-border, var(--persona-border, #e5e7eb));
|
|
3101
|
+
border-width: var(--persona-components-message-operator-borderWidth, 1px);
|
|
3102
|
+
}
|
|
3103
|
+
|
|
3104
|
+
[data-persona-root] .persona-message-operator-bubble.persona-shadow-sm {
|
|
3105
|
+
box-shadow: var(--persona-components-message-operator-shadow, 0 1px 2px 0 rgb(0 0 0 / 0.05));
|
|
3106
|
+
}
|
|
3107
|
+
|
|
3089
3108
|
[data-persona-root] .persona-message-user-bubble.persona-shadow-sm {
|
|
3090
3109
|
box-shadow: var(--persona-message-user-shadow);
|
|
3091
3110
|
}
|
|
@@ -3244,7 +3263,8 @@
|
|
|
3244
3263
|
text-decoration: underline;
|
|
3245
3264
|
}
|
|
3246
3265
|
|
|
3247
|
-
[data-persona-root] .persona-message-assistant-bubble a
|
|
3266
|
+
[data-persona-root] .persona-message-assistant-bubble a,
|
|
3267
|
+
[data-persona-root] .persona-message-operator-bubble a {
|
|
3248
3268
|
color: var(--persona-md-link-color, var(--persona-accent, #0f0f0f));
|
|
3249
3269
|
}
|
|
3250
3270
|
|
|
@@ -3329,6 +3349,8 @@
|
|
|
3329
3349
|
|
|
3330
3350
|
.persona-message-assistant-bubble p,
|
|
3331
3351
|
.persona-message-assistant-bubble li,
|
|
3352
|
+
.persona-message-operator-bubble p,
|
|
3353
|
+
.persona-message-operator-bubble li,
|
|
3332
3354
|
.persona-error-detail p,
|
|
3333
3355
|
.persona-error-detail li {
|
|
3334
3356
|
color: inherit;
|
package/src/theme-reference.ts
CHANGED
|
@@ -144,6 +144,8 @@ export const THEME_TOKEN_DOCS = {
|
|
|
144
144
|
user: 'background, text, borderRadius, shadow.',
|
|
145
145
|
assistant:
|
|
146
146
|
'background, text, borderRadius, border (optional), shadow (optional).',
|
|
147
|
+
operator:
|
|
148
|
+
'Human operator bubble (role "operator", e.g. a support takeover), on the assistant side. background (default: container tinted 6% with the primary), text (semantic.colors.text), borderRadius, border (container tinted 20% with the primary), borderWidth (1px), shadow, plus padding, maxWidth, fontSize, fontFamily, lineHeight. V5 outlines it instead: transparent background, semantic.colors.border border.',
|
|
147
149
|
},
|
|
148
150
|
markdown: {
|
|
149
151
|
description: 'Markdown rendering in messages and artifact pane.',
|
|
@@ -325,7 +327,7 @@ export const THEME_TOKEN_DOCS = {
|
|
|
325
327
|
header:
|
|
326
328
|
'"default" | "minimal". Options: showIcon, showTitle, showSubtitle, showCloseButton, showClearChat.',
|
|
327
329
|
messages:
|
|
328
|
-
'"bubble" | "flat" | "minimal". Options: groupConsecutive, avatar (show, position, userAvatar, assistantAvatar), timestamp (show, position).',
|
|
330
|
+
'"bubble" | "flat" | "minimal". Options: groupConsecutive, avatar (show, position, userAvatar, assistantAvatar, operatorAvatar), timestamp (show, position, format).',
|
|
329
331
|
},
|
|
330
332
|
statusIndicator: {
|
|
331
333
|
description: 'Status text shown below the composer.',
|
package/src/types/theme.ts
CHANGED
|
@@ -580,6 +580,27 @@ export interface MessageTokens {
|
|
|
580
580
|
/** Assistant bubble box-shadow (token ref or raw CSS, e.g. `none`). */
|
|
581
581
|
shadow?: string;
|
|
582
582
|
};
|
|
583
|
+
/**
|
|
584
|
+
* Operator bubble: a human speaking on the agent's side (`role: "operator"`).
|
|
585
|
+
* Renders on the assistant's side. Defaults tint the container with the
|
|
586
|
+
* theme primary (`--persona-primary`), so `brand` themes follow without
|
|
587
|
+
* extra config. Geometry keys fall back to the layout preset, not to the
|
|
588
|
+
* assistant's geometry.
|
|
589
|
+
*/
|
|
590
|
+
operator?: MessageGeometryTokens & {
|
|
591
|
+
/** @default "color-mix(in srgb, var(--persona-primary) 6%, var(--persona-container))" */
|
|
592
|
+
background?: TokenReference<'color'>;
|
|
593
|
+
/** @default semantic.colors.text */
|
|
594
|
+
text?: TokenReference<'color'>;
|
|
595
|
+
/** @default palette.radius.lg */
|
|
596
|
+
borderRadius?: TokenReference<'radius'>;
|
|
597
|
+
/** @default "color-mix(in srgb, var(--persona-primary) 20%, var(--persona-container))" */
|
|
598
|
+
border?: TokenReference<'color'>;
|
|
599
|
+
/** Border width; set to 0px to drop it. @default "1px" */
|
|
600
|
+
borderWidth?: string;
|
|
601
|
+
/** Operator bubble box-shadow (token ref or raw CSS, e.g. `none`). */
|
|
602
|
+
shadow?: string;
|
|
603
|
+
};
|
|
583
604
|
/** Border color between messages in the thread. */
|
|
584
605
|
border?: TokenReference<'color'>;
|
|
585
606
|
}
|
package/src/types.ts
CHANGED
|
@@ -616,7 +616,7 @@ export type AgentWidgetReplayedToolResult = {
|
|
|
616
616
|
* from its own stored transcript there.
|
|
617
617
|
*/
|
|
618
618
|
export type AgentWidgetRequestPayloadMessage = {
|
|
619
|
-
role:
|
|
619
|
+
role: AgentWidgetModelMessageRole | "tool";
|
|
620
620
|
content: MessageContent;
|
|
621
621
|
createdAt: string;
|
|
622
622
|
/** Optional assistant phase for backend adapters that replay provider history. */
|
|
@@ -1123,6 +1123,21 @@ export type AgentMessageMetadata = {
|
|
|
1123
1123
|
};
|
|
1124
1124
|
};
|
|
1125
1125
|
|
|
1126
|
+
/** Ordinary model context only; the serializer owns tool-call/result pairing. */
|
|
1127
|
+
export type AgentWidgetModelMessageProjection = Pick<AgentWidgetRequestPayloadMessage, "content" | "createdAt" | "phase"> & {
|
|
1128
|
+
role: AgentWidgetModelMessageRole;
|
|
1129
|
+
};
|
|
1130
|
+
|
|
1131
|
+
/** Synchronous, per-message conversion before any dispatch transport serializes history. */
|
|
1132
|
+
export type AgentWidgetModelMessageTransform = (context: {
|
|
1133
|
+
/** Full UI transcript entry, including identity and human provenance. */
|
|
1134
|
+
message: Readonly<AgentWidgetMessage>;
|
|
1135
|
+
/** Resolved model content: contentParts > llmContent > rawContent > content. */
|
|
1136
|
+
content: MessageContent;
|
|
1137
|
+
/** Normal model projection; null for operator messages. Return null to omit. */
|
|
1138
|
+
modelMessage: AgentWidgetModelMessageProjection | null;
|
|
1139
|
+
}) => AgentWidgetModelMessageProjection | null;
|
|
1140
|
+
|
|
1126
1141
|
export type AgentWidgetRequestMiddlewareContext = {
|
|
1127
1142
|
payload: AgentWidgetRequestPayload;
|
|
1128
1143
|
config: AgentWidgetConfig;
|
|
@@ -1391,7 +1406,7 @@ export type AgentWidgetMessageActionsConfig = {
|
|
|
1391
1406
|
};
|
|
1392
1407
|
|
|
1393
1408
|
/** Which roles a custom message action appears on. */
|
|
1394
|
-
export type AgentWidgetCustomMessageActionRole = "user" | "assistant";
|
|
1409
|
+
export type AgentWidgetCustomMessageActionRole = "user" | "assistant" | "operator";
|
|
1395
1410
|
|
|
1396
1411
|
/** One host-contributed message action. */
|
|
1397
1412
|
export type AgentWidgetCustomMessageAction = {
|
|
@@ -7191,6 +7206,11 @@ export type AgentWidgetAvatarConfig = {
|
|
|
7191
7206
|
userAvatar?: string;
|
|
7192
7207
|
/** URL or emoji for assistant avatar */
|
|
7193
7208
|
assistantAvatar?: string;
|
|
7209
|
+
/**
|
|
7210
|
+
* URL or emoji for operator (human takeover) avatar. Defaults to the letter
|
|
7211
|
+
* "O" on a neutral fill.
|
|
7212
|
+
*/
|
|
7213
|
+
operatorAvatar?: string;
|
|
7194
7214
|
};
|
|
7195
7215
|
|
|
7196
7216
|
/**
|
|
@@ -7246,7 +7266,10 @@ export type AgentWidgetMessageLayoutConfig = {
|
|
|
7246
7266
|
groupConsecutive?: boolean;
|
|
7247
7267
|
/** Width behavior for user-authored message rows */
|
|
7248
7268
|
user?: AgentWidgetMessageRoleLayout;
|
|
7249
|
-
/**
|
|
7269
|
+
/**
|
|
7270
|
+
* Width behavior for assistant-authored message rows and assistant UI
|
|
7271
|
+
* variants. Operator rows sit on the same side and use it too.
|
|
7272
|
+
*/
|
|
7250
7273
|
assistant?: AgentWidgetMessageRoleLayout;
|
|
7251
7274
|
/**
|
|
7252
7275
|
* Custom renderer for user messages
|
|
@@ -8398,6 +8421,8 @@ export type AgentWidgetConfig = {
|
|
|
8398
8421
|
*/
|
|
8399
8422
|
getHeaders?: AgentWidgetHeadersFunction;
|
|
8400
8423
|
copy?: {
|
|
8424
|
+
/** Visible and accessible label for human operator replies. @default "Human operator" */
|
|
8425
|
+
operatorLabel?: string;
|
|
8401
8426
|
/** @deprecated Use `welcome.title`. */
|
|
8402
8427
|
welcomeTitle?: string;
|
|
8403
8428
|
/** @deprecated Use `welcome.subtitle`. */
|
|
@@ -8590,6 +8615,16 @@ export type AgentWidgetConfig = {
|
|
|
8590
8615
|
* See `docs/context-mentions-plan.md`.
|
|
8591
8616
|
*/
|
|
8592
8617
|
contextMentions?: AgentWidgetContextMentionConfig;
|
|
8618
|
+
/**
|
|
8619
|
+
* Synchronous model-context conversion for each nonempty conversation entry.
|
|
8620
|
+
* Runs on proxy, agent and client-token dispatch, after local-only notices and
|
|
8621
|
+
* voice captions are excluded. By default operator messages are omitted;
|
|
8622
|
+
* stateless adapters can explicitly map their historical human contributions
|
|
8623
|
+
* into supported model roles. Do not replay contributions already owned by
|
|
8624
|
+
* server history. This does not authorize steering, takeover or tool approval.
|
|
8625
|
+
* Invalid return values fail dispatch. See docs/MESSAGE-INJECTION.md.
|
|
8626
|
+
*/
|
|
8627
|
+
modelMessageTransform?: AgentWidgetModelMessageTransform;
|
|
8593
8628
|
requestMiddleware?: AgentWidgetRequestMiddleware;
|
|
8594
8629
|
actionParsers?: AgentWidgetActionParser[];
|
|
8595
8630
|
actionHandlers?: AgentWidgetActionHandler[];
|
|
@@ -9093,7 +9128,25 @@ type ConfigPatch<T> = T extends (...args: never[]) => unknown
|
|
|
9093
9128
|
? { [K in keyof T]?: ConfigPatch<T[K]> }
|
|
9094
9129
|
: T;
|
|
9095
9130
|
|
|
9096
|
-
|
|
9131
|
+
/**
|
|
9132
|
+
* Who authored a message.
|
|
9133
|
+
*
|
|
9134
|
+
* - `user`: the end user.
|
|
9135
|
+
* - `assistant`: the agent (model output).
|
|
9136
|
+
* - `system`: context for the model, rendered like an assistant row.
|
|
9137
|
+
* - `operator`: a human speaking on the agent's side of the conversation, such
|
|
9138
|
+
* as a support agent who has taken over the thread. It renders on the
|
|
9139
|
+
* assistant's side with its own bubble style (`theme.components.message.operator`).
|
|
9140
|
+
* Operator messages are omitted from model requests by default. A stateless
|
|
9141
|
+
* adapter can explicitly convert them with `modelMessageTransform`.
|
|
9142
|
+
*/
|
|
9143
|
+
export type AgentWidgetMessageRole = "user" | "assistant" | "system" | "operator";
|
|
9144
|
+
|
|
9145
|
+
/**
|
|
9146
|
+
* Roles that reach a model request. Human operator UI entries must be explicitly
|
|
9147
|
+
* converted to a supported model role by an adapter.
|
|
9148
|
+
*/
|
|
9149
|
+
export type AgentWidgetModelMessageRole = Exclude<AgentWidgetMessageRole, "operator">;
|
|
9097
9150
|
|
|
9098
9151
|
export type AgentWidgetReasoning = {
|
|
9099
9152
|
id: string;
|
|
@@ -9216,11 +9269,18 @@ export type AgentWidgetTurnActivityConfig = {
|
|
|
9216
9269
|
}) => HTMLElement | string | null | undefined;
|
|
9217
9270
|
};
|
|
9218
9271
|
|
|
9272
|
+
/** Display provenance, supplied by the host; never an authorization credential. */
|
|
9273
|
+
export type AgentWidgetMessageAuthor = {
|
|
9274
|
+
id?: string;
|
|
9275
|
+
displayName?: string;
|
|
9276
|
+
kind?: "human" | "ai" | "system";
|
|
9277
|
+
};
|
|
9278
|
+
|
|
9219
9279
|
/**
|
|
9220
9280
|
* Represents a message in the chat conversation.
|
|
9221
9281
|
*
|
|
9222
9282
|
* @property id - Unique message identifier
|
|
9223
|
-
* @property role - Message role: "user", "assistant", or "
|
|
9283
|
+
* @property role - Message role: "user", "assistant", "system", or "operator"
|
|
9224
9284
|
* @property content - Message text content (for display)
|
|
9225
9285
|
* @property contentParts - Original multi-modal content parts (for API requests)
|
|
9226
9286
|
* @property createdAt - ISO timestamp when message was created
|
|
@@ -9234,6 +9294,8 @@ export type AgentWidgetTurnActivityConfig = {
|
|
|
9234
9294
|
* Useful for implementing voice-specific behaviors like auto-reactivation.
|
|
9235
9295
|
*/
|
|
9236
9296
|
export type AgentWidgetMessage = {
|
|
9297
|
+
/** Preserved by injection, history and persistence; not sent to models automatically. */
|
|
9298
|
+
author?: AgentWidgetMessageAuthor;
|
|
9237
9299
|
/** Opt-in whole-turn presentation metadata. Does not change model-visible content. */
|
|
9238
9300
|
turnActivity?: AgentWidgetTurnActivity;
|
|
9239
9301
|
id: string;
|
|
@@ -9388,10 +9450,13 @@ export type AgentWidgetMessage = {
|
|
|
9388
9450
|
* }
|
|
9389
9451
|
*/
|
|
9390
9452
|
export type InjectMessageOptions = {
|
|
9453
|
+
/** Host-supplied display provenance. Does not grant model or backend authority. */
|
|
9454
|
+
author?: AgentWidgetMessageAuthor;
|
|
9391
9455
|
/** Mark assistant content as dispatch failure detail for `errorDisplay`. */
|
|
9392
9456
|
dispatchError?: boolean;
|
|
9393
9457
|
/**
|
|
9394
|
-
* Message role: "assistant", "user", or "
|
|
9458
|
+
* Message role: "assistant", "user", "system", or "operator". Operator
|
|
9459
|
+
* messages are omitted from requests unless modelMessageTransform converts them.
|
|
9395
9460
|
*/
|
|
9396
9461
|
role: AgentWidgetMessageRole;
|
|
9397
9462
|
|