@burdenoff/microfe-botmanor 2026.1106.1 → 2026.1108.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.
- package/dist/botlit/BotlitRoutes.js +34 -30
- package/dist/botlit/BotlitRoutes.js.map +1 -1
- package/dist/botlit/components/AiOperationsPanel.js +254 -0
- package/dist/botlit/components/AiOperationsPanel.js.map +1 -0
- package/dist/botlit/components/Input.js +1 -1
- package/dist/botlit/hooks/index.js +1 -0
- package/dist/botlit/hooks/useAiOperations.js +153 -0
- package/dist/botlit/hooks/useAiOperations.js.map +1 -0
- package/dist/botlit/pages/AgentDetailPage.js +198 -185
- package/dist/botlit/pages/AgentDetailPage.js.map +1 -1
- package/dist/botlit/pages/AiOperationsPage.js +166 -0
- package/dist/botlit/pages/AiOperationsPage.js.map +1 -0
- package/dist/botlit/pages/BotDetailPage.js +282 -268
- package/dist/botlit/pages/BotDetailPage.js.map +1 -1
- package/dist/generated/wspace-operations.js +159 -66
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/generated/wspace-types.js +5 -3
- package/dist/generated/wspace-types.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AiOperationsPanel.js","names":[],"sources":["../../../src/botlit/components/AiOperationsPanel.tsx"],"sourcesContent":["/**\n * AI Operations Panel (BOFF-3839)\n * @module botlit/components\n *\n * The decision-card list at the heart of the supervisor feed. Rendered by the\n * workspace-wide /botmanor/ai page and, narrowed with `filter`, as a slice on\n * the agent and bot detail pages. Accept always confirms first and then\n * applies the recommendation through the existing mutation for the finding\n * type (rebuild index, pause agent) before marking the finding ACCEPTED;\n * findings without a safe one-click action are acknowledged with a deep link\n * to the subject. Dismiss records the signal and suppresses same-day\n * re-fires (fingerprint bucket, enforced server-side).\n */\n\nimport type { FC, ReactNode } from 'react';\nimport { useCallback, useMemo, useState } from 'react';\nimport { useNavigate } from 'react-router';\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { Check, ShieldAlert, X } from 'lucide-react';\nimport {\n PauseBotmanorAgentForSupervisorDocument,\n RebuildKnowledgeBaseIndexForSupervisorDocument,\n type PauseBotmanorAgentForSupervisorMutation,\n type RebuildKnowledgeBaseIndexForSupervisorMutation,\n} from '../../generated/wspace-operations';\nimport { AiPredictionStatus } from '../../generated/wspace-types';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { cn } from '../utils/cn';\nimport { formatRelativeTime } from '../utils/formatters';\nimport { Badge } from './Badge';\nimport { Button } from './Button';\nimport { Card, CardContent } from './Card';\nimport { EmptyState } from './EmptyState';\nimport { ErrorState } from './ErrorState';\nimport { useWorkspaceClient } from '../hooks/internal/workspaceGateway';\nimport {\n predictionSeverity,\n predictionSubjectName,\n useAiPredictions,\n useSetAiPredictionStatus,\n type AiPredictionRecord,\n type AiPredictionsFilter,\n} from '../hooks/useAiOperations';\n\nconst SEVERITY_VARIANTS: Record<\n string,\n 'default' | 'secondary' | 'warning' | 'destructive' | 'outline'\n> = {\n CRITICAL: 'destructive',\n WARNING: 'warning',\n INFO: 'secondary',\n};\n\nconst SEVERITY_LABEL_KEYS: Record<string, string> = {\n CRITICAL: 'botlit.aiOperations.severity.critical',\n WARNING: 'botlit.aiOperations.severity.warning',\n INFO: 'botlit.aiOperations.severity.info',\n};\n\nconst TYPE_LABEL_KEYS: Record<string, string> = {\n failure_rate_regression: 'botlit.aiOperations.type.failureRateRegression',\n grounding_decay: 'botlit.aiOperations.type.groundingDecay',\n cost_anomaly: 'botlit.aiOperations.type.costAnomaly',\n latency_drift: 'botlit.aiOperations.type.latencyDrift',\n stalled_index: 'botlit.aiOperations.type.stalledIndex',\n idle_agent: 'botlit.aiOperations.type.idleAgent',\n stale_knowledge: 'botlit.aiOperations.type.staleKnowledge',\n};\n\ninterface AcceptAction {\n /** Existing mutation that applies the recommendation. */\n kind: 'rebuild-index' | 'pause-agent';\n /** Dynamic confirmation copy. */\n confirmTitle: string;\n confirmBody: string;\n /** Fallback deep-link target when there is no one-click action. */\n acknowledgeOnly?: false;\n}\n\n/** Maps a finding to its Accept action, or null when Accept is not offered. */\nfunction acceptActionFor(\n record: AiPredictionRecord,\n t: ReturnType<typeof useTr>\n): AcceptAction | null {\n switch (record.predictionType) {\n case 'stalled_index':\n if (record.subjectType !== 'KNOWLEDGE_BASE' || !record.subjectId) return null;\n return {\n kind: 'rebuild-index',\n confirmTitle: t(\n 'botlit.aiOperations.confirm.rebuildTitle',\n 'Rebuild the knowledge base index?'\n ),\n confirmBody: t(\n 'botlit.aiOperations.confirm.rebuildBody',\n 'This rebuilds the index for this knowledge base so agents can retrieve from it again. The finding is marked accepted afterwards.'\n ),\n };\n case 'failure_rate_regression':\n case 'idle_agent':\n if (record.subjectType !== 'AGENT' || !record.subjectId) return null;\n return {\n kind: 'pause-agent',\n confirmTitle: t('botlit.aiOperations.confirm.pauseTitle', 'Pause this agent?'),\n confirmBody: t(\n 'botlit.aiOperations.confirm.pauseBody',\n 'The agent stops answering until a workspace member reactivates it. The finding is marked accepted afterwards.'\n ),\n };\n default:\n return null;\n }\n}\n\n/** Deep link to the subject for findings Accept cannot apply automatically. */\nfunction subjectLinkFor(record: AiPredictionRecord): string | null {\n if (!record.subjectId) return null;\n switch (record.subjectType) {\n case 'AGENT':\n return `/agents/${record.subjectId}`;\n case 'APP':\n return `/bots/${record.subjectId}`;\n case 'KNOWLEDGE_BASE':\n return `/knowledge/${record.subjectId}`;\n default:\n return null;\n }\n}\n\nfunction evidenceSummary(record: AiPredictionRecord): Array<[string, string]> {\n const features = record.features ?? {};\n const entries: Array<[string, string]> = [];\n const push = (key: string, value: unknown): void => {\n if (typeof value === 'number')\n entries.push([key, Number.isInteger(value) ? String(value) : value.toFixed(4)]);\n else if (typeof value === 'string' && value.length > 0 && value.length < 80)\n entries.push([key, value]);\n };\n for (const [key, value] of Object.entries(features).slice(0, 6)) {\n if (key.endsWith('At') || key === 'knowledgeBaseIds' || key === 'agentIds') continue;\n push(key, value);\n }\n return entries.slice(0, 4);\n}\n\ninterface DecisionCardProps {\n record: AiPredictionRecord;\n busy: boolean;\n onAccept: (record: AiPredictionRecord) => void;\n onDismiss: (record: AiPredictionRecord) => void;\n}\n\nconst DecisionCard: FC<DecisionCardProps> = ({ record, busy, onAccept, onDismiss }) => {\n const t = useTr();\n const navigate = useNavigate();\n const severity = predictionSeverity(record);\n const subjectName = predictionSubjectName(record);\n const action = record.status === 'GENERATED' ? acceptActionFor(record, t) : null;\n const subjectLink = subjectLinkFor(record);\n const evidence = evidenceSummary(record);\n const isOpen = record.status === 'GENERATED';\n\n return (\n <Card className={cn('p-5', severity === 'CRITICAL' && 'glow')} data-testid=\"ai-operations-card\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <Badge variant={SEVERITY_VARIANTS[severity] ?? 'secondary'}>\n {t(SEVERITY_LABEL_KEYS[severity] ?? SEVERITY_LABEL_KEYS.INFO, severity.toLowerCase())}\n </Badge>\n <Badge variant=\"outline\">\n {t(\n TYPE_LABEL_KEYS[record.predictionType] ?? 'botlit.aiOperations.type.unknown',\n record.predictionType.replace(/_/g, ' ')\n )}\n </Badge>\n <span className=\"text-sm text-text-secondary\">\n {t('botlit.aiOperations.onSubject', 'on {{name}}', { name: subjectName })}\n </span>\n </div>\n <span className=\"text-xs text-text-tertiary whitespace-nowrap\">\n {formatRelativeTime(record.generatedAt)}\n </span>\n </div>\n\n <p className=\"mt-3 text-sm text-text-primary\">{record.recommendation}</p>\n\n {evidence.length > 0 && (\n <dl className=\"mt-3 grid grid-cols-2 gap-x-4 gap-y-1 sm:grid-cols-4\">\n {evidence.map(([key, value]) => (\n <div key={key}>\n <dt className=\"text-xs text-text-tertiary\">{key}</dt>\n <dd className=\"text-sm text-text-secondary\">{value}</dd>\n </div>\n ))}\n </dl>\n )}\n\n {isOpen && (\n <div className=\"mt-4 flex items-center gap-2\">\n {action ? (\n <Button size=\"sm\" onClick={() => onAccept(record)} disabled={busy}>\n <Check className=\"h-4 w-4\" />\n {t('botlit.aiOperations.actions.accept', 'Accept')}\n </Button>\n ) : (\n subjectLink && (\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => navigate(subjectLink)}\n disabled={busy}\n >\n <Check className=\"h-4 w-4\" />\n {t('botlit.aiOperations.actions.reviewSubject', 'Review subject')}\n </Button>\n )\n )}\n <Button size=\"sm\" variant=\"outline\" onClick={() => onDismiss(record)} disabled={busy}>\n <X className=\"h-4 w-4\" />\n {t('botlit.aiOperations.actions.dismiss', 'Dismiss')}\n </Button>\n </div>\n )}\n {!isOpen && (\n <div className=\"mt-4\">\n <Badge variant={record.status === 'ACCEPTED' ? 'success' : 'outline'}>\n {record.status}\n </Badge>\n </div>\n )}\n </Card>\n );\n};\n\nexport interface AiOperationsPanelProps {\n /** Narrow the feed to one subject (agent detail / bot detail slices). */\n filter?: AiPredictionsFilter;\n title?: string;\n description?: string;\n className?: string;\n}\n\n/**\n * The decision-card feed. Pure presentation over `useAiPredictions` — the\n * owning page composes it with config and run controls.\n */\nexport const AiOperationsPanel: FC<AiOperationsPanelProps> = ({\n filter,\n title,\n description,\n className,\n}) => {\n const t = useTr();\n const client = useWorkspaceClient();\n const { data: records, loading, error, refetch } = useAiPredictions(filter);\n const [setStatus, setStatusState] = useSetAiPredictionStatus();\n const [pendingAccept, setPendingAccept] = useState<AiPredictionRecord | null>(null);\n const [actionError, setActionError] = useState<string | null>(null);\n const busy = setStatusState.loading;\n\n const onDismiss = useCallback(\n async (record: AiPredictionRecord) => {\n setActionError(null);\n try {\n await setStatus({ id: record.id, status: AiPredictionStatus.Dismissed });\n await refetch();\n } catch (dismissError) {\n setActionError(dismissError instanceof Error ? dismissError.message : String(dismissError));\n }\n },\n [setStatus, refetch]\n );\n\n const applyAccept = useCallback(async () => {\n const record = pendingAccept;\n if (!record) return;\n setPendingAccept(null);\n setActionError(null);\n const action = acceptActionFor(record, t);\n try {\n if (action?.kind === 'rebuild-index' && record.subjectId) {\n await client.mutate<RebuildKnowledgeBaseIndexForSupervisorMutation>({\n mutation: RebuildKnowledgeBaseIndexForSupervisorDocument,\n variables: { knowledgeBaseId: record.subjectId },\n });\n } else if (action?.kind === 'pause-agent' && record.subjectId) {\n await client.mutate<PauseBotmanorAgentForSupervisorMutation>({\n mutation: PauseBotmanorAgentForSupervisorDocument,\n variables: { id: record.subjectId, input: { status: 'PAUSED' } },\n });\n }\n await setStatus({ id: record.id, status: AiPredictionStatus.Accepted });\n await refetch();\n } catch (acceptError) {\n setActionError(acceptError instanceof Error ? acceptError.message : String(acceptError));\n }\n }, [pendingAccept, client, setStatus, refetch, t]);\n\n const pendingAction = pendingAccept ? acceptActionFor(pendingAccept, t) : null;\n\n const content: ReactNode = useMemo(() => {\n if (error) {\n return (\n <ErrorState\n resource={t('botlit.aiOperations.errorTitle', 'AI operations findings')}\n error={error}\n onRetry={() => void refetch()}\n />\n );\n }\n if (loading && records.length === 0) {\n return (\n <div className=\"space-y-3\" aria-busy=\"true\">\n {[0, 1, 2].map((index) => (\n <Card key={index} className=\"p-5 h-28 animate-pulse bg-bg-subtle\">\n <span className=\"sr-only\">\n {t('botlit.aiOperations.loading', 'Loading findings')}\n </span>\n </Card>\n ))}\n </div>\n );\n }\n if (records.length === 0) {\n return (\n <EmptyState\n icon={<ShieldAlert className=\"h-8 w-8\" />}\n title={t('botlit.aiOperations.emptyTitle', 'No findings')}\n description={t(\n 'botlit.aiOperations.emptyDescription',\n 'The supervisor has not flagged anything. Enable the scheduled review to keep it that way.'\n )}\n />\n );\n }\n return (\n <div className=\"space-y-3\" data-testid=\"ai-operations-feed\">\n {records.map((record) => (\n <DecisionCard\n key={record.id}\n record={record}\n busy={busy}\n onAccept={(target) => setPendingAccept(target)}\n onDismiss={(target) => void onDismiss(target)}\n />\n ))}\n </div>\n );\n }, [error, loading, records, busy, onDismiss, refetch, t]);\n\n return (\n <section className={className} data-testid=\"ai-operations-panel\">\n {(title || description) && (\n <header className=\"mb-4\">\n {title && <h2 className=\"text-lg font-semibold text-text-primary\">{title}</h2>}\n {description && <p className=\"text-sm text-text-secondary\">{description}</p>}\n </header>\n )}\n {actionError && (\n <p className=\"mb-3 text-sm text-status-error-text\" role=\"alert\">\n {actionError}\n </p>\n )}\n {content}\n\n <AlertDialog\n open={pendingAccept != null}\n onOpenChange={(open) => !open && setPendingAccept(null)}\n >\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>{pendingAction?.confirmTitle}</AlertDialogTitle>\n <AlertDialogDescription>{pendingAction?.confirmBody}</AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>\n {t('botlit.aiOperations.actions.cancel', 'Cancel')}\n </AlertDialogCancel>\n <AlertDialogAction onClick={() => void applyAccept()}>\n {t('botlit.aiOperations.actions.confirmAccept', 'Confirm')}\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </section>\n );\n};\n"],"mappings":"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"}
|
|
@@ -54,6 +54,6 @@ var o = ({ children: t, htmlFor: i, required: a, className: o }) => /* @__PURE__
|
|
|
54
54
|
]
|
|
55
55
|
});
|
|
56
56
|
//#endregion
|
|
57
|
-
export { s as FormField, i as Input, a as Textarea };
|
|
57
|
+
export { s as FormField, i as Input, o as Label, a as Textarea };
|
|
58
58
|
|
|
59
59
|
//# sourceMappingURL=Input.js.map
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { ConfigureSupervisorDocument as e, GetSupervisorConfigDocument as t, ListAiPredictionsDocument as n, ListSupervisorRunsDocument as r, RunSupervisorNowDocument as i, SetAiPredictionStatusDocument as a } from "../../generated/wspace-operations.js";
|
|
2
|
+
import { useWorkspaceId as o, useWorkspaceMutation as s, useWorkspaceQuery as c } from "./internal/workspaceGateway.js";
|
|
3
|
+
import { useMemo as l } from "react";
|
|
4
|
+
//#region src/botlit/hooks/useAiOperations.ts
|
|
5
|
+
var u = 50, d = [], f = [];
|
|
6
|
+
function p(e) {
|
|
7
|
+
let t = o(), { subjectType: r, subjectId: i, status: a } = e ?? {};
|
|
8
|
+
return c({
|
|
9
|
+
document: n,
|
|
10
|
+
variables: l(() => {
|
|
11
|
+
let e = { sourceProduct: "botmanor" };
|
|
12
|
+
return r && (e.subjectType = r), i && (e.subjectId = i), a && (e.status = a), {
|
|
13
|
+
filter: e,
|
|
14
|
+
pagination: {
|
|
15
|
+
limit: u,
|
|
16
|
+
offset: 0
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}, [
|
|
20
|
+
r,
|
|
21
|
+
i,
|
|
22
|
+
a
|
|
23
|
+
]),
|
|
24
|
+
enabled: !!t,
|
|
25
|
+
errorMessage: "Failed to load AI operations findings.",
|
|
26
|
+
fallback: d,
|
|
27
|
+
select: (e) => (e?.aiPredictions.items ?? []).map((e) => ({
|
|
28
|
+
id: e.id,
|
|
29
|
+
predictionType: e.predictionType,
|
|
30
|
+
subjectType: e.subjectType ?? null,
|
|
31
|
+
subjectId: e.subjectId ?? null,
|
|
32
|
+
score: e.score,
|
|
33
|
+
confidence: e.confidence,
|
|
34
|
+
recommendation: e.recommendation,
|
|
35
|
+
status: e.status,
|
|
36
|
+
features: e.features ?? null,
|
|
37
|
+
metadata: e.metadata ?? null,
|
|
38
|
+
generatedAt: e.generatedAt
|
|
39
|
+
}))
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function m() {
|
|
43
|
+
return s({
|
|
44
|
+
document: a,
|
|
45
|
+
variables: (e) => ({
|
|
46
|
+
id: e.id,
|
|
47
|
+
status: e.status
|
|
48
|
+
}),
|
|
49
|
+
select: (e) => {
|
|
50
|
+
let t = e?.setAiPredictionStatus;
|
|
51
|
+
return t ? {
|
|
52
|
+
id: t.id,
|
|
53
|
+
status: t.status
|
|
54
|
+
} : void 0;
|
|
55
|
+
},
|
|
56
|
+
errorMessage: "Failed to update the finding."
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function h() {
|
|
60
|
+
return c({
|
|
61
|
+
document: t,
|
|
62
|
+
variables: {},
|
|
63
|
+
enabled: !!o(),
|
|
64
|
+
errorMessage: "Failed to load the supervisor configuration.",
|
|
65
|
+
fallback: void 0,
|
|
66
|
+
select: (e) => {
|
|
67
|
+
let t = e?.supervisorConfig;
|
|
68
|
+
if (t) return {
|
|
69
|
+
enabled: t.enabled,
|
|
70
|
+
cron: t.cron,
|
|
71
|
+
timezone: t.timezone,
|
|
72
|
+
lastRunAt: t.lastRunAt ?? null,
|
|
73
|
+
updatedAt: t.updatedAt
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
function g() {
|
|
79
|
+
return s({
|
|
80
|
+
document: e,
|
|
81
|
+
variables: (e) => ({ input: e }),
|
|
82
|
+
select: (e) => {
|
|
83
|
+
let t = e?.configureSupervisor;
|
|
84
|
+
if (t) return {
|
|
85
|
+
enabled: t.enabled,
|
|
86
|
+
cron: t.cron,
|
|
87
|
+
timezone: t.timezone,
|
|
88
|
+
lastRunAt: t.lastRunAt ?? null,
|
|
89
|
+
updatedAt: t.updatedAt
|
|
90
|
+
};
|
|
91
|
+
},
|
|
92
|
+
errorMessage: "Failed to save the supervisor configuration."
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function _() {
|
|
96
|
+
return s({
|
|
97
|
+
document: i,
|
|
98
|
+
variables: () => ({}),
|
|
99
|
+
select: (e) => {
|
|
100
|
+
let t = e?.runSupervisorNow;
|
|
101
|
+
if (!t) return;
|
|
102
|
+
let n = t.run;
|
|
103
|
+
return {
|
|
104
|
+
findings: t.findings,
|
|
105
|
+
run: n ? {
|
|
106
|
+
id: n.id,
|
|
107
|
+
trigger: n.trigger,
|
|
108
|
+
status: n.status,
|
|
109
|
+
findings: n.findings,
|
|
110
|
+
notified: n.notified,
|
|
111
|
+
error: n.error ?? null,
|
|
112
|
+
startedAt: n.startedAt,
|
|
113
|
+
completedAt: n.completedAt ?? null
|
|
114
|
+
} : null
|
|
115
|
+
};
|
|
116
|
+
},
|
|
117
|
+
errorMessage: "Failed to run the supervisor review."
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
function v() {
|
|
121
|
+
return c({
|
|
122
|
+
document: r,
|
|
123
|
+
variables: { pagination: {
|
|
124
|
+
limit: 20,
|
|
125
|
+
offset: 0
|
|
126
|
+
} },
|
|
127
|
+
enabled: !!o(),
|
|
128
|
+
errorMessage: "Failed to load the supervisor run history.",
|
|
129
|
+
fallback: f,
|
|
130
|
+
select: (e) => (e?.supervisorRuns.items ?? []).map((e) => ({
|
|
131
|
+
id: e.id,
|
|
132
|
+
trigger: e.trigger,
|
|
133
|
+
status: e.status,
|
|
134
|
+
findings: e.findings,
|
|
135
|
+
notified: e.notified,
|
|
136
|
+
error: e.error ?? null,
|
|
137
|
+
startedAt: e.startedAt,
|
|
138
|
+
completedAt: e.completedAt ?? null
|
|
139
|
+
}))
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
function y(e) {
|
|
143
|
+
let t = e.metadata?.severity;
|
|
144
|
+
return typeof t == "string" ? t : "INFO";
|
|
145
|
+
}
|
|
146
|
+
function b(e) {
|
|
147
|
+
let t = e.metadata?.subjectName;
|
|
148
|
+
return typeof t == "string" && t.length > 0 ? t : e.subjectId ?? e.predictionType;
|
|
149
|
+
}
|
|
150
|
+
//#endregion
|
|
151
|
+
export { y as predictionSeverity, b as predictionSubjectName, p as useAiPredictions, g as useConfigureSupervisor, _ as useRunSupervisorNow, m as useSetAiPredictionStatus, h as useSupervisorConfig, v as useSupervisorRuns };
|
|
152
|
+
|
|
153
|
+
//# sourceMappingURL=useAiOperations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAiOperations.js","names":[],"sources":["../../../src/botlit/hooks/useAiOperations.ts"],"sourcesContent":["/**\n * AI Operations hooks (BOFF-3839)\n * @module botlit/hooks\n *\n * Backed by the supervisor surface on wspace-botmanor-svc: the AiPrediction\n * feed (findings as decision cards), the supervisor configuration, run-now\n * and the run history. Read conventions come from `useExecutions`\n * (network-only, explicit error state, caller-driven refetch).\n */\n\nimport { useMemo } from 'react';\nimport {\n ConfigureSupervisorDocument,\n GetSupervisorConfigDocument,\n ListAiPredictionsDocument,\n ListSupervisorRunsDocument,\n RunSupervisorNowDocument,\n SetAiPredictionStatusDocument,\n type ConfigureSupervisorMutation,\n type ConfigureSupervisorMutationVariables,\n type GetSupervisorConfigQuery,\n type ListAiPredictionsQuery,\n type ListSupervisorRunsQuery,\n type RunSupervisorNowMutation,\n type SetAiPredictionStatusMutation,\n type SetAiPredictionStatusMutationVariables,\n} from '../../generated/wspace-operations';\nimport type {\n AiPredictionFilterInput,\n AiPredictionStatus,\n ConfigureSupervisorInput,\n} from '../../generated/wspace-types';\nimport {\n useWorkspaceId,\n useWorkspaceMutation,\n useWorkspaceQuery,\n type QueryResult,\n} from './internal/workspaceGateway';\n\nconst LIST_LIMIT = 50;\n\nexport interface AiPredictionRecord {\n id: string;\n predictionType: string;\n subjectType: string | null;\n subjectId: string | null;\n score: number;\n confidence: number;\n recommendation: string;\n status: string;\n features: Record<string, unknown> | null;\n /** Supervisor metadata: fingerprint, severity, subjectName, runId. */\n metadata: Record<string, unknown> | null;\n generatedAt: string;\n}\n\nexport interface AiPredictionsFilter {\n subjectType?: string;\n subjectId?: string;\n status?: string;\n}\n\nexport interface SupervisorConfigRecord {\n enabled: boolean;\n cron: string;\n timezone: string;\n lastRunAt: string | null;\n updatedAt: string;\n}\n\nexport interface SupervisorRunRecord {\n id: string;\n trigger: string;\n status: string;\n findings: number;\n notified: number;\n error: string | null;\n startedAt: string;\n completedAt: string | null;\n}\n\nconst NO_PREDICTIONS: AiPredictionRecord[] = [];\nconst NO_RUNS: SupervisorRunRecord[] = [];\n\nexport const aiOperationsKeys = {\n all: ['ai-operations'] as const,\n predictions: (filter?: AiPredictionsFilter) =>\n [...aiOperationsKeys.all, 'predictions', filter] as const,\n config: () => [...aiOperationsKeys.all, 'config'] as const,\n runs: () => [...aiOperationsKeys.all, 'runs'] as const,\n};\n\n/** Decision-card feed, optionally narrowed to one subject (agent/app/KB). */\nexport function useAiPredictions(filter?: AiPredictionsFilter): QueryResult<AiPredictionRecord[]> {\n const workspaceId = useWorkspaceId();\n const { subjectType, subjectId, status } = filter ?? {};\n\n const variables = useMemo(() => {\n const gqlFilter: AiPredictionFilterInput = { sourceProduct: 'botmanor' };\n if (subjectType) gqlFilter.subjectType = subjectType;\n if (subjectId) gqlFilter.subjectId = subjectId;\n if (status) gqlFilter.status = status as AiPredictionStatus;\n return { filter: gqlFilter, pagination: { limit: LIST_LIMIT, offset: 0 } };\n }, [subjectType, subjectId, status]);\n\n return useWorkspaceQuery<ListAiPredictionsQuery, AiPredictionRecord[]>({\n document: ListAiPredictionsDocument,\n variables,\n enabled: !!workspaceId,\n errorMessage: 'Failed to load AI operations findings.',\n fallback: NO_PREDICTIONS,\n select: (data) =>\n (data?.aiPredictions.items ?? []).map((item) => ({\n id: item.id,\n predictionType: item.predictionType,\n subjectType: item.subjectType ?? null,\n subjectId: item.subjectId ?? null,\n score: item.score,\n confidence: item.confidence,\n recommendation: item.recommendation,\n status: item.status,\n features: (item.features ?? null) as Record<string, unknown> | null,\n metadata: (item.metadata ?? null) as Record<string, unknown> | null,\n generatedAt: item.generatedAt,\n })),\n });\n}\n\n/** Accept / dismiss one finding. Callers refetch the feed afterwards. */\nexport function useSetAiPredictionStatus(): readonly [\n (input: { id: string; status: AiPredictionStatus }) => Promise<{ id: string; status: string }>,\n { data: { id: string; status: string } | undefined; loading: boolean; error: Error | undefined },\n] {\n return useWorkspaceMutation<\n SetAiPredictionStatusMutation,\n { id: string; status: AiPredictionStatus },\n { id: string; status: string }\n >({\n document: SetAiPredictionStatusDocument,\n variables: (input) =>\n ({ id: input.id, status: input.status }) as SetAiPredictionStatusMutationVariables,\n select: (data) => {\n const row = data?.setAiPredictionStatus;\n return row ? { id: row.id, status: row.status } : undefined;\n },\n errorMessage: 'Failed to update the finding.',\n });\n}\n\nexport function useSupervisorConfig(): QueryResult<SupervisorConfigRecord | undefined> {\n const workspaceId = useWorkspaceId();\n return useWorkspaceQuery<GetSupervisorConfigQuery, SupervisorConfigRecord | undefined>({\n document: GetSupervisorConfigDocument,\n variables: {},\n enabled: !!workspaceId,\n errorMessage: 'Failed to load the supervisor configuration.',\n fallback: undefined,\n select: (data) => {\n const config = data?.supervisorConfig;\n if (!config) return undefined;\n return {\n enabled: config.enabled,\n cron: config.cron,\n timezone: config.timezone,\n lastRunAt: config.lastRunAt ?? null,\n updatedAt: config.updatedAt,\n };\n },\n });\n}\n\nexport function useConfigureSupervisor(): readonly [\n (input: {\n enabled?: boolean;\n cron?: string;\n timezone?: string;\n }) => Promise<SupervisorConfigRecord>,\n { data: SupervisorConfigRecord | undefined; loading: boolean; error: Error | undefined },\n] {\n return useWorkspaceMutation<\n ConfigureSupervisorMutation,\n ConfigureSupervisorInput,\n SupervisorConfigRecord\n >({\n document: ConfigureSupervisorDocument,\n variables: (input) => ({ input }) as ConfigureSupervisorMutationVariables,\n select: (data) => {\n const config = data?.configureSupervisor;\n if (!config) return undefined;\n return {\n enabled: config.enabled,\n cron: config.cron,\n timezone: config.timezone,\n lastRunAt: config.lastRunAt ?? null,\n updatedAt: config.updatedAt,\n };\n },\n errorMessage: 'Failed to save the supervisor configuration.',\n });\n}\n\nexport interface RunSupervisorNowResult {\n run: SupervisorRunRecord | null;\n findings: number;\n}\n\n/** Manual fleet review (the scheduled path is the FluidGrids cron tick). */\nexport function useRunSupervisorNow(): readonly [\n () => Promise<RunSupervisorNowResult>,\n { data: RunSupervisorNowResult | undefined; loading: boolean; error: Error | undefined },\n] {\n return useWorkspaceMutation<RunSupervisorNowMutation, void, RunSupervisorNowResult>({\n document: RunSupervisorNowDocument,\n variables: () => ({}),\n select: (data) => {\n const result = data?.runSupervisorNow;\n if (!result) return undefined;\n const run = result.run;\n return {\n findings: result.findings,\n run: run\n ? {\n id: run.id,\n trigger: run.trigger,\n status: run.status,\n findings: run.findings,\n notified: run.notified,\n error: run.error ?? null,\n startedAt: run.startedAt,\n completedAt: run.completedAt ?? null,\n }\n : null,\n };\n },\n errorMessage: 'Failed to run the supervisor review.',\n });\n}\n\nexport function useSupervisorRuns(): QueryResult<SupervisorRunRecord[]> {\n const workspaceId = useWorkspaceId();\n return useWorkspaceQuery<ListSupervisorRunsQuery, SupervisorRunRecord[]>({\n document: ListSupervisorRunsDocument,\n variables: { pagination: { limit: 20, offset: 0 } },\n enabled: !!workspaceId,\n errorMessage: 'Failed to load the supervisor run history.',\n fallback: NO_RUNS,\n select: (data) =>\n (data?.supervisorRuns.items ?? []).map((run) => ({\n id: run.id,\n trigger: run.trigger,\n status: run.status,\n findings: run.findings,\n notified: run.notified,\n error: run.error ?? null,\n startedAt: run.startedAt,\n completedAt: run.completedAt ?? null,\n })),\n });\n}\n\n/** Severity from supervisor metadata (WARNING/CRITICAL/INFO), defaulting to INFO. */\nexport function predictionSeverity(record: AiPredictionRecord): string {\n const severity = record.metadata?.['severity'];\n return typeof severity === 'string' ? severity : 'INFO';\n}\n\n/** Human name the supervisor recorded for the subject, when present. */\nexport function predictionSubjectName(record: AiPredictionRecord): string {\n const name = record.metadata?.['subjectName'];\n if (typeof name === 'string' && name.length > 0) return name;\n return record.subjectId ?? record.predictionType;\n}\n"],"mappings":";;;;AAuCA,IAAM,IAAa,IA0Cb,IAAuC,EAAE,EACzC,IAAiC,EAAE;AAWzC,SAAgB,EAAiB,GAAiE;CAChG,IAAM,IAAc,GAAgB,EAC9B,EAAE,gBAAa,cAAW,cAAW,KAAU,EAAE;AAUvD,QAAO,EAAgE;EACrE,UAAU;EACV,WAVgB,QAAc;GAC9B,IAAM,IAAqC,EAAE,eAAe,YAAY;AAIxE,UAHI,MAAa,EAAU,cAAc,IACrC,MAAW,EAAU,YAAY,IACjC,MAAQ,EAAU,SAAS,IACxB;IAAE,QAAQ;IAAW,YAAY;KAAE,OAAO;KAAY,QAAQ;KAAG;IAAE;KACzE;GAAC;GAAa;GAAW;GAAO,CAAC;EAKlC,SAAS,CAAC,CAAC;EACX,cAAc;EACd,UAAU;EACV,SAAS,OACN,GAAM,cAAc,SAAS,EAAE,EAAE,KAAK,OAAU;GAC/C,IAAI,EAAK;GACT,gBAAgB,EAAK;GACrB,aAAa,EAAK,eAAe;GACjC,WAAW,EAAK,aAAa;GAC7B,OAAO,EAAK;GACZ,YAAY,EAAK;GACjB,gBAAgB,EAAK;GACrB,QAAQ,EAAK;GACb,UAAW,EAAK,YAAY;GAC5B,UAAW,EAAK,YAAY;GAC5B,aAAa,EAAK;GACnB,EAAE;EACN,CAAC;;AAIJ,SAAgB,IAGd;AACA,QAAO,EAIL;EACA,UAAU;EACV,YAAY,OACT;GAAE,IAAI,EAAM;GAAI,QAAQ,EAAM;GAAQ;EACzC,SAAS,MAAS;GAChB,IAAM,IAAM,GAAM;AAClB,UAAO,IAAM;IAAE,IAAI,EAAI;IAAI,QAAQ,EAAI;IAAQ,GAAG,KAAA;;EAEpD,cAAc;EACf,CAAC;;AAGJ,SAAgB,IAAuE;AAErF,QAAO,EAAgF;EACrF,UAAU;EACV,WAAW,EAAE;EACb,SAAS,CAAC,CAJQ,GAAgB;EAKlC,cAAc;EACd,UAAU,KAAA;EACV,SAAS,MAAS;GAChB,IAAM,IAAS,GAAM;AAChB,SACL,QAAO;IACL,SAAS,EAAO;IAChB,MAAM,EAAO;IACb,UAAU,EAAO;IACjB,WAAW,EAAO,aAAa;IAC/B,WAAW,EAAO;IACnB;;EAEJ,CAAC;;AAGJ,SAAgB,IAOd;AACA,QAAO,EAIL;EACA,UAAU;EACV,YAAY,OAAW,EAAE,UAAO;EAChC,SAAS,MAAS;GAChB,IAAM,IAAS,GAAM;AAChB,SACL,QAAO;IACL,SAAS,EAAO;IAChB,MAAM,EAAO;IACb,UAAU,EAAO;IACjB,WAAW,EAAO,aAAa;IAC/B,WAAW,EAAO;IACnB;;EAEH,cAAc;EACf,CAAC;;AASJ,SAAgB,IAGd;AACA,QAAO,EAA6E;EAClF,UAAU;EACV,kBAAkB,EAAE;EACpB,SAAS,MAAS;GAChB,IAAM,IAAS,GAAM;AACrB,OAAI,CAAC,EAAQ;GACb,IAAM,IAAM,EAAO;AACnB,UAAO;IACL,UAAU,EAAO;IACjB,KAAK,IACD;KACE,IAAI,EAAI;KACR,SAAS,EAAI;KACb,QAAQ,EAAI;KACZ,UAAU,EAAI;KACd,UAAU,EAAI;KACd,OAAO,EAAI,SAAS;KACpB,WAAW,EAAI;KACf,aAAa,EAAI,eAAe;KACjC,GACD;IACL;;EAEH,cAAc;EACf,CAAC;;AAGJ,SAAgB,IAAwD;AAEtE,QAAO,EAAkE;EACvE,UAAU;EACV,WAAW,EAAE,YAAY;GAAE,OAAO;GAAI,QAAQ;GAAG,EAAE;EACnD,SAAS,CAAC,CAJQ,GAAgB;EAKlC,cAAc;EACd,UAAU;EACV,SAAS,OACN,GAAM,eAAe,SAAS,EAAE,EAAE,KAAK,OAAS;GAC/C,IAAI,EAAI;GACR,SAAS,EAAI;GACb,QAAQ,EAAI;GACZ,UAAU,EAAI;GACd,UAAU,EAAI;GACd,OAAO,EAAI,SAAS;GACpB,WAAW,EAAI;GACf,aAAa,EAAI,eAAe;GACjC,EAAE;EACN,CAAC;;AAIJ,SAAgB,EAAmB,GAAoC;CACrE,IAAM,IAAW,EAAO,UAAW;AACnC,QAAO,OAAO,KAAa,WAAW,IAAW;;AAInD,SAAgB,EAAsB,GAAoC;CACxE,IAAM,IAAO,EAAO,UAAW;AAE/B,QADI,OAAO,KAAS,YAAY,EAAK,SAAS,IAAU,IACjD,EAAO,aAAa,EAAO"}
|