@burdenoff/microfe-botmanor 2026.1026.1 → 2026.1027.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentApprovalPanel.js","names":[],"sources":["../../../src/botlit/components/AgentApprovalPanel.tsx"],"sourcesContent":["/**\n * Agent Activation Approval Panel (BOFF-9682)\n * @module botlit/components\n *\n * Reviewer gate on the agent detail page. Builders request activation (the\n * request pins the agent's current config); reviewers with\n * `botmanor-agent:approve` approve it, which publishes the draft and takes the\n * agent live, or reject it with a reason. The requester cannot decide their\n * own request, and a request whose agent changed afterwards is flagged stale\n * and cannot be approved.\n */\n\nimport { useState, type FC } from 'react';\nimport { AlertTriangle, CheckCircle2, ShieldCheck, Send, XCircle } from 'lucide-react';\nimport { toast } from 'sonner';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from './Card';\nimport { Button } from './Button';\nimport { Badge } from './Badge';\nimport {\n useAgentApprovalActions,\n useAgentApprovalRequests,\n useAgentApprovalSettings,\n type AgentApprovalRequestRecord,\n type AgentApprovalStatus,\n} from '../hooks/useAgentApprovals';\nimport { useBotlit } from '../providers/BotlitProvider';\nimport { formatRelativeTime } from '../utils/formatters';\n\nexport function approvalStatusVariant(\n status: AgentApprovalStatus\n): 'warning' | 'success' | 'destructive' | 'outline' {\n if (status === 'PENDING') return 'warning';\n if (status === 'APPROVED') return 'success';\n if (status === 'REJECTED') return 'destructive';\n return 'outline';\n}\n\ntype DecisionKind = 'request' | 'approve' | 'reject';\n\ninterface DecisionDialogProps {\n kind: DecisionKind | null;\n loading: boolean;\n onClose: () => void;\n onConfirm: (text: string) => Promise<void>;\n}\n\n/** Note / reason dialog shared by the detail panel and the approvals page. */\nexport const ApprovalDecisionDialog: FC<DecisionDialogProps> = ({\n kind,\n loading,\n onClose,\n onConfirm,\n}) => {\n const t = useTr();\n const [text, setText] = useState('');\n const reasonRequired = kind === 'reject';\n\n const copy: Record<DecisionKind, { title: string; description: string; confirm: string }> = {\n request: {\n title: t('botlit.agentApproval.dialog.request.title', 'Request activation'),\n description: t(\n 'botlit.agentApproval.dialog.request.description',\n \"A reviewer will check the agent's current prompt, model, tools and limits before it goes live. Changing the agent afterwards makes this request stale.\"\n ),\n confirm: t('botlit.agentApproval.dialog.request.confirm', 'Submit for review'),\n },\n approve: {\n title: t('botlit.agentApproval.dialog.approve.title', 'Approve and go live'),\n description: t(\n 'botlit.agentApproval.dialog.approve.description',\n \"Approving publishes the agent's draft and makes it ACTIVE immediately.\"\n ),\n confirm: t('botlit.agentApproval.dialog.approve.confirm', 'Approve'),\n },\n reject: {\n title: t('botlit.agentApproval.dialog.reject.title', 'Reject activation'),\n description: t(\n 'botlit.agentApproval.dialog.reject.description',\n 'Tell the requester what needs to change. The agent stays as it is.'\n ),\n confirm: t('botlit.agentApproval.dialog.reject.confirm', 'Reject'),\n },\n };\n const current = kind ? copy[kind] : null;\n\n return (\n <Dialog\n open={kind !== null}\n onOpenChange={(open) => {\n if (!open) {\n setText('');\n onClose();\n }\n }}\n >\n <DialogContent>\n <DialogHeader>\n <DialogTitle>{current?.title}</DialogTitle>\n <DialogDescription>{current?.description}</DialogDescription>\n </DialogHeader>\n <Textarea\n aria-label={\n kind === 'request'\n ? t('botlit.agentApproval.dialog.noteLabel', 'Note for the reviewer')\n : t('botlit.agentApproval.dialog.reasonLabel', 'Reason')\n }\n value={text}\n maxLength={2000}\n onChange={(event) => setText(event.target.value)}\n placeholder={\n kind === 'request'\n ? t('botlit.agentApproval.dialog.notePlaceholder', 'What changed and why (optional)')\n : reasonRequired\n ? t('botlit.agentApproval.dialog.reasonRequiredPlaceholder', 'Reason (required)')\n : t('botlit.agentApproval.dialog.reasonPlaceholder', 'Reason (optional)')\n }\n rows={4}\n />\n <DialogFooter>\n <Button\n variant=\"outline\"\n onClick={() => {\n setText('');\n onClose();\n }}\n >\n {t('botlit.common.cancel', 'Cancel')}\n </Button>\n <Button\n variant={kind === 'reject' ? 'destructive' : 'default'}\n isLoading={loading}\n disabled={reasonRequired && text.trim().length === 0}\n onClick={async () => {\n await onConfirm(text);\n setText('');\n }}\n >\n {current?.confirm}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n};\n\ninterface AgentApprovalPanelProps {\n agentId: string;\n agentStatus: string;\n /** Called after a decision that changes the agent so the page can refetch it. */\n onChanged?: () => void;\n}\n\nexport const AgentApprovalPanel: FC<AgentApprovalPanelProps> = ({\n agentId,\n agentStatus,\n onChanged,\n}) => {\n const t = useTr();\n const { currentUser } = useBotlit();\n const settings = useAgentApprovalSettings();\n const requests = useAgentApprovalRequests({ agentId });\n const { run, pending } = useAgentApprovalActions();\n const [dialog, setDialog] = useState<{\n kind: DecisionKind;\n request?: AgentApprovalRequestRecord;\n } | null>(null);\n\n const required = settings.data?.requireActivationApproval === true;\n const open = requests.data.find((request) => request.status === 'PENDING') ?? null;\n const history = requests.data.filter((request) => request.status !== 'PENDING').slice(0, 10);\n const isRequester = open !== null && open.requestedById === currentUser?.id;\n\n const who = (id?: string | null): string =>\n id && id === currentUser?.id ? t('botlit.agentApproval.you', 'you') : (id ?? '—');\n\n const confirm = async (text: string): Promise<void> => {\n if (!dialog) return;\n try {\n if (dialog.kind === 'request') {\n await run({ kind: 'request', agentId, note: text });\n toast.success(t('botlit.agentApproval.toast.requested', 'Activation requested'));\n } else if (dialog.kind === 'approve' && dialog.request) {\n await run({ kind: 'approve', id: dialog.request.id, reason: text });\n toast.success(t('botlit.agentApproval.toast.approved', 'Approved — the agent is live'));\n onChanged?.();\n } else if (dialog.kind === 'reject' && dialog.request) {\n await run({ kind: 'reject', id: dialog.request.id, reason: text });\n toast.success(t('botlit.agentApproval.toast.rejected', 'Activation rejected'));\n }\n setDialog(null);\n await requests.refetch();\n } catch (err: unknown) {\n toast.error(\n err instanceof Error\n ? err.message\n : t('botlit.agentApproval.toast.error', 'Approval action failed')\n );\n }\n };\n\n const cancel = async (request: AgentApprovalRequestRecord): Promise<void> => {\n try {\n await run({ kind: 'cancel', id: request.id });\n toast.success(t('botlit.agentApproval.toast.cancelled', 'Request cancelled'));\n await requests.refetch();\n } catch (err: unknown) {\n toast.error(\n err instanceof Error\n ? err.message\n : t('botlit.agentApproval.toast.error', 'Approval action failed')\n );\n }\n };\n\n return (\n <Card data-testid=\"agent-approval-panel\">\n <CardHeader className=\"flex flex-row items-start justify-between gap-4\">\n <div>\n <CardTitle className=\"flex items-center gap-2\">\n <ShieldCheck size={18} />\n {t('botlit.agentApproval.title', 'Activation approval')}\n <Badge variant={required ? 'warning' : 'outline'}>\n {required\n ? t('botlit.agentApproval.required', 'Required in this workspace')\n : t('botlit.agentApproval.optional', 'Optional')}\n </Badge>\n </CardTitle>\n <CardDescription>\n {required\n ? t(\n 'botlit.agentApproval.descriptionRequired',\n 'This workspace requires a reviewer to approve an agent before it goes live or before a new version is published.'\n )\n : t(\n 'botlit.agentApproval.descriptionOptional',\n 'You can ask a reviewer to check this agent before it goes live. A workspace admin can make review mandatory on the Approvals page.'\n )}\n </CardDescription>\n </div>\n {!open && agentStatus !== 'ARCHIVED' && (\n <Button leftIcon={<Send size={16} />} onClick={() => setDialog({ kind: 'request' })}>\n {t('botlit.agentApproval.requestAction', 'Request activation')}\n </Button>\n )}\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {requests.loading && requests.data.length === 0 ? (\n <div className=\"h-20 bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg\" />\n ) : null}\n\n {open && (\n <div\n className=\"p-4 border border-status-warning-border bg-status-warning-bg rounded-lg space-y-2\"\n data-testid=\"agent-approval-pending\"\n >\n <div className=\"flex items-center gap-2 flex-wrap\">\n <Badge variant=\"warning\">{open.status}</Badge>\n <span className=\"text-sm text-text-secondary\">\n {t('botlit.agentApproval.requestedBy', 'Requested by')} {who(open.requestedById)}{' '}\n {formatRelativeTime(open.createdAt)}\n </span>\n </div>\n {open.requestNote && <p className=\"text-sm break-words\">{open.requestNote}</p>}\n {open.isStale && (\n <p className=\"flex items-center gap-2 text-sm text-status-warning-text\">\n <AlertTriangle size={14} />\n {t(\n 'botlit.agentApproval.stale',\n 'The agent changed after this request. Submit a new request so the reviewer sees the current configuration.'\n )}\n </p>\n )}\n <div className=\"flex gap-2 flex-wrap\">\n {isRequester ? (\n <>\n <Button\n variant=\"outline\"\n size=\"sm\"\n isLoading={pending === 'cancel'}\n onClick={() => void cancel(open)}\n >\n {t('botlit.agentApproval.cancelAction', 'Cancel request')}\n </Button>\n {open.isStale && (\n <Button size=\"sm\" onClick={() => setDialog({ kind: 'request' })}>\n {t('botlit.agentApproval.resubmitAction', 'Submit new request')}\n </Button>\n )}\n </>\n ) : (\n <>\n <Button\n size=\"sm\"\n leftIcon={<CheckCircle2 size={14} />}\n disabled={open.isStale}\n onClick={() => setDialog({ kind: 'approve', request: open })}\n >\n {t('botlit.agentApproval.approveAction', 'Approve')}\n </Button>\n <Button\n size=\"sm\"\n variant=\"destructive\"\n leftIcon={<XCircle size={14} />}\n onClick={() => setDialog({ kind: 'reject', request: open })}\n >\n {t('botlit.agentApproval.rejectAction', 'Reject')}\n </Button>\n </>\n )}\n </div>\n </div>\n )}\n\n {history.length > 0 && (\n <div className=\"space-y-2\">\n <p className=\"text-sm font-medium\">\n {t('botlit.agentApproval.history', 'Decision history')}\n </p>\n <ul className=\"space-y-2\" data-testid=\"agent-approval-history\">\n {history.map((request) => (\n <li\n key={request.id}\n className=\"p-3 border border-border-subtle rounded-lg text-sm space-y-1\"\n >\n <div className=\"flex items-center gap-2 flex-wrap\">\n <Badge variant={approvalStatusVariant(request.status)}>{request.status}</Badge>\n <span className=\"text-text-secondary\">\n {t('botlit.agentApproval.decidedBy', 'by')} {who(request.reviewerId)}{' '}\n {request.decidedAt ? formatRelativeTime(request.decidedAt) : ''}\n </span>\n </div>\n {request.decisionReason && (\n <p className=\"break-words\">{request.decisionReason}</p>\n )}\n </li>\n ))}\n </ul>\n </div>\n )}\n\n {!open && history.length === 0 && !requests.loading && (\n <p className=\"text-sm text-text-secondary\">\n {t('botlit.agentApproval.empty', 'No activation requests for this agent yet.')}\n </p>\n )}\n </CardContent>\n\n <ApprovalDecisionDialog\n kind={dialog?.kind ?? null}\n loading={pending !== null}\n onClose={() => setDialog(null)}\n onConfirm={confirm}\n />\n </Card>\n );\n};\n"],"mappings":"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"}
@@ -0,0 +1,195 @@
1
+ import { useBotlit as e } from "../providers/BotlitProvider.js";
2
+ import { useCallback as t, useEffect as n, useState as r } from "react";
3
+ import { gql as i } from "@apollo/client";
4
+ import { useGatewayClient as a } from "@burdenoff/fe-libs/shared/graphql";
5
+ //#region src/botlit/hooks/useAgentApprovals.ts
6
+ var o = "\n id\n agentId\n versionId\n status\n requestedById\n requestNote\n reviewerId\n decisionReason\n decidedAt\n isStale\n createdAt\n", s = i`
7
+ query BotmanorAgentApprovalSettings {
8
+ botmanorAgentApprovalSettings {
9
+ workspaceId
10
+ requireActivationApproval
11
+ updatedAt
12
+ updatedById
13
+ }
14
+ }
15
+ `, c = i`
16
+ mutation UpdateBotmanorAgentApprovalSettings($requireActivationApproval: Boolean!) {
17
+ updateBotmanorAgentApprovalSettings(requireActivationApproval: $requireActivationApproval) {
18
+ workspaceId
19
+ requireActivationApproval
20
+ updatedAt
21
+ updatedById
22
+ }
23
+ }
24
+ `, l = i`
25
+ query ListBotmanorAgentApprovalRequests(
26
+ $filter: BotmanorAgentApprovalFilterInput
27
+ $pagination: PaginationInput
28
+ ) {
29
+ listBotmanorAgentApprovalRequests(filter: $filter, pagination: $pagination) {
30
+ ${o}
31
+ }
32
+ }
33
+ `, u = i`
34
+ mutation RequestBotmanorAgentActivation($agentId: ID!, $note: String) {
35
+ requestBotmanorAgentActivation(agentId: $agentId, note: $note) {
36
+ ${o}
37
+ }
38
+ }
39
+ `, d = i`
40
+ mutation ApproveBotmanorAgentActivation($id: ID!, $reason: String) {
41
+ approveBotmanorAgentActivation(id: $id, reason: $reason) {
42
+ ${o}
43
+ }
44
+ }
45
+ `, f = i`
46
+ mutation RejectBotmanorAgentActivation($id: ID!, $reason: String!) {
47
+ rejectBotmanorAgentActivation(id: $id, reason: $reason) {
48
+ ${o}
49
+ }
50
+ }
51
+ `, p = i`
52
+ mutation CancelBotmanorAgentActivationRequest($id: ID!) {
53
+ cancelBotmanorAgentActivationRequest(id: $id) {
54
+ ${o}
55
+ }
56
+ }
57
+ `;
58
+ function m(e, t) {
59
+ return e instanceof Error ? e : Error(t);
60
+ }
61
+ function h() {
62
+ let i = a("workspace"), { workspaceId: o } = e(), [l, u] = r(null), [d, f] = r(!1), [p, h] = r(!1), [g, _] = r(), v = t(async () => {
63
+ if (!o) {
64
+ u(null);
65
+ return;
66
+ }
67
+ f(!0), _(void 0);
68
+ try {
69
+ u((await i.query({
70
+ query: s,
71
+ fetchPolicy: "network-only"
72
+ })).data?.botmanorAgentApprovalSettings ?? null);
73
+ } catch (e) {
74
+ _(m(e, "Failed to load approval settings"));
75
+ } finally {
76
+ f(!1);
77
+ }
78
+ }, [i, o]);
79
+ return n(() => {
80
+ v();
81
+ }, [v]), {
82
+ data: l,
83
+ loading: d,
84
+ saving: p,
85
+ error: g,
86
+ refetch: v,
87
+ update: t(async (e) => {
88
+ h(!0);
89
+ try {
90
+ let t = (await i.mutate({
91
+ mutation: c,
92
+ variables: { requireActivationApproval: e }
93
+ })).data?.updateBotmanorAgentApprovalSettings;
94
+ if (!t) throw Error("Failed to update approval settings");
95
+ return u(t), t;
96
+ } catch (e) {
97
+ throw m(e, "Failed to update approval settings");
98
+ } finally {
99
+ h(!1);
100
+ }
101
+ }, [i])
102
+ };
103
+ }
104
+ function g(i) {
105
+ let o = a("workspace"), { workspaceId: s } = e(), [c, u] = r([]), [d, f] = r(!1), [p, h] = r(), { agentId: g, status: _ } = i, v = t(async () => {
106
+ if (!s) {
107
+ u([]);
108
+ return;
109
+ }
110
+ f(!0), h(void 0);
111
+ try {
112
+ u((await o.query({
113
+ query: l,
114
+ variables: {
115
+ filter: {
116
+ agentId: g || void 0,
117
+ status: _ || void 0
118
+ },
119
+ pagination: {
120
+ limit: 50,
121
+ offset: 0
122
+ }
123
+ },
124
+ fetchPolicy: "network-only"
125
+ })).data?.listBotmanorAgentApprovalRequests ?? []);
126
+ } catch (e) {
127
+ h(m(e, "Failed to load activation requests"));
128
+ } finally {
129
+ f(!1);
130
+ }
131
+ }, [
132
+ g,
133
+ o,
134
+ _,
135
+ s
136
+ ]);
137
+ return n(() => {
138
+ v();
139
+ }, [v]), {
140
+ data: c,
141
+ loading: d,
142
+ error: p,
143
+ refetch: v
144
+ };
145
+ }
146
+ function _() {
147
+ let e = a("workspace"), [n, i] = r(null);
148
+ return {
149
+ run: t(async (t) => {
150
+ i(t.kind);
151
+ try {
152
+ switch (t.kind) {
153
+ case "request": return v((await e.mutate({
154
+ mutation: u,
155
+ variables: {
156
+ agentId: t.agentId,
157
+ note: t.note?.trim() || void 0
158
+ }
159
+ })).data?.requestBotmanorAgentActivation, "request");
160
+ case "approve": return v((await e.mutate({
161
+ mutation: d,
162
+ variables: {
163
+ id: t.id,
164
+ reason: t.reason?.trim() || void 0
165
+ }
166
+ })).data?.approveBotmanorAgentActivation, "approve");
167
+ case "reject": return v((await e.mutate({
168
+ mutation: f,
169
+ variables: {
170
+ id: t.id,
171
+ reason: t.reason.trim()
172
+ }
173
+ })).data?.rejectBotmanorAgentActivation, "reject");
174
+ case "cancel": return v((await e.mutate({
175
+ mutation: p,
176
+ variables: { id: t.id }
177
+ })).data?.cancelBotmanorAgentActivationRequest, "cancel");
178
+ }
179
+ } catch (e) {
180
+ throw m(e, `Failed to ${t.kind} activation`);
181
+ } finally {
182
+ i(null);
183
+ }
184
+ }, [e]),
185
+ pending: n
186
+ };
187
+ }
188
+ function v(e, t) {
189
+ if (!e) throw Error(`Failed to ${t} activation`);
190
+ return e;
191
+ }
192
+ //#endregion
193
+ export { _ as useAgentApprovalActions, g as useAgentApprovalRequests, h as useAgentApprovalSettings };
194
+
195
+ //# sourceMappingURL=useAgentApprovals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAgentApprovals.js","names":[],"sources":["../../../src/botlit/hooks/useAgentApprovals.ts"],"sourcesContent":["/**\n * Agent activation approval hooks (BOFF-9682).\n *\n * A workspace can require reviewer approval before an agent goes live. Builders\n * request activation; reviewers holding `botmanor-agent:approve` approve\n * (which publishes the agent's draft and sets it ACTIVE) or reject with a\n * reason. These hooks wrap the `*BotmanorAgentApproval*` fields of\n * wspace-botmanor-svc.\n */\nimport { gql, type ApolloClient } from '@apollo/client';\nimport { useCallback, useEffect, useState } from 'react';\nimport { useGatewayClient } from '@burdenoff/fe-libs/shared/graphql';\n\nimport { useBotlit } from '../providers/BotlitProvider';\n\nconst APPROVAL_REQUEST_FIELDS = `\n id\n agentId\n versionId\n status\n requestedById\n requestNote\n reviewerId\n decisionReason\n decidedAt\n isStale\n createdAt\n`;\n\nconst GET_SETTINGS_DOCUMENT = gql`\n query BotmanorAgentApprovalSettings {\n botmanorAgentApprovalSettings {\n workspaceId\n requireActivationApproval\n updatedAt\n updatedById\n }\n }\n`;\n\nconst UPDATE_SETTINGS_DOCUMENT = gql`\n mutation UpdateBotmanorAgentApprovalSettings($requireActivationApproval: Boolean!) {\n updateBotmanorAgentApprovalSettings(requireActivationApproval: $requireActivationApproval) {\n workspaceId\n requireActivationApproval\n updatedAt\n updatedById\n }\n }\n`;\n\nconst LIST_REQUESTS_DOCUMENT = gql`\n query ListBotmanorAgentApprovalRequests(\n $filter: BotmanorAgentApprovalFilterInput\n $pagination: PaginationInput\n ) {\n listBotmanorAgentApprovalRequests(filter: $filter, pagination: $pagination) {\n ${APPROVAL_REQUEST_FIELDS}\n }\n }\n`;\n\nconst REQUEST_ACTIVATION_DOCUMENT = gql`\n mutation RequestBotmanorAgentActivation($agentId: ID!, $note: String) {\n requestBotmanorAgentActivation(agentId: $agentId, note: $note) {\n ${APPROVAL_REQUEST_FIELDS}\n }\n }\n`;\n\nconst APPROVE_DOCUMENT = gql`\n mutation ApproveBotmanorAgentActivation($id: ID!, $reason: String) {\n approveBotmanorAgentActivation(id: $id, reason: $reason) {\n ${APPROVAL_REQUEST_FIELDS}\n }\n }\n`;\n\nconst REJECT_DOCUMENT = gql`\n mutation RejectBotmanorAgentActivation($id: ID!, $reason: String!) {\n rejectBotmanorAgentActivation(id: $id, reason: $reason) {\n ${APPROVAL_REQUEST_FIELDS}\n }\n }\n`;\n\nconst CANCEL_DOCUMENT = gql`\n mutation CancelBotmanorAgentActivationRequest($id: ID!) {\n cancelBotmanorAgentActivationRequest(id: $id) {\n ${APPROVAL_REQUEST_FIELDS}\n }\n }\n`;\n\nexport type AgentApprovalStatus = 'PENDING' | 'APPROVED' | 'REJECTED' | 'CANCELLED';\n\nexport interface AgentApprovalSettingsRecord {\n workspaceId: string;\n requireActivationApproval: boolean;\n updatedAt?: string | null;\n updatedById?: string | null;\n}\n\nexport interface AgentApprovalRequestRecord {\n id: string;\n agentId: string;\n versionId?: string | null;\n status: AgentApprovalStatus;\n requestedById: string;\n requestNote?: string | null;\n reviewerId?: string | null;\n decisionReason?: string | null;\n decidedAt?: string | null;\n isStale: boolean;\n createdAt: string;\n}\n\nexport interface AgentApprovalFilter {\n agentId?: string;\n status?: AgentApprovalStatus;\n}\n\nfunction toError(error: unknown, fallback: string): Error {\n return error instanceof Error ? error : new Error(fallback);\n}\n\n/** The workspace's approval requirement, with a setter for reviewers. */\nexport function useAgentApprovalSettings() {\n const client = useGatewayClient('workspace') as ApolloClient;\n const { workspaceId } = useBotlit();\n const [data, setData] = useState<AgentApprovalSettingsRecord | null>(null);\n const [loading, setLoading] = useState(false);\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState<Error | undefined>();\n\n const refetch = useCallback(async () => {\n if (!workspaceId) {\n setData(null);\n return;\n }\n setLoading(true);\n setError(undefined);\n try {\n const result = await client.query<{\n botmanorAgentApprovalSettings: AgentApprovalSettingsRecord;\n }>({\n query: GET_SETTINGS_DOCUMENT,\n fetchPolicy: 'network-only',\n });\n setData(result.data?.botmanorAgentApprovalSettings ?? null);\n } catch (queryError) {\n setError(toError(queryError, 'Failed to load approval settings'));\n } finally {\n setLoading(false);\n }\n }, [client, workspaceId]);\n\n useEffect(() => {\n void refetch();\n }, [refetch]);\n\n const update = useCallback(\n async (requireActivationApproval: boolean) => {\n setSaving(true);\n try {\n const result = await client.mutate<{\n updateBotmanorAgentApprovalSettings: AgentApprovalSettingsRecord;\n }>({\n mutation: UPDATE_SETTINGS_DOCUMENT,\n variables: { requireActivationApproval },\n });\n const next = result.data?.updateBotmanorAgentApprovalSettings;\n if (!next) throw new Error('Failed to update approval settings');\n setData(next);\n return next;\n } catch (mutationError) {\n throw toError(mutationError, 'Failed to update approval settings');\n } finally {\n setSaving(false);\n }\n },\n [client]\n );\n\n return { data, loading, saving, error, refetch, update };\n}\n\n/** Activation requests in the workspace, newest first. */\nexport function useAgentApprovalRequests(filter: AgentApprovalFilter) {\n const client = useGatewayClient('workspace') as ApolloClient;\n const { workspaceId } = useBotlit();\n const [data, setData] = useState<AgentApprovalRequestRecord[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | undefined>();\n const { agentId, status } = filter;\n\n const refetch = useCallback(async () => {\n if (!workspaceId) {\n setData([]);\n return;\n }\n setLoading(true);\n setError(undefined);\n try {\n const result = await client.query<{\n listBotmanorAgentApprovalRequests: AgentApprovalRequestRecord[];\n }>({\n query: LIST_REQUESTS_DOCUMENT,\n variables: {\n filter: { agentId: agentId || undefined, status: status || undefined },\n pagination: { limit: 50, offset: 0 },\n },\n fetchPolicy: 'network-only',\n });\n setData(result.data?.listBotmanorAgentApprovalRequests ?? []);\n } catch (queryError) {\n setError(toError(queryError, 'Failed to load activation requests'));\n } finally {\n setLoading(false);\n }\n }, [agentId, client, status, workspaceId]);\n\n useEffect(() => {\n void refetch();\n }, [refetch]);\n\n return { data, loading, error, refetch };\n}\n\ntype ApprovalAction =\n | { kind: 'request'; agentId: string; note?: string }\n | { kind: 'approve'; id: string; reason?: string }\n | { kind: 'reject'; id: string; reason: string }\n | { kind: 'cancel'; id: string };\n\n/** Request, approve, reject or cancel an activation request. */\nexport function useAgentApprovalActions() {\n const client = useGatewayClient('workspace') as ApolloClient;\n const [pending, setPending] = useState<ApprovalAction['kind'] | null>(null);\n\n const run = useCallback(\n async (action: ApprovalAction): Promise<AgentApprovalRequestRecord> => {\n setPending(action.kind);\n try {\n switch (action.kind) {\n case 'request': {\n const result = await client.mutate<{\n requestBotmanorAgentActivation: AgentApprovalRequestRecord;\n }>({\n mutation: REQUEST_ACTIVATION_DOCUMENT,\n variables: { agentId: action.agentId, note: action.note?.trim() || undefined },\n });\n return required(result.data?.requestBotmanorAgentActivation, 'request');\n }\n case 'approve': {\n const result = await client.mutate<{\n approveBotmanorAgentActivation: AgentApprovalRequestRecord;\n }>({\n mutation: APPROVE_DOCUMENT,\n variables: { id: action.id, reason: action.reason?.trim() || undefined },\n });\n return required(result.data?.approveBotmanorAgentActivation, 'approve');\n }\n case 'reject': {\n const result = await client.mutate<{\n rejectBotmanorAgentActivation: AgentApprovalRequestRecord;\n }>({\n mutation: REJECT_DOCUMENT,\n variables: { id: action.id, reason: action.reason.trim() },\n });\n return required(result.data?.rejectBotmanorAgentActivation, 'reject');\n }\n case 'cancel': {\n const result = await client.mutate<{\n cancelBotmanorAgentActivationRequest: AgentApprovalRequestRecord;\n }>({\n mutation: CANCEL_DOCUMENT,\n variables: { id: action.id },\n });\n return required(result.data?.cancelBotmanorAgentActivationRequest, 'cancel');\n }\n }\n } catch (mutationError) {\n throw toError(mutationError, `Failed to ${action.kind} activation`);\n } finally {\n setPending(null);\n }\n },\n [client]\n );\n\n return { run, pending };\n}\n\nfunction required(\n value: AgentApprovalRequestRecord | undefined,\n kind: ApprovalAction['kind']\n): AgentApprovalRequestRecord {\n if (!value) throw new Error(`Failed to ${kind} activation`);\n return value;\n}\n"],"mappings":"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"}
@@ -0,0 +1,205 @@
1
+ import { useTr as e } from "../../shared/hooks/useTr.js";
2
+ import { useBotlit as t } from "../providers/BotlitProvider.js";
3
+ import { useAgents as n } from "../hooks/useAgents.js";
4
+ import { formatRelativeTime as r } from "../utils/formatters.js";
5
+ import { PageLayout as i } from "../components/PageLayout.js";
6
+ import { Card as a, CardContent as o, CardDescription as s, CardHeader as c, CardTitle as l } from "../components/Card.js";
7
+ import { Button as u } from "../components/Button.js";
8
+ import { Badge as d } from "../components/Badge.js";
9
+ import { ErrorState as f } from "../components/ErrorState.js";
10
+ import { useAgentApprovalActions as p, useAgentApprovalRequests as m, useAgentApprovalSettings as h } from "../hooks/useAgentApprovals.js";
11
+ import { ApprovalDecisionDialog as g, approvalStatusVariant as _ } from "../components/AgentApprovalPanel.js";
12
+ import { useMemo as v, useState as y } from "react";
13
+ import { PagePurpose as b } from "@burdenoff/fe-libs/ui";
14
+ import { jsx as x, jsxs as S } from "react/jsx-runtime";
15
+ import { AlertTriangle as C, ArrowLeft as w, CheckCircle2 as T, ShieldCheck as E, XCircle as D } from "lucide-react";
16
+ import { toast as O } from "sonner";
17
+ //#region src/botlit/pages/AgentApprovalsPage.tsx
18
+ var k = () => {
19
+ let k = e(), { navigateTo: A, currentUser: j } = t(), M = h(), N = m({ status: "PENDING" }), P = m({}), { data: F } = n(), { run: I, pending: L } = p(), [R, z] = y(null), B = v(() => new Map(F.map((e) => [e.id, e.name])), [F]), V = P.data.filter((e) => e.status !== "PENDING").slice(0, 20), H = M.data?.requireActivationApproval === !0, U = (e) => e && e === j?.id ? k("botlit.agentApproval.you", "you") : e ?? "—", W = async () => {
20
+ await Promise.all([N.refetch(), P.refetch()]);
21
+ }, G = async () => {
22
+ try {
23
+ let e = await M.update(!H);
24
+ O.success(e.requireActivationApproval ? k("botlit.agentApprovals.toast.enabled", "Agents now need approval before going live") : k("botlit.agentApprovals.toast.disabled", "Activation approval is now optional"));
25
+ } catch (e) {
26
+ O.error(e instanceof Error ? e.message : k("botlit.agentApprovals.toast.settingsError", "Failed to update the setting"));
27
+ }
28
+ };
29
+ return /* @__PURE__ */ S(i, {
30
+ title: /* @__PURE__ */ S("div", {
31
+ className: "flex items-center gap-3",
32
+ children: [/* @__PURE__ */ x(u, {
33
+ variant: "ghost",
34
+ size: "icon",
35
+ onClick: () => A("agents"),
36
+ children: /* @__PURE__ */ x(w, { size: 20 })
37
+ }), /* @__PURE__ */ x("span", { children: k("botlit.agentApprovals.title", "Agent approvals") })]
38
+ }),
39
+ description: k("botlit.agentApprovals.description", "Review requests to take agents live"),
40
+ children: [/* @__PURE__ */ S("div", {
41
+ className: "space-y-6",
42
+ children: [
43
+ /* @__PURE__ */ x(b, {
44
+ icon: /* @__PURE__ */ x(E, { size: 18 }),
45
+ children: k("botlit.agentApprovals.purpose", "A reviewer checks an agent's prompt, model, tools and limits before it goes live. The person who requested activation cannot approve their own request, and every decision is kept in the history below.")
46
+ }),
47
+ /* @__PURE__ */ x(a, {
48
+ "data-testid": "agent-approvals-settings",
49
+ children: /* @__PURE__ */ S(c, {
50
+ className: "flex flex-row items-start justify-between gap-4",
51
+ children: [/* @__PURE__ */ S("div", { children: [/* @__PURE__ */ S(l, {
52
+ className: "flex items-center gap-2",
53
+ children: [k("botlit.agentApprovals.settings.title", "Require approval before agents go live"), /* @__PURE__ */ x(d, {
54
+ variant: H ? "warning" : "outline",
55
+ children: H ? k("botlit.agentApprovals.settings.on", "On") : k("botlit.agentApprovals.settings.off", "Off")
56
+ })]
57
+ }), /* @__PURE__ */ x(s, { children: k("botlit.agentApprovals.settings.description", "When on, activating an agent, publishing a new version, or editing the prompt, model or tools of a live agent needs an approved request. Rolling a live agent back to an earlier version stays available for emergencies.") })] }), /* @__PURE__ */ x(u, {
58
+ variant: H ? "outline" : "default",
59
+ isLoading: M.saving,
60
+ disabled: M.loading || !M.data,
61
+ onClick: () => void G(),
62
+ children: H ? k("botlit.agentApprovals.settings.turnOff", "Turn off") : k("botlit.agentApprovals.settings.turnOn", "Turn on")
63
+ })]
64
+ })
65
+ }),
66
+ /* @__PURE__ */ S(a, { children: [/* @__PURE__ */ x(c, { children: /* @__PURE__ */ x(l, { children: k("botlit.agentApprovals.pending.title", "Waiting for review") }) }), /* @__PURE__ */ x(o, { children: N.error ? /* @__PURE__ */ x(f, {
67
+ resource: k("botlit.agentApprovals.resource", "activation requests"),
68
+ error: N.error,
69
+ onRetry: () => void N.refetch()
70
+ }) : N.loading && N.data.length === 0 ? /* @__PURE__ */ x("div", { className: "h-24 bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg" }) : N.data.length === 0 ? /* @__PURE__ */ x("p", {
71
+ className: "text-sm text-text-secondary",
72
+ children: k("botlit.agentApprovals.pending.empty", "No agents are waiting for review.")
73
+ }) : /* @__PURE__ */ x("ul", {
74
+ className: "space-y-3",
75
+ "data-testid": "agent-approvals-pending",
76
+ children: N.data.map((e) => {
77
+ let t = e.requestedById === j?.id;
78
+ return /* @__PURE__ */ S("li", {
79
+ className: "p-4 border border-border-subtle rounded-lg space-y-2",
80
+ children: [
81
+ /* @__PURE__ */ S("div", {
82
+ className: "flex items-center gap-2 flex-wrap",
83
+ children: [
84
+ /* @__PURE__ */ x("button", {
85
+ type: "button",
86
+ className: "font-medium text-primary hover:underline",
87
+ onClick: () => A(`agents/${e.agentId}`),
88
+ children: B.get(e.agentId) ?? e.agentId
89
+ }),
90
+ /* @__PURE__ */ x(d, {
91
+ variant: "warning",
92
+ children: e.status
93
+ }),
94
+ /* @__PURE__ */ S("span", {
95
+ className: "text-sm text-text-secondary",
96
+ children: [
97
+ k("botlit.agentApproval.requestedBy", "Requested by"),
98
+ " ",
99
+ U(e.requestedById),
100
+ " ",
101
+ r(e.createdAt)
102
+ ]
103
+ })
104
+ ]
105
+ }),
106
+ e.requestNote && /* @__PURE__ */ x("p", {
107
+ className: "text-sm break-words",
108
+ children: e.requestNote
109
+ }),
110
+ e.isStale && /* @__PURE__ */ S("p", {
111
+ className: "flex items-center gap-2 text-sm text-status-warning-text",
112
+ children: [/* @__PURE__ */ x(C, { size: 14 }), k("botlit.agentApproval.stale", "The agent changed after this request. Submit a new request so the reviewer sees the current configuration.")]
113
+ }),
114
+ t ? /* @__PURE__ */ x("p", {
115
+ className: "text-xs text-text-secondary",
116
+ children: k("botlit.agentApprovals.ownRequest", "You requested this, so another reviewer has to decide it.")
117
+ }) : /* @__PURE__ */ S("div", {
118
+ className: "flex gap-2",
119
+ children: [/* @__PURE__ */ x(u, {
120
+ size: "sm",
121
+ leftIcon: /* @__PURE__ */ x(T, { size: 14 }),
122
+ disabled: e.isStale,
123
+ onClick: () => z({
124
+ kind: "approve",
125
+ request: e
126
+ }),
127
+ children: k("botlit.agentApproval.approveAction", "Approve")
128
+ }), /* @__PURE__ */ x(u, {
129
+ size: "sm",
130
+ variant: "destructive",
131
+ leftIcon: /* @__PURE__ */ x(D, { size: 14 }),
132
+ onClick: () => z({
133
+ kind: "reject",
134
+ request: e
135
+ }),
136
+ children: k("botlit.agentApproval.rejectAction", "Reject")
137
+ })]
138
+ })
139
+ ]
140
+ }, e.id);
141
+ })
142
+ }) })] }),
143
+ /* @__PURE__ */ S(a, { children: [/* @__PURE__ */ x(c, { children: /* @__PURE__ */ x(l, { children: k("botlit.agentApprovals.history.title", "Recent decisions") }) }), /* @__PURE__ */ x(o, { children: V.length === 0 ? /* @__PURE__ */ x("p", {
144
+ className: "text-sm text-text-secondary",
145
+ children: k("botlit.agentApprovals.history.empty", "No decisions yet.")
146
+ }) : /* @__PURE__ */ x("ul", {
147
+ className: "space-y-2",
148
+ "data-testid": "agent-approvals-history",
149
+ children: V.map((e) => /* @__PURE__ */ S("li", {
150
+ className: "p-3 border border-border-subtle rounded-lg text-sm space-y-1",
151
+ children: [/* @__PURE__ */ S("div", {
152
+ className: "flex items-center gap-2 flex-wrap",
153
+ children: [
154
+ /* @__PURE__ */ x("span", {
155
+ className: "font-medium",
156
+ children: B.get(e.agentId) ?? e.agentId
157
+ }),
158
+ /* @__PURE__ */ x(d, {
159
+ variant: _(e.status),
160
+ children: e.status
161
+ }),
162
+ /* @__PURE__ */ S("span", {
163
+ className: "text-text-secondary",
164
+ children: [
165
+ k("botlit.agentApproval.decidedBy", "by"),
166
+ " ",
167
+ U(e.reviewerId),
168
+ " ",
169
+ e.decidedAt ? r(e.decidedAt) : ""
170
+ ]
171
+ })
172
+ ]
173
+ }), e.decisionReason && /* @__PURE__ */ x("p", {
174
+ className: "break-words",
175
+ children: e.decisionReason
176
+ })]
177
+ }, e.id))
178
+ }) })] })
179
+ ]
180
+ }), /* @__PURE__ */ x(g, {
181
+ kind: R?.kind ?? null,
182
+ loading: L !== null,
183
+ onClose: () => z(null),
184
+ onConfirm: async (e) => {
185
+ if (R) try {
186
+ R.kind === "approve" ? (await I({
187
+ kind: "approve",
188
+ id: R.request.id,
189
+ reason: e
190
+ }), O.success(k("botlit.agentApproval.toast.approved", "Approved — the agent is live"))) : (await I({
191
+ kind: "reject",
192
+ id: R.request.id,
193
+ reason: e
194
+ }), O.success(k("botlit.agentApproval.toast.rejected", "Activation rejected"))), z(null), await W();
195
+ } catch (e) {
196
+ O.error(e instanceof Error ? e.message : k("botlit.agentApproval.toast.error", "Approval action failed"));
197
+ }
198
+ }
199
+ })]
200
+ });
201
+ };
202
+ //#endregion
203
+ export { k as AgentApprovalsPage };
204
+
205
+ //# sourceMappingURL=AgentApprovalsPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentApprovalsPage.js","names":[],"sources":["../../../src/botlit/pages/AgentApprovalsPage.tsx"],"sourcesContent":["/**\n * Agent Activation Approvals Page (BOFF-9682)\n * @module botlit/pages\n *\n * Reviewer queue for the workspace: every pending request to take an agent\n * live, with approve / reject, the recent decisions, and the workspace switch\n * that makes review mandatory. Approving publishes the agent's draft and sets\n * it ACTIVE; a request whose agent changed after it was submitted is flagged\n * stale and cannot be approved.\n */\n\nimport { useMemo, useState, type FC } from 'react';\nimport { AlertTriangle, ArrowLeft, CheckCircle2, ShieldCheck, XCircle } from 'lucide-react';\nimport { toast } from 'sonner';\nimport { PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { PageLayout } from '../components/PageLayout';\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../components/Card';\nimport { Button } from '../components/Button';\nimport { Badge } from '../components/Badge';\nimport { ErrorState } from '../components/ErrorState';\nimport { ApprovalDecisionDialog, approvalStatusVariant } from '../components/AgentApprovalPanel';\nimport { useAgents } from '../hooks/useAgents';\nimport {\n useAgentApprovalActions,\n useAgentApprovalRequests,\n useAgentApprovalSettings,\n type AgentApprovalRequestRecord,\n} from '../hooks/useAgentApprovals';\nimport { useBotlit } from '../providers/BotlitProvider';\nimport { formatRelativeTime } from '../utils/formatters';\n\nexport const AgentApprovalsPage: FC = () => {\n const t = useTr();\n const { navigateTo, currentUser } = useBotlit();\n const settings = useAgentApprovalSettings();\n const pendingRequests = useAgentApprovalRequests({ status: 'PENDING' });\n const allRequests = useAgentApprovalRequests({});\n const { data: agents } = useAgents();\n const { run, pending } = useAgentApprovalActions();\n const [dialog, setDialog] = useState<{\n kind: 'approve' | 'reject';\n request: AgentApprovalRequestRecord;\n } | null>(null);\n\n const agentNames = useMemo(\n () => new Map(agents.map((agent) => [agent.id, agent.name])),\n [agents]\n );\n const decided = allRequests.data.filter((request) => request.status !== 'PENDING').slice(0, 20);\n const required = settings.data?.requireActivationApproval === true;\n const who = (id?: string | null): string =>\n id && id === currentUser?.id ? t('botlit.agentApproval.you', 'you') : (id ?? '—');\n\n const refreshAll = async (): Promise<void> => {\n await Promise.all([pendingRequests.refetch(), allRequests.refetch()]);\n };\n\n const toggleRequired = async (): Promise<void> => {\n try {\n const next = await settings.update(!required);\n toast.success(\n next.requireActivationApproval\n ? t('botlit.agentApprovals.toast.enabled', 'Agents now need approval before going live')\n : t('botlit.agentApprovals.toast.disabled', 'Activation approval is now optional')\n );\n } catch (err: unknown) {\n toast.error(\n err instanceof Error\n ? err.message\n : t('botlit.agentApprovals.toast.settingsError', 'Failed to update the setting')\n );\n }\n };\n\n const confirm = async (text: string): Promise<void> => {\n if (!dialog) return;\n try {\n if (dialog.kind === 'approve') {\n await run({ kind: 'approve', id: dialog.request.id, reason: text });\n toast.success(t('botlit.agentApproval.toast.approved', 'Approved — the agent is live'));\n } else {\n await run({ kind: 'reject', id: dialog.request.id, reason: text });\n toast.success(t('botlit.agentApproval.toast.rejected', 'Activation rejected'));\n }\n setDialog(null);\n await refreshAll();\n } catch (err: unknown) {\n toast.error(\n err instanceof Error\n ? err.message\n : t('botlit.agentApproval.toast.error', 'Approval action failed')\n );\n }\n };\n\n return (\n <PageLayout\n title={\n <div className=\"flex items-center gap-3\">\n <Button variant=\"ghost\" size=\"icon\" onClick={() => navigateTo('agents')}>\n <ArrowLeft size={20} />\n </Button>\n <span>{t('botlit.agentApprovals.title', 'Agent approvals')}</span>\n </div>\n }\n description={t('botlit.agentApprovals.description', 'Review requests to take agents live')}\n >\n <div className=\"space-y-6\">\n <PagePurpose icon={<ShieldCheck size={18} />}>\n {t(\n 'botlit.agentApprovals.purpose',\n \"A reviewer checks an agent's prompt, model, tools and limits before it goes live. The person who requested activation cannot approve their own request, and every decision is kept in the history below.\"\n )}\n </PagePurpose>\n\n <Card data-testid=\"agent-approvals-settings\">\n <CardHeader className=\"flex flex-row items-start justify-between gap-4\">\n <div>\n <CardTitle className=\"flex items-center gap-2\">\n {t(\n 'botlit.agentApprovals.settings.title',\n 'Require approval before agents go live'\n )}\n <Badge variant={required ? 'warning' : 'outline'}>\n {required\n ? t('botlit.agentApprovals.settings.on', 'On')\n : t('botlit.agentApprovals.settings.off', 'Off')}\n </Badge>\n </CardTitle>\n <CardDescription>\n {t(\n 'botlit.agentApprovals.settings.description',\n 'When on, activating an agent, publishing a new version, or editing the prompt, model or tools of a live agent needs an approved request. Rolling a live agent back to an earlier version stays available for emergencies.'\n )}\n </CardDescription>\n </div>\n <Button\n variant={required ? 'outline' : 'default'}\n isLoading={settings.saving}\n disabled={settings.loading || !settings.data}\n onClick={() => void toggleRequired()}\n >\n {required\n ? t('botlit.agentApprovals.settings.turnOff', 'Turn off')\n : t('botlit.agentApprovals.settings.turnOn', 'Turn on')}\n </Button>\n </CardHeader>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>{t('botlit.agentApprovals.pending.title', 'Waiting for review')}</CardTitle>\n </CardHeader>\n <CardContent>\n {pendingRequests.error ? (\n <ErrorState\n resource={t('botlit.agentApprovals.resource', 'activation requests')}\n error={pendingRequests.error}\n onRetry={() => void pendingRequests.refetch()}\n />\n ) : pendingRequests.loading && pendingRequests.data.length === 0 ? (\n <div className=\"h-24 bg-bg-sunken motion-safe:animate-vc-shimmer rounded-lg\" />\n ) : pendingRequests.data.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n {t('botlit.agentApprovals.pending.empty', 'No agents are waiting for review.')}\n </p>\n ) : (\n <ul className=\"space-y-3\" data-testid=\"agent-approvals-pending\">\n {pendingRequests.data.map((request) => {\n const own = request.requestedById === currentUser?.id;\n return (\n <li\n key={request.id}\n className=\"p-4 border border-border-subtle rounded-lg space-y-2\"\n >\n <div className=\"flex items-center gap-2 flex-wrap\">\n <button\n type=\"button\"\n className=\"font-medium text-primary hover:underline\"\n onClick={() => navigateTo(`agents/${request.agentId}`)}\n >\n {agentNames.get(request.agentId) ?? request.agentId}\n </button>\n <Badge variant=\"warning\">{request.status}</Badge>\n <span className=\"text-sm text-text-secondary\">\n {t('botlit.agentApproval.requestedBy', 'Requested by')}{' '}\n {who(request.requestedById)} {formatRelativeTime(request.createdAt)}\n </span>\n </div>\n {request.requestNote && (\n <p className=\"text-sm break-words\">{request.requestNote}</p>\n )}\n {request.isStale && (\n <p className=\"flex items-center gap-2 text-sm text-status-warning-text\">\n <AlertTriangle size={14} />\n {t(\n 'botlit.agentApproval.stale',\n 'The agent changed after this request. Submit a new request so the reviewer sees the current configuration.'\n )}\n </p>\n )}\n {own ? (\n <p className=\"text-xs text-text-secondary\">\n {t(\n 'botlit.agentApprovals.ownRequest',\n 'You requested this, so another reviewer has to decide it.'\n )}\n </p>\n ) : (\n <div className=\"flex gap-2\">\n <Button\n size=\"sm\"\n leftIcon={<CheckCircle2 size={14} />}\n disabled={request.isStale}\n onClick={() => setDialog({ kind: 'approve', request })}\n >\n {t('botlit.agentApproval.approveAction', 'Approve')}\n </Button>\n <Button\n size=\"sm\"\n variant=\"destructive\"\n leftIcon={<XCircle size={14} />}\n onClick={() => setDialog({ kind: 'reject', request })}\n >\n {t('botlit.agentApproval.rejectAction', 'Reject')}\n </Button>\n </div>\n )}\n </li>\n );\n })}\n </ul>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>{t('botlit.agentApprovals.history.title', 'Recent decisions')}</CardTitle>\n </CardHeader>\n <CardContent>\n {decided.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n {t('botlit.agentApprovals.history.empty', 'No decisions yet.')}\n </p>\n ) : (\n <ul className=\"space-y-2\" data-testid=\"agent-approvals-history\">\n {decided.map((request) => (\n <li\n key={request.id}\n className=\"p-3 border border-border-subtle rounded-lg text-sm space-y-1\"\n >\n <div className=\"flex items-center gap-2 flex-wrap\">\n <span className=\"font-medium\">\n {agentNames.get(request.agentId) ?? request.agentId}\n </span>\n <Badge variant={approvalStatusVariant(request.status)}>\n {request.status}\n </Badge>\n <span className=\"text-text-secondary\">\n {t('botlit.agentApproval.decidedBy', 'by')} {who(request.reviewerId)}{' '}\n {request.decidedAt ? formatRelativeTime(request.decidedAt) : ''}\n </span>\n </div>\n {request.decisionReason && (\n <p className=\"break-words\">{request.decisionReason}</p>\n )}\n </li>\n ))}\n </ul>\n )}\n </CardContent>\n </Card>\n </div>\n\n <ApprovalDecisionDialog\n kind={dialog?.kind ?? null}\n loading={pending !== null}\n onClose={() => setDialog(null)}\n onConfirm={confirm}\n />\n </PageLayout>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;AAgCA,IAAa,UAA+B;CAC1C,IAAM,IAAI,GAAO,EACX,EAAE,eAAY,mBAAgB,GAAW,EACzC,IAAW,GAA0B,EACrC,IAAkB,EAAyB,EAAE,QAAQ,WAAW,CAAC,EACjE,IAAc,EAAyB,EAAE,CAAC,EAC1C,EAAE,MAAM,MAAW,GAAW,EAC9B,EAAE,QAAK,eAAY,GAAyB,EAC5C,CAAC,GAAQ,KAAa,EAGlB,KAAK,EAET,IAAa,QACX,IAAI,IAAI,EAAO,KAAK,MAAU,CAAC,EAAM,IAAI,EAAM,KAAK,CAAC,CAAC,EAC5D,CAAC,EAAO,CACT,EACK,IAAU,EAAY,KAAK,QAAQ,MAAY,EAAQ,WAAW,UAAU,CAAC,MAAM,GAAG,GAAG,EACzF,IAAW,EAAS,MAAM,8BAA8B,IACxD,KAAO,MACX,KAAM,MAAO,GAAa,KAAK,EAAE,4BAA4B,MAAM,GAAI,KAAM,KAEzE,IAAa,YAA2B;AAC5C,QAAM,QAAQ,IAAI,CAAC,EAAgB,SAAS,EAAE,EAAY,SAAS,CAAC,CAAC;IAGjE,IAAiB,YAA2B;AAChD,MAAI;GACF,IAAM,IAAO,MAAM,EAAS,OAAO,CAAC,EAAS;AAC7C,KAAM,QACJ,EAAK,4BACD,EAAE,uCAAuC,6CAA6C,GACtF,EAAE,wCAAwC,sCAAsC,CACrF;WACM,GAAc;AACrB,KAAM,MACJ,aAAe,QACX,EAAI,UACJ,EAAE,6CAA6C,+BAA+B,CACnF;;;AAyBL,QACE,kBAAC,GAAD;EACE,OACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,GAAD;IAAQ,SAAQ;IAAQ,MAAK;IAAO,eAAe,EAAW,SAAS;cACrE,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA;IAChB,CAAA,EACT,kBAAC,QAAD,EAAA,UAAO,EAAE,+BAA+B,kBAAkB,EAAQ,CAAA,CAC9D;;EAER,aAAa,EAAE,qCAAqC,sCAAsC;YAT5F,CAWE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KAAa,MAAM,kBAAC,GAAD,EAAa,MAAM,IAAM,CAAA;eACzC,EACC,iCACA,2MACD;KACW,CAAA;IAEd,kBAAC,GAAD;KAAM,eAAY;eAChB,kBAAC,GAAD;MAAY,WAAU;gBAAtB,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,GAAD;OAAW,WAAU;iBAArB,CACG,EACC,wCACA,yCACD,EACD,kBAAC,GAAD;QAAO,SAAS,IAAW,YAAY;kBACpC,IACG,EAAE,qCAAqC,KAAK,GAC5C,EAAE,sCAAsC,MAAM;QAC5C,CAAA,CACE;UACZ,kBAAC,GAAD,EAAA,UACG,EACC,8CACA,4NACD,EACe,CAAA,CACd,EAAA,CAAA,EACN,kBAAC,GAAD;OACE,SAAS,IAAW,YAAY;OAChC,WAAW,EAAS;OACpB,UAAU,EAAS,WAAW,CAAC,EAAS;OACxC,eAAe,KAAK,GAAgB;iBAEnC,IACG,EAAE,0CAA0C,WAAW,GACvD,EAAE,yCAAyC,UAAU;OAClD,CAAA,CACE;;KACR,CAAA;IAEP,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAY,EAAE,uCAAuC,qBAAqB,EAAa,CAAA,EAC5E,CAAA,EACb,kBAAC,GAAD,EAAA,UACG,EAAgB,QACf,kBAAC,GAAD;KACE,UAAU,EAAE,kCAAkC,sBAAsB;KACpE,OAAO,EAAgB;KACvB,eAAe,KAAK,EAAgB,SAAS;KAC7C,CAAA,GACA,EAAgB,WAAW,EAAgB,KAAK,WAAW,IAC7D,kBAAC,OAAD,EAAK,WAAU,+DAAgE,CAAA,GAC7E,EAAgB,KAAK,WAAW,IAClC,kBAAC,KAAD;KAAG,WAAU;eACV,EAAE,uCAAuC,oCAAoC;KAC5E,CAAA,GAEJ,kBAAC,MAAD;KAAI,WAAU;KAAY,eAAY;eACnC,EAAgB,KAAK,KAAK,MAAY;MACrC,IAAM,IAAM,EAAQ,kBAAkB,GAAa;AACnD,aACE,kBAAC,MAAD;OAEE,WAAU;iBAFZ;QAIE,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,UAAD;WACE,MAAK;WACL,WAAU;WACV,eAAe,EAAW,UAAU,EAAQ,UAAU;qBAErD,EAAW,IAAI,EAAQ,QAAQ,IAAI,EAAQ;WACrC,CAAA;UACT,kBAAC,GAAD;WAAO,SAAQ;qBAAW,EAAQ;WAAe,CAAA;UACjD,kBAAC,QAAD;WAAM,WAAU;qBAAhB;YACG,EAAE,oCAAoC,eAAe;YAAE;YACvD,EAAI,EAAQ,cAAc;YAAC;YAAE,EAAmB,EAAQ,UAAU;YAC9D;;UACH;;QACL,EAAQ,eACP,kBAAC,KAAD;SAAG,WAAU;mBAAuB,EAAQ;SAAgB,CAAA;QAE7D,EAAQ,WACP,kBAAC,KAAD;SAAG,WAAU;mBAAb,CACE,kBAAC,GAAD,EAAe,MAAM,IAAM,CAAA,EAC1B,EACC,8BACA,6GACD,CACC;;QAEL,IACC,kBAAC,KAAD;SAAG,WAAU;mBACV,EACC,oCACA,4DACD;SACC,CAAA,GAEJ,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UACE,MAAK;UACL,UAAU,kBAAC,GAAD,EAAc,MAAM,IAAM,CAAA;UACpC,UAAU,EAAQ;UAClB,eAAe,EAAU;WAAE,MAAM;WAAW;WAAS,CAAC;oBAErD,EAAE,sCAAsC,UAAU;UAC5C,CAAA,EACT,kBAAC,GAAD;UACE,MAAK;UACL,SAAQ;UACR,UAAU,kBAAC,GAAD,EAAS,MAAM,IAAM,CAAA;UAC/B,eAAe,EAAU;WAAE,MAAM;WAAU;WAAS,CAAC;oBAEpD,EAAE,qCAAqC,SAAS;UAC1C,CAAA,CACL;;QAEL;SAxDE,EAAQ,GAwDV;OAEP;KACC,CAAA,EAEK,CAAA,CACT,EAAA,CAAA;IAEP,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAY,EAAE,uCAAuC,mBAAmB,EAAa,CAAA,EAC1E,CAAA,EACb,kBAAC,GAAD,EAAA,UACG,EAAQ,WAAW,IAClB,kBAAC,KAAD;KAAG,WAAU;eACV,EAAE,uCAAuC,oBAAoB;KAC5D,CAAA,GAEJ,kBAAC,MAAD;KAAI,WAAU;KAAY,eAAY;eACnC,EAAQ,KAAK,MACZ,kBAAC,MAAD;MAEE,WAAU;gBAFZ,CAIE,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAW,IAAI,EAAQ,QAAQ,IAAI,EAAQ;SACvC,CAAA;QACP,kBAAC,GAAD;SAAO,SAAS,EAAsB,EAAQ,OAAO;mBAClD,EAAQ;SACH,CAAA;QACR,kBAAC,QAAD;SAAM,WAAU;mBAAhB;UACG,EAAE,kCAAkC,KAAK;UAAC;UAAE,EAAI,EAAQ,WAAW;UAAE;UACrE,EAAQ,YAAY,EAAmB,EAAQ,UAAU,GAAG;UACxD;;QACH;UACL,EAAQ,kBACP,kBAAC,KAAD;OAAG,WAAU;iBAAe,EAAQ;OAAmB,CAAA,CAEtD;QAlBE,EAAQ,GAkBV,CACL;KACC,CAAA,EAEK,CAAA,CACT,EAAA,CAAA;IACH;MAEN,kBAAC,GAAD;GACE,MAAM,GAAQ,QAAQ;GACtB,SAAS,MAAY;GACrB,eAAe,EAAU,KAAK;GAC9B,WA7MU,OAAO,MAAgC;AAChD,UACL,KAAI;AASF,KARI,EAAO,SAAS,aAClB,MAAM,EAAI;MAAE,MAAM;MAAW,IAAI,EAAO,QAAQ;MAAI,QAAQ;MAAM,CAAC,EACnE,EAAM,QAAQ,EAAE,uCAAuC,+BAA+B,CAAC,KAEvF,MAAM,EAAI;MAAE,MAAM;MAAU,IAAI,EAAO,QAAQ;MAAI,QAAQ;MAAM,CAAC,EAClE,EAAM,QAAQ,EAAE,uCAAuC,sBAAsB,CAAC,GAEhF,EAAU,KAAK,EACf,MAAM,GAAY;aACX,GAAc;AACrB,OAAM,MACJ,aAAe,QACX,EAAI,UACJ,EAAE,oCAAoC,yBAAyB,CACpE;;;GA6LC,CAAA,CACS"}