@flamingo-stack/openframe-frontend-core 0.0.850 → 0.0.851
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/chat-protocol/events.d.ts +4 -1
- package/dist/chat-protocol/events.d.ts.map +1 -1
- package/dist/chat-protocol/index.cjs +90 -40
- package/dist/chat-protocol/nats-decoder.d.ts.map +1 -1
- package/dist/chat-protocol/nats-decoder.js +5 -1
- package/dist/chat-protocol/policy-review.d.ts +18 -0
- package/dist/chat-protocol/policy-review.d.ts.map +1 -0
- package/dist/chat-protocol/policy-review.js +55 -0
- package/dist/{chunk-ZDSC3GFX.cjs → chunk-2NGW73T2.cjs} +3 -3
- package/dist/{chunk-2QZUTNL3.cjs → chunk-2WZHNMQY.cjs} +67 -67
- package/dist/{chunk-R5JPZ6TY.cjs → chunk-5SEJEBAB.cjs} +8 -8
- package/dist/{chunk-TX4NTH22.cjs → chunk-B6D4WWKD.cjs} +36 -36
- package/dist/{chunk-FXLROQUT.cjs → chunk-CDW6JTAQ.cjs} +442 -459
- package/dist/{chunk-5PIR77V5.cjs → chunk-DN227KZH.cjs} +4 -4
- package/dist/{chunk-QJER2ZNA.cjs → chunk-FGSN5UNY.cjs} +3 -3
- package/dist/{chunk-ZGEWT3SF.cjs → chunk-HXURNKXI.cjs} +3 -3
- package/dist/{chunk-FRG724DU.cjs → chunk-KRTTH3ET.cjs} +4 -4
- package/dist/{chunk-YJZ7R3W5.cjs → chunk-KXFIYO4O.cjs} +8 -8
- package/dist/{chunk-4YBS4HIO.cjs → chunk-LM7EL3UQ.cjs} +444 -27
- package/dist/{chunk-LGYPCUHO.cjs → chunk-NQEPLUUW.cjs} +21 -21
- package/dist/{chunk-OHHDX3Z4.cjs → chunk-ODMZSAIY.cjs} +36 -36
- package/dist/{chunk-VRWBJL7B.cjs → chunk-OUP7L3U5.cjs} +50 -50
- package/dist/{chunk-5CBI2ZYV.cjs → chunk-PJNEBSP5.cjs} +5 -5
- package/dist/{chunk-6AJYGXXS.cjs → chunk-PPCRLSVR.cjs} +11 -11
- package/dist/{chunk-SA2NNWIJ.cjs → chunk-QCSA7GIX.cjs} +17 -62
- package/dist/{chunk-JWIITCVB.cjs → chunk-SPKAXETT.cjs} +42 -42
- package/dist/{chunk-CAGNWDVJ.cjs → chunk-U4RTWH5D.cjs} +42 -42
- package/dist/{chunk-IHY7E4EX.cjs → chunk-UFGPLXH6.cjs} +4 -4
- package/dist/{chunk-7ICHJVAT.cjs → chunk-UQK6J3ZS.cjs} +13 -13
- package/dist/{chunk-S3QXLD4X.cjs → chunk-VCFQHPOO.cjs} +37 -3
- package/dist/{chunk-IPGGSFR7.cjs → chunk-VU6N3NZS.cjs} +6 -6
- package/dist/{chunk-GAYLAA3L.cjs → chunk-VXIZCUNZ.cjs} +61 -61
- package/dist/{chunk-3W4ASAXN.cjs → chunk-WYROOMUZ.cjs} +2 -2
- package/dist/{chunk-XKOL5CNU.cjs → chunk-ZEPEFKHA.cjs} +811 -932
- package/dist/{cloud-tenants-TVB3DT3J.cjs → cloud-tenants-XH6GV4EI.cjs} +12 -13
- package/dist/components/case-studies/index.cjs +24 -25
- package/dist/components/chat/approval-batch-message.d.ts +5 -0
- package/dist/components/chat/approval-batch-message.d.ts.map +1 -1
- package/dist/components/chat/approval-batch-message.js +36 -10
- package/dist/components/chat/index.cjs +8 -9
- package/dist/components/chat/policy-review.d.ts +27 -0
- package/dist/components/chat/policy-review.d.ts.map +1 -0
- package/dist/components/chat/policy-review.js +67 -0
- package/dist/components/chat/stream/message-mutations.d.ts.map +1 -1
- package/dist/components/chat/stream/message-mutations.js +4 -1
- package/dist/components/chat/tool-execution-display.d.ts.map +1 -1
- package/dist/components/chat/tool-execution-display.js +16 -5
- package/dist/components/chat/types/message.types.d.ts +40 -2
- package/dist/components/chat/types/message.types.d.ts.map +1 -1
- package/dist/components/chat/utils/extract-incomplete-message-state.d.ts.map +1 -1
- package/dist/components/chat/utils/extract-incomplete-message-state.js +2 -0
- package/dist/components/chat/utils/message-segment-accumulator.d.ts.map +1 -1
- package/dist/components/chat/utils/message-segment-accumulator.js +3 -0
- package/dist/components/chat/utils/process-historical-messages.d.ts +2 -2
- package/dist/components/chat/utils/process-historical-messages.d.ts.map +1 -1
- package/dist/components/chat/utils/process-historical-messages.js +7 -1
- package/dist/components/contact/index.cjs +13 -14
- package/dist/components/docs/index.cjs +16 -17
- package/dist/components/embeds/index.cjs +13 -14
- package/dist/components/faq/index.cjs +14 -15
- package/dist/components/features/approval-level.d.ts +6 -12
- package/dist/components/features/approval-level.d.ts.map +1 -1
- package/dist/components/features/approval-level.js +45 -55
- package/dist/components/features/index.cjs +16 -16
- package/dist/components/features/notifications/approval-request-notification-tile.d.ts.map +1 -1
- package/dist/components/features/notifications/approval-request-notification-tile.js +71 -48
- package/dist/components/features/notifications/types.d.ts +2 -0
- package/dist/components/features/notifications/types.d.ts.map +1 -1
- package/dist/components/features/notifications/types.js +3 -1
- package/dist/components/features/policy-configuration-panel.d.ts.map +1 -1
- package/dist/components/features/policy-configuration-panel.js +75 -64
- package/dist/components/help-center-pages/index.cjs +78 -79
- package/dist/components/index.cjs +57 -59
- package/dist/components/meeting-scheduler/index.cjs +61 -61
- package/dist/components/navigation/index.cjs +15 -16
- package/dist/components/onboarding-guides/index.cjs +14 -15
- package/dist/components/product-demo/index.cjs +23 -24
- package/dist/components/tickets/index.cjs +17 -18
- package/dist/components/ui/file-manager/index.cjs +8 -9
- package/dist/components/ui/index.cjs +16 -18
- package/dist/components/ui/radio-group.d.ts.map +1 -1
- package/dist/components/ui/radio-group.js +8 -2
- package/dist/components/ui/tab-selector.d.ts +7 -1
- package/dist/components/ui/tab-selector.d.ts.map +1 -1
- package/dist/components/ui/tab-selector.js +22 -3
- package/dist/{devices-NGCTSXIU.cjs → devices-7PT6MPJI.cjs} +13 -14
- package/dist/{embeddable-chat-322NDFP5.cjs → embeddable-chat-5NY76K7K.cjs} +84 -85
- package/dist/index.cjs +23 -25
- package/dist/{knowledge-75PO5DYS.cjs → knowledge-J6N7IEVI.cjs} +13 -14
- package/dist/{logs-PZESOAIG.cjs → logs-D6KQP2BX.cjs} +19 -20
- package/dist/{policies-6XNMBFWO.cjs → policies-J7MCOL3C.cjs} +16 -17
- package/dist/{remote-session-Q2FIBCGT.cjs → remote-session-PXHA5KFK.cjs} +8 -9
- package/dist/{scripts-B3JW6TPF.cjs → scripts-VURXIHVS.cjs} +19 -20
- package/dist/{software-update-NWP542J7.cjs → software-update-6SHUBDIE.cjs} +16 -17
- package/dist/{tickets-board-7FLGQEHF.cjs → tickets-board-NLCN7WNX.cjs} +9 -9
- package/package.json +1 -1
- package/src/chat-protocol/events.ts +4 -1
- package/src/chat-protocol/nats-decoder.ts +4 -0
- package/src/chat-protocol/policy-review.ts +71 -0
- package/src/components/chat/approval-batch-message.tsx +47 -10
- package/src/components/chat/policy-review.tsx +90 -0
- package/src/components/chat/stream/message-mutations.ts +4 -0
- package/src/components/chat/tool-execution-display.tsx +23 -4
- package/src/components/chat/types/message.types.ts +42 -2
- package/src/components/chat/utils/extract-incomplete-message-state.ts +2 -0
- package/src/components/chat/utils/message-segment-accumulator.ts +3 -0
- package/src/components/chat/utils/process-historical-messages.ts +14 -3
- package/src/components/features/approval-level.tsx +74 -78
- package/src/components/features/notifications/approval-request-notification-tile.tsx +75 -50
- package/src/components/features/notifications/types.ts +4 -0
- package/src/components/features/policy-configuration-panel.tsx +100 -83
- package/src/components/ui/radio-group.tsx +20 -2
- package/src/components/ui/tab-selector.tsx +26 -3
- package/dist/chunk-N3MBQRCQ.cjs +0 -41
|
@@ -3,24 +3,18 @@
|
|
|
3
3
|
import type { ComponentType } from 'react';
|
|
4
4
|
import type { ApprovalLevel } from '../../types/permissions';
|
|
5
5
|
import { cn } from '../../utils/cn';
|
|
6
|
-
import { HeadsetIcon } from '../icons-v2-generated/audio-and-visual/headset-icon';
|
|
7
6
|
import { BannedIcon } from '../icons-v2-generated/security/banned-icon';
|
|
7
|
+
import { ShieldCheckIcon } from '../icons-v2-generated/security/shield-check-icon';
|
|
8
8
|
import { CheckCircleIcon } from '../icons-v2-generated/signs-and-symbols/check-circle-icon';
|
|
9
|
-
import {
|
|
9
|
+
import { UserCheckIcon } from '../icons-v2-generated/users/user-check-icon';
|
|
10
10
|
import { UnifiedSkeleton } from '../loading/unified-skeleton';
|
|
11
|
-
import { TOGGLE_GROUP_CLASS, ToggleGroup, ToggleGroupItem } from '../toggle-group';
|
|
12
11
|
import { TextHold } from '../ui/layout-hold';
|
|
13
12
|
import { useLoadingScope } from '../ui/loading-scope';
|
|
14
|
-
import {
|
|
15
|
-
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/tooltip';
|
|
13
|
+
import { TabSelector } from '../ui/tab-selector';
|
|
16
14
|
import { TouchFriendlyTooltip } from '../ui/touch-friendly-tooltip';
|
|
17
15
|
|
|
18
|
-
/**
|
|
19
|
-
|
|
20
|
-
* (allow, never) side by side, then the two that ask someone (a technician,
|
|
21
|
-
* the user) side by side.
|
|
22
|
-
*/
|
|
23
|
-
export const APPROVAL_LEVELS: readonly ApprovalLevel[] = ['ALLOW', 'DENY', 'ASK_TECHNICIAN', 'ASK_USER'];
|
|
16
|
+
/** The approval levels, from the freest to the strictest, as the guardrails settings order them (Figma 4365-22670). */
|
|
17
|
+
export const APPROVAL_LEVELS: readonly ApprovalLevel[] = ['ALLOW', 'ASK_USER', 'ASK_TECHNICIAN', 'DENY'];
|
|
24
18
|
|
|
25
19
|
export interface ApprovalLevelMeta {
|
|
26
20
|
/** The level's name, as every surface writes it. */
|
|
@@ -47,31 +41,48 @@ export const APPROVAL_LEVEL_META: Record<ApprovalLevel, ApprovalLevelMeta> = {
|
|
|
47
41
|
ASK_USER: {
|
|
48
42
|
label: 'Ask User',
|
|
49
43
|
hint: 'Asks the person at the computer first.',
|
|
50
|
-
Icon:
|
|
44
|
+
Icon: UserCheckIcon,
|
|
51
45
|
toneClassName: 'text-ods-warning',
|
|
52
46
|
},
|
|
53
47
|
ASK_TECHNICIAN: {
|
|
54
48
|
label: 'Ask Technician',
|
|
55
49
|
hint: 'Asks a technician first. Nothing runs until they approve.',
|
|
56
|
-
Icon:
|
|
50
|
+
Icon: ShieldCheckIcon,
|
|
57
51
|
toneClassName: 'text-ods-warning',
|
|
58
52
|
},
|
|
59
53
|
DENY: { label: 'Restrict', hint: 'Never runs.', Icon: BannedIcon, toneClassName: 'text-ods-error' },
|
|
60
54
|
};
|
|
61
55
|
|
|
62
|
-
|
|
56
|
+
/** A read-only level's icon (Figma 1608-49531: 24px, before the name). */
|
|
57
|
+
const ICON_PX = 24;
|
|
63
58
|
|
|
64
59
|
/** The view's layout, read by the view and by its skeleton, so the skeleton is the view's size. */
|
|
65
60
|
const READ_ONLY_ROW_CLASS = 'inline-flex shrink-0 items-center gap-[var(--spacing-system-xxs)]';
|
|
66
|
-
|
|
61
|
+
/** Editable: the name after the strip on a phone, before it from `content-md` (Figma 4365-22461 / 4365-22663). */
|
|
62
|
+
const EDITABLE_ROW_CLASS =
|
|
63
|
+
'flex shrink-0 items-center gap-[var(--spacing-system-xxs)] content-md:gap-[var(--spacing-system-m)]';
|
|
64
|
+
const EDITABLE_NAME_ORDER_CLASS = 'order-last content-md:order-none';
|
|
65
|
+
/** A read-only level's icon box: 16px on a phone, 24 from `content-md` (Figma 555-39573 / 1608-49531). */
|
|
66
|
+
const READ_ONLY_ICON_BOX_CLASS = '[&>*]:!size-4 content-md:[&>*]:!size-6';
|
|
67
|
+
/** Read-only, the name is the row's value (h4, primary); editable, a caption in the level's colour beside the strip. */
|
|
68
|
+
const READ_ONLY_NAME_CLASS = 'whitespace-nowrap text-ods-text-primary text-h4';
|
|
67
69
|
const LEVEL_NAME_CLASS = 'whitespace-nowrap text-h6';
|
|
68
|
-
/** An editable level's
|
|
69
|
-
const
|
|
70
|
+
/** An editable level's icon (Figma: 20px in the segment). */
|
|
71
|
+
const EDITABLE_ICON_PX = 20;
|
|
70
72
|
/** The longest level name: the skeleton's name bar is exactly its width (the component's own words, unseen). */
|
|
71
73
|
const LONGEST_LEVEL_LABEL = APPROVAL_LEVELS.map(level => APPROVAL_LEVEL_META[level].label).reduce((a, b) =>
|
|
72
74
|
b.length > a.length ? b : a,
|
|
73
75
|
);
|
|
74
76
|
|
|
77
|
+
/** The strip's four levels: the one in force (none while loading) in the primary colour, the others secondary (Figma 4365-22670). */
|
|
78
|
+
function levelItems(inForce: ApprovalLevel | undefined) {
|
|
79
|
+
return APPROVAL_LEVELS.map(level => {
|
|
80
|
+
const { label, Icon } = APPROVAL_LEVEL_META[level];
|
|
81
|
+
const tone = level === inForce ? 'text-ods-text-primary' : 'text-ods-text-secondary';
|
|
82
|
+
return { id: level, ariaLabel: label, icon: <Icon size={EDITABLE_ICON_PX} className={tone} aria-hidden /> };
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
75
86
|
export interface ApprovalLevelViewProps {
|
|
76
87
|
/** The level in force. */
|
|
77
88
|
value: ApprovalLevel;
|
|
@@ -100,13 +111,11 @@ export interface ApprovalLevelViewProps {
|
|
|
100
111
|
|
|
101
112
|
/**
|
|
102
113
|
* THE way a level is shown, read-only or editable, by one prop: the level's
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
* Editable, each icon has its level's name as its accessible name and a
|
|
109
|
-
* tooltip with what the level means (hover or keyboard focus; a tap selects).
|
|
114
|
+
* icon in its colour, then its name (read-only, Figma 1608-49531), or its name
|
|
115
|
+
* in its colour, then the four levels' icons in a compact `TabSelector` strip,
|
|
116
|
+
* the one in force filled (editable, Figma 4365-22670). Name, icon and colour
|
|
117
|
+
* are `APPROVAL_LEVEL_META`'s in both. Editable, each icon has its level's name
|
|
118
|
+
* as its accessible name.
|
|
110
119
|
*/
|
|
111
120
|
export function ApprovalLevelView({
|
|
112
121
|
value,
|
|
@@ -124,18 +133,16 @@ export function ApprovalLevelView({
|
|
|
124
133
|
return <ApprovalLevelSkeleton editable={canEdit} showLabel={showLabel} className={className} />;
|
|
125
134
|
}
|
|
126
135
|
const current = APPROVAL_LEVEL_META[value];
|
|
127
|
-
const name = showLabel ? <span className={cn(LEVEL_NAME_CLASS, current.toneClassName)}>{current.label}</span> : null;
|
|
128
136
|
|
|
129
137
|
if (!canEdit) {
|
|
130
138
|
const icon = (
|
|
131
|
-
<span className={cn('inline-flex shrink-0', current.toneClassName)}>
|
|
139
|
+
<span className={cn('inline-flex shrink-0', READ_ONLY_ICON_BOX_CLASS, current.toneClassName)}>
|
|
132
140
|
<current.Icon size={ICON_PX} aria-hidden />
|
|
133
141
|
{!showLabel && <span className="sr-only">{current.label}</span>}
|
|
134
142
|
</span>
|
|
135
143
|
);
|
|
136
144
|
return (
|
|
137
145
|
<span className={cn(READ_ONLY_ROW_CLASS, className)}>
|
|
138
|
-
{name}
|
|
139
146
|
{showLabel ? (
|
|
140
147
|
icon
|
|
141
148
|
) : (
|
|
@@ -143,49 +150,32 @@ export function ApprovalLevelView({
|
|
|
143
150
|
{icon}
|
|
144
151
|
</TouchFriendlyTooltip>
|
|
145
152
|
)}
|
|
153
|
+
{showLabel && <span className={READ_ONLY_NAME_CLASS}>{current.label}</span>}
|
|
146
154
|
</span>
|
|
147
155
|
);
|
|
148
156
|
}
|
|
149
157
|
|
|
158
|
+
const name = showLabel ? (
|
|
159
|
+
<span className={cn(LEVEL_NAME_CLASS, EDITABLE_NAME_ORDER_CLASS, current.toneClassName)}>{current.label}</span>
|
|
160
|
+
) : null;
|
|
161
|
+
|
|
150
162
|
return (
|
|
151
163
|
<div className={cn(EDITABLE_ROW_CLASS, className)}>
|
|
152
164
|
{name}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
variant="
|
|
157
|
-
size="
|
|
165
|
+
{/* The settings design's segmented control: one strip, the level in force filled (Figma 4365-22670). */}
|
|
166
|
+
<div role="group" aria-labelledby={labelledBy}>
|
|
167
|
+
<TabSelector
|
|
168
|
+
variant="secondary"
|
|
169
|
+
size="compact"
|
|
158
170
|
value={value}
|
|
159
171
|
disabled={disabled}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if (next && next !== value) onChange?.(next as ApprovalLevel);
|
|
172
|
+
onValueChange={next => {
|
|
173
|
+
if (next !== value) onChange?.(next as ApprovalLevel);
|
|
163
174
|
}}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
return (
|
|
169
|
-
// A plain tooltip (hover and keyboard focus): a tap must SELECT the level, and on
|
|
170
|
-
// touch the name beside the group says what is in force.
|
|
171
|
-
<Tooltip key={level}>
|
|
172
|
-
<TooltipTrigger asChild>
|
|
173
|
-
<ToggleGroupItem
|
|
174
|
-
value={level}
|
|
175
|
-
aria-label={label}
|
|
176
|
-
className={cn(LEVEL_BUTTON_CLASS, level === value && toneClassName)}
|
|
177
|
-
>
|
|
178
|
-
<Icon size={ICON_PX} aria-hidden />
|
|
179
|
-
</ToggleGroupItem>
|
|
180
|
-
</TooltipTrigger>
|
|
181
|
-
<TooltipContent side="top" className="max-w-xs">
|
|
182
|
-
<b>{label}</b>: {hint}
|
|
183
|
-
</TooltipContent>
|
|
184
|
-
</Tooltip>
|
|
185
|
-
);
|
|
186
|
-
})}
|
|
187
|
-
</ToggleGroup>
|
|
188
|
-
</TooltipProvider>
|
|
175
|
+
items={levelItems(value)}
|
|
176
|
+
className="w-auto"
|
|
177
|
+
/>
|
|
178
|
+
</div>
|
|
189
179
|
</div>
|
|
190
180
|
);
|
|
191
181
|
}
|
|
@@ -200,35 +190,41 @@ interface ApprovalLevelSkeletonProps {
|
|
|
200
190
|
|
|
201
191
|
/**
|
|
202
192
|
* `ApprovalLevelView`'s own skeleton (a level chip, not a whole view): a level while it is read: the view's own row, the name a bar at its type step (the longest name's width), and the
|
|
203
|
-
* read-only icon's box or the editable
|
|
193
|
+
* read-only icon's box or the editable strip itself, disabled. Inert: nothing in it can be picked.
|
|
204
194
|
*/
|
|
205
195
|
function ApprovalLevelSkeleton({ editable = false, showLabel = true, className }: ApprovalLevelSkeletonProps) {
|
|
206
|
-
const name = showLabel ? (
|
|
207
|
-
<span className={LEVEL_NAME_CLASS}>
|
|
208
|
-
<TextHold>{LONGEST_LEVEL_LABEL}</TextHold>
|
|
209
|
-
</span>
|
|
210
|
-
) : null;
|
|
211
196
|
if (!editable) {
|
|
212
197
|
return (
|
|
213
198
|
<span aria-hidden className={cn(READ_ONLY_ROW_CLASS, className)}>
|
|
214
|
-
{
|
|
215
|
-
|
|
199
|
+
<span className={cn('inline-flex shrink-0', READ_ONLY_ICON_BOX_CLASS)}>
|
|
200
|
+
<UnifiedSkeleton as="span" className="inline-block shrink-0 rounded-sm" width={ICON_PX} height={ICON_PX} />
|
|
201
|
+
</span>
|
|
202
|
+
{showLabel && (
|
|
203
|
+
<span className={READ_ONLY_NAME_CLASS}>
|
|
204
|
+
<TextHold>{LONGEST_LEVEL_LABEL}</TextHold>
|
|
205
|
+
</span>
|
|
206
|
+
)}
|
|
216
207
|
</span>
|
|
217
208
|
);
|
|
218
209
|
}
|
|
210
|
+
const name = showLabel ? (
|
|
211
|
+
<span className={cn(LEVEL_NAME_CLASS, EDITABLE_NAME_ORDER_CLASS)}>
|
|
212
|
+
<TextHold>{LONGEST_LEVEL_LABEL}</TextHold>
|
|
213
|
+
</span>
|
|
214
|
+
) : null;
|
|
219
215
|
return (
|
|
220
216
|
<div aria-hidden inert className={cn(EDITABLE_ROW_CLASS, className)}>
|
|
221
217
|
{name}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
218
|
+
{/* The real strip, inert, with no level in force yet. */}
|
|
219
|
+
<TabSelector
|
|
220
|
+
variant="secondary"
|
|
221
|
+
size="compact"
|
|
222
|
+
value=""
|
|
223
|
+
disabled
|
|
224
|
+
onValueChange={() => {}}
|
|
225
|
+
items={levelItems(undefined)}
|
|
226
|
+
className="w-auto"
|
|
227
|
+
/>
|
|
232
228
|
</div>
|
|
233
229
|
);
|
|
234
230
|
}
|
|
@@ -5,7 +5,9 @@ import { cn } from '../../../utils/cn';
|
|
|
5
5
|
import { ApprovalBatchMessage, ApprovalStatusTag } from '../../chat/approval-batch-message';
|
|
6
6
|
import { ExpandChevron } from '../../chat/expand-chevron';
|
|
7
7
|
import { useCollapsible } from '../../chat/hooks/use-collapsible';
|
|
8
|
+
import { PolicyEscalationAlert, isPolicyEscalated } from '../../chat/policy-review';
|
|
8
9
|
import type { ChatApprovalStatus } from '../../chat/types';
|
|
10
|
+
import { AlertCircleIcon } from '../../icons-v2-generated';
|
|
9
11
|
import { Button } from '../../ui/button';
|
|
10
12
|
import {
|
|
11
13
|
APPROVAL_BUTTON_CLASS,
|
|
@@ -85,6 +87,10 @@ export function ApprovalRequestNotificationTile({
|
|
|
85
87
|
type: notification.type ?? 'Approval Required',
|
|
86
88
|
};
|
|
87
89
|
|
|
90
|
+
// What Mingo escalated is read with the commands folded away, in the chat block's order (Figma 1375-114530): the
|
|
91
|
+
// "!" beside the chevron, the "Mingo Escalated" band before the buttons, Approve in the warning colour.
|
|
92
|
+
const escalatedCalls = batchData.toolCalls.filter(call => isPolicyEscalated(call.policyReview));
|
|
93
|
+
const escalated = escalatedCalls.length > 0;
|
|
88
94
|
const commandWord = batchData.toolCalls.length > 1 ? 'commands' : 'command';
|
|
89
95
|
const toggleLabel = `${expanded ? 'Hide' : 'Show'} ${commandWord} section`;
|
|
90
96
|
|
|
@@ -119,57 +125,75 @@ export function ApprovalRequestNotificationTile({
|
|
|
119
125
|
timeLabel={timeLabel}
|
|
120
126
|
paused={pinned || processing}
|
|
121
127
|
actions={
|
|
122
|
-
|
|
123
|
-
{
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
128
|
+
<>
|
|
129
|
+
{!lockscreen &&
|
|
130
|
+
escalatedCalls.map(call => (
|
|
131
|
+
// Edge to edge, as under the chat's command row: the tile's padding cancelled (`w-auto`, so the stretched
|
|
132
|
+
// flex item grows into it instead of keeping the Alert's `w-full`), a rule above and below.
|
|
133
|
+
<PolicyEscalationAlert
|
|
134
|
+
key={call.toolExecutionRequestId}
|
|
135
|
+
review={call.policyReview}
|
|
136
|
+
className="-mx-[var(--spacing-system-s)] w-auto border-y border-ods-border"
|
|
137
|
+
/>
|
|
138
|
+
))}
|
|
139
|
+
<div className="flex w-full items-center gap-[var(--spacing-system-xs)]">
|
|
140
|
+
{status === 'pending' ? (
|
|
141
|
+
<>
|
|
142
|
+
<Button
|
|
143
|
+
variant={escalated ? 'warning' : 'accent'}
|
|
144
|
+
size="small"
|
|
145
|
+
onClick={() => resolve('approved', onApprove)}
|
|
146
|
+
disabled={processing}
|
|
147
|
+
data-pressed={approvePressed || undefined}
|
|
148
|
+
className={cn(
|
|
149
|
+
'transition-[filter,transform] duration-200 motion-reduce:transition-none',
|
|
150
|
+
lockscreen ? LOCKSCREEN_APPROVAL_BUTTON_CLASS : APPROVAL_BUTTON_CLASS,
|
|
151
|
+
approvePressed && 'scale-[0.98] brightness-75',
|
|
152
|
+
)}
|
|
153
|
+
>
|
|
154
|
+
Approve
|
|
155
|
+
</Button>
|
|
156
|
+
<Button
|
|
157
|
+
variant="outline"
|
|
158
|
+
size="small"
|
|
159
|
+
onClick={() => resolve('rejected', onReject)}
|
|
160
|
+
disabled={processing}
|
|
161
|
+
className={lockscreen ? LOCKSCREEN_APPROVAL_BUTTON_CLASS : APPROVAL_BUTTON_CLASS}
|
|
162
|
+
>
|
|
163
|
+
Reject
|
|
164
|
+
</Button>
|
|
165
|
+
</>
|
|
166
|
+
) : (
|
|
167
|
+
<span className="flex min-w-0 items-center gap-[var(--spacing-system-xsf)]">
|
|
168
|
+
<ApprovalStatusTag status={status} />
|
|
169
|
+
{approval.resolvedByName ? (
|
|
170
|
+
<span className="truncate text-ods-text-secondary text-h6">by {approval.resolvedByName}</span>
|
|
171
|
+
) : null}
|
|
172
|
+
</span>
|
|
173
|
+
)}
|
|
174
|
+
{!lockscreen && (
|
|
175
|
+
<span className="ml-auto flex shrink-0 items-center gap-[var(--spacing-system-xs)]">
|
|
176
|
+
{escalated && !expanded && (
|
|
177
|
+
// Decorative: the band above says it in words. Open, each command row draws its own.
|
|
178
|
+
<AlertCircleIcon className="size-4 text-ods-warning" aria-hidden data-escalation-mark />
|
|
135
179
|
)}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
<span className="truncate text-ods-text-secondary text-h6">by {approval.resolvedByName}</span>
|
|
154
|
-
) : null}
|
|
155
|
-
</span>
|
|
156
|
-
)}
|
|
157
|
-
{!lockscreen && (
|
|
158
|
-
<Button
|
|
159
|
-
variant="outline"
|
|
160
|
-
size="small"
|
|
161
|
-
onClick={() => {
|
|
162
|
-
setExpanded(prev => !prev);
|
|
163
|
-
setPinned(true);
|
|
164
|
-
}}
|
|
165
|
-
aria-expanded={expanded}
|
|
166
|
-
aria-label={toggleLabel}
|
|
167
|
-
className={APPROVAL_TOGGLE_BUTTON_CLASS}
|
|
168
|
-
>
|
|
169
|
-
<ExpandChevron expanded={expanded} />
|
|
170
|
-
</Button>
|
|
171
|
-
)}
|
|
172
|
-
</div>
|
|
180
|
+
<Button
|
|
181
|
+
variant="outline"
|
|
182
|
+
size="small"
|
|
183
|
+
onClick={() => {
|
|
184
|
+
setExpanded(prev => !prev);
|
|
185
|
+
setPinned(true);
|
|
186
|
+
}}
|
|
187
|
+
aria-expanded={expanded}
|
|
188
|
+
aria-label={toggleLabel}
|
|
189
|
+
className={APPROVAL_TOGGLE_BUTTON_CLASS}
|
|
190
|
+
>
|
|
191
|
+
<ExpandChevron expanded={expanded} />
|
|
192
|
+
</Button>
|
|
193
|
+
</span>
|
|
194
|
+
)}
|
|
195
|
+
</div>
|
|
196
|
+
</>
|
|
173
197
|
}
|
|
174
198
|
>
|
|
175
199
|
{!lockscreen && (
|
|
@@ -182,6 +206,7 @@ export function ApprovalRequestNotificationTile({
|
|
|
182
206
|
data={batchData}
|
|
183
207
|
showExecutionStatus={false}
|
|
184
208
|
showFooterActions={false}
|
|
209
|
+
showEscalationAlerts={false}
|
|
185
210
|
maxBodyHeight="50vh"
|
|
186
211
|
className="mb-0 rounded-none border-0"
|
|
187
212
|
/>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
+
import { policyReviewField } from '../../../chat-protocol/policy-review';
|
|
2
3
|
import type { NotificationSeverity } from '../../../utils/notifications';
|
|
3
4
|
import { APPROVAL_STATUS, type ApprovalBatchData, type ChatApprovalStatus } from '../../chat/types';
|
|
4
5
|
|
|
@@ -73,6 +74,8 @@ export interface ApprovalToolCallMeta {
|
|
|
73
74
|
requiresApproval?: boolean;
|
|
74
75
|
approvalType?: string | null;
|
|
75
76
|
toolCallArguments?: Record<string, unknown> | null;
|
|
77
|
+
/** The guardrail review, as the wire sends it: normalized into the batch by `approvalMetaToBatchData`. */
|
|
78
|
+
policyReview?: unknown;
|
|
76
79
|
}
|
|
77
80
|
|
|
78
81
|
/** Approval payload stashed in `notification.meta` for an approval-request notification. */
|
|
@@ -138,6 +141,7 @@ export function approvalMetaToBatchData(approval: ApprovalNotificationMeta): App
|
|
|
138
141
|
requiresApproval: tc.requiresApproval ?? true,
|
|
139
142
|
approvalType: tc.approvalType ?? null,
|
|
140
143
|
toolCallArguments: tc.toolCallArguments ?? null,
|
|
144
|
+
...policyReviewField(tc.policyReview),
|
|
141
145
|
})),
|
|
142
146
|
};
|
|
143
147
|
}
|