@agent-native/core 0.78.9 → 0.79.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +11 -0
- package/corpus/core/docs/content/actions.md +4 -2
- package/corpus/core/docs/content/agent-surfaces.md +9 -0
- package/corpus/core/docs/content/extensions.md +10 -0
- package/corpus/core/docs/content/generative-ui.md +217 -0
- package/corpus/core/docs/content/key-concepts.md +2 -2
- package/corpus/core/docs/content/mcp-apps.md +3 -5
- package/corpus/core/docs/content/native-chat-ui.md +9 -5
- package/corpus/core/docs/content/tracking.md +9 -3
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/action-ui.ts +1 -0
- package/corpus/core/src/client/analytics.ts +195 -12
- package/corpus/core/src/client/chat/index.ts +1 -0
- package/corpus/core/src/client/chat/tool-call-display.tsx +5 -2
- package/corpus/core/src/client/chat/widgets/InlineExtensionWidget.tsx +139 -0
- package/corpus/core/src/client/chat/widgets/builtin-tool-renderers.tsx +27 -1
- package/corpus/core/src/client/conversation/AgentConversation.tsx +38 -0
- package/corpus/core/src/client/conversation/code-agent-transcript.ts +9 -0
- package/corpus/core/src/client/conversation/types.ts +4 -0
- package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +27 -5
- package/corpus/core/src/client/extensions/ExtensionViewer.tsx +26 -5
- package/corpus/core/src/client/extensions/InlineExtensionFrame.tsx +654 -0
- package/corpus/core/src/client/extensions/iframe-bridge.ts +30 -0
- package/corpus/core/src/client/extensions/index.ts +5 -0
- package/corpus/core/src/client/index.ts +1 -0
- package/corpus/core/src/client/session-replay.ts +89 -14
- package/corpus/core/src/code-agents/transcript-normalizer.ts +14 -0
- package/corpus/core/src/extensions/actions.ts +218 -1
- package/corpus/core/src/extensions/html-shell.ts +70 -3
- package/corpus/core/src/extensions/routes.ts +12 -4
- package/corpus/core/src/index.browser.ts +1 -0
- package/corpus/core/src/index.ts +1 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +21 -9
- package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +13 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/references/api.md +2 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/generative-ui/SKILL.md +115 -0
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +198 -59
- package/corpus/templates/analytics/AGENTS.md +12 -0
- package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +1105 -0
- package/corpus/templates/analytics/actions/dashboard-panel-order.ts +169 -0
- package/corpus/templates/analytics/actions/get-sql-dashboard.ts +89 -6
- package/corpus/templates/analytics/actions/mutate-dashboard.ts +333 -0
- package/corpus/templates/analytics/actions/reorder-dashboard-panels.ts +214 -0
- package/corpus/templates/analytics/actions/send-dashboard-report-now.ts +48 -21
- package/corpus/templates/analytics/actions/update-dashboard.ts +193 -100
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +11 -0
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +1 -1
- package/corpus/templates/analytics/app/global.css +81 -10
- package/corpus/templates/analytics/app/i18n-data.ts +249 -4
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/ChartCard.tsx +3 -3
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +41 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/EmailReportDialog.tsx +1 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +4 -4
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +48 -18
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-layout.ts +32 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +204 -70
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +1101 -227
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +160 -133
- package/corpus/templates/analytics/changelog/2026-06-26-agents-can-now-edit-dashboards-through-a-typed-mutation-scri.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-dashboard-bar-chart-hovers-stay-subtle-in-dark-mode.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-dashboard-chart-moves-are-now-handled-by-panel-id-making-sim.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-dashboard-charts-drag-smoothly-with-stable-cards-inactive-ch.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-dashboard-charts-show-a-refresh-spinner-while-keeping-existi.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-dashboard-edits-stay-stable-while-chart-order-and-deletions-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-dashboard-email-reports-queue-immediate-sends.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-explorer-dashboards-now-drag-charts-with-a-stable-preview-an.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-session-replay-playback-can-now-be-toggled-by-clicking-the-r.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-26-session-replays-now-open-from-the-full-row-and-play-with-tim.md +6 -0
- package/corpus/templates/analytics/server/lib/dashboard-report-subscriptions.ts +21 -0
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +41 -17
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +5 -0
- package/dist/action-ui.d.ts +1 -0
- package/dist/action-ui.d.ts.map +1 -1
- package/dist/action-ui.js +1 -0
- package/dist/action-ui.js.map +1 -1
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +173 -12
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/chat/index.d.ts +1 -1
- package/dist/client/chat/index.d.ts.map +1 -1
- package/dist/client/chat/index.js +1 -1
- package/dist/client/chat/index.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +3 -1
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/chat/widgets/InlineExtensionWidget.d.ts +17 -0
- package/dist/client/chat/widgets/InlineExtensionWidget.d.ts.map +1 -0
- package/dist/client/chat/widgets/InlineExtensionWidget.js +85 -0
- package/dist/client/chat/widgets/InlineExtensionWidget.js.map +1 -0
- package/dist/client/chat/widgets/builtin-tool-renderers.d.ts.map +1 -1
- package/dist/client/chat/widgets/builtin-tool-renderers.js +15 -2
- package/dist/client/chat/widgets/builtin-tool-renderers.js.map +1 -1
- package/dist/client/conversation/AgentConversation.d.ts.map +1 -1
- package/dist/client/conversation/AgentConversation.js +33 -0
- package/dist/client/conversation/AgentConversation.js.map +1 -1
- package/dist/client/conversation/code-agent-transcript.js +8 -0
- package/dist/client/conversation/code-agent-transcript.js.map +1 -1
- package/dist/client/conversation/types.d.ts +4 -0
- package/dist/client/conversation/types.d.ts.map +1 -1
- package/dist/client/conversation/types.js.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.js +29 -1
- package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
- package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionViewer.js +28 -1
- package/dist/client/extensions/ExtensionViewer.js.map +1 -1
- package/dist/client/extensions/InlineExtensionFrame.d.ts +25 -0
- package/dist/client/extensions/InlineExtensionFrame.d.ts.map +1 -0
- package/dist/client/extensions/InlineExtensionFrame.js +488 -0
- package/dist/client/extensions/InlineExtensionFrame.js.map +1 -0
- package/dist/client/extensions/iframe-bridge.d.ts +2 -0
- package/dist/client/extensions/iframe-bridge.d.ts.map +1 -1
- package/dist/client/extensions/iframe-bridge.js +23 -0
- package/dist/client/extensions/iframe-bridge.js.map +1 -1
- package/dist/client/extensions/index.d.ts +1 -0
- package/dist/client/extensions/index.d.ts.map +1 -1
- package/dist/client/extensions/index.js +1 -0
- package/dist/client/extensions/index.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/session-replay.d.ts +11 -1
- package/dist/client/session-replay.d.ts.map +1 -1
- package/dist/client/session-replay.js +74 -14
- package/dist/client/session-replay.js.map +1 -1
- package/dist/code-agents/transcript-normalizer.d.ts +2 -0
- package/dist/code-agents/transcript-normalizer.d.ts.map +1 -1
- package/dist/code-agents/transcript-normalizer.js +8 -0
- package/dist/code-agents/transcript-normalizer.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/extensions/actions.d.ts.map +1 -1
- package/dist/extensions/actions.js +200 -1
- package/dist/extensions/actions.js.map +1 -1
- package/dist/extensions/html-shell.d.ts.map +1 -1
- package/dist/extensions/html-shell.js +70 -3
- package/dist/extensions/html-shell.js.map +1 -1
- package/dist/extensions/routes.d.ts.map +1 -1
- package/dist/extensions/routes.js +11 -4
- package/dist/extensions/routes.js.map +1 -1
- package/dist/index.browser.d.ts +1 -1
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.browser.js +1 -1
- package/dist/index.browser.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +21 -9
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +13 -0
- package/dist/templates/workspace-core/.agents/skills/extensions/references/api.md +2 -0
- package/dist/templates/workspace-core/.agents/skills/generative-ui/SKILL.md +115 -0
- package/docs/content/actions.md +4 -2
- package/docs/content/agent-surfaces.md +9 -0
- package/docs/content/extensions.md +10 -0
- package/docs/content/generative-ui.md +217 -0
- package/docs/content/key-concepts.md +2 -2
- package/docs/content/mcp-apps.md +3 -5
- package/docs/content/native-chat-ui.md +9 -5
- package/docs/content/tracking.md +9 -3
- package/package.json +1 -1
- package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +13 -0
- package/src/templates/workspace-core/.agents/skills/extensions/references/api.md +2 -0
- package/src/templates/workspace-core/.agents/skills/generative-ui/SKILL.md +115 -0
|
@@ -1,24 +1,19 @@
|
|
|
1
1
|
import { CodeSurface } from "@agent-native/core/blocks";
|
|
2
2
|
import { useActionQuery, useT } from "@agent-native/core/client";
|
|
3
3
|
import {
|
|
4
|
+
IconChevronDown,
|
|
4
5
|
IconCode,
|
|
5
6
|
IconFilter,
|
|
6
7
|
IconPlayerPlay,
|
|
7
8
|
IconRefresh,
|
|
8
9
|
IconSearch,
|
|
9
10
|
} from "@tabler/icons-react";
|
|
10
|
-
import { useMemo } from "react";
|
|
11
|
-
import {
|
|
11
|
+
import { useMemo, useState } from "react";
|
|
12
|
+
import { useNavigate, useSearchParams } from "react-router";
|
|
12
13
|
|
|
13
14
|
import { Badge } from "@/components/ui/badge";
|
|
14
15
|
import { Button } from "@/components/ui/button";
|
|
15
|
-
import {
|
|
16
|
-
Card,
|
|
17
|
-
CardContent,
|
|
18
|
-
CardDescription,
|
|
19
|
-
CardHeader,
|
|
20
|
-
CardTitle,
|
|
21
|
-
} from "@/components/ui/card";
|
|
16
|
+
import { Card, CardContent } from "@/components/ui/card";
|
|
22
17
|
import { Input } from "@/components/ui/input";
|
|
23
18
|
import {
|
|
24
19
|
Select,
|
|
@@ -28,14 +23,6 @@ import {
|
|
|
28
23
|
SelectValue,
|
|
29
24
|
} from "@/components/ui/select";
|
|
30
25
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
31
|
-
import {
|
|
32
|
-
Table,
|
|
33
|
-
TableBody,
|
|
34
|
-
TableCell,
|
|
35
|
-
TableHead,
|
|
36
|
-
TableHeader,
|
|
37
|
-
TableRow,
|
|
38
|
-
} from "@/components/ui/table";
|
|
39
26
|
import { cn } from "@/lib/utils";
|
|
40
27
|
|
|
41
28
|
type ReplayRange = "24h" | "7d" | "30d" | "90d" | "all";
|
|
@@ -74,7 +61,9 @@ const RANGE_OPTIONS: ReplayRange[] = ["24h", "7d", "30d", "90d", "all"];
|
|
|
74
61
|
|
|
75
62
|
export default function SessionsPage() {
|
|
76
63
|
const t = useT();
|
|
64
|
+
const navigate = useNavigate();
|
|
77
65
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
66
|
+
const [filtersOpen, setFiltersOpen] = useState(false);
|
|
78
67
|
const range = readRange(searchParams.get("range"));
|
|
79
68
|
const app = searchParams.get("app") ?? "";
|
|
80
69
|
const query = searchParams.get("q") ?? "";
|
|
@@ -105,76 +94,39 @@ export default function SessionsPage() {
|
|
|
105
94
|
}
|
|
106
95
|
|
|
107
96
|
return (
|
|
108
|
-
<div className="mx-auto flex w-full max-w-7xl flex-col gap-
|
|
109
|
-
<div className="flex
|
|
110
|
-
<div className="min-w-0
|
|
97
|
+
<div className="mx-auto flex w-full max-w-7xl flex-col gap-4 px-4 py-5 md:px-6">
|
|
98
|
+
<div className="flex items-center">
|
|
99
|
+
<div className="min-w-0">
|
|
111
100
|
<div className="flex items-center gap-2">
|
|
112
101
|
<IconPlayerPlay className="h-5 w-5 text-primary" />
|
|
113
|
-
<h1 className="text-
|
|
102
|
+
<h1 className="text-xl font-semibold tracking-normal">
|
|
114
103
|
{t("sessions.title")}
|
|
115
104
|
</h1>
|
|
116
105
|
</div>
|
|
117
|
-
<p className="max-w-3xl text-sm text-muted-foreground">
|
|
118
|
-
{t("sessions.description")}
|
|
119
|
-
</p>
|
|
120
106
|
</div>
|
|
121
|
-
<Button
|
|
122
|
-
type="button"
|
|
123
|
-
variant="outline"
|
|
124
|
-
onClick={() => void refetch()}
|
|
125
|
-
disabled={isFetching}
|
|
126
|
-
>
|
|
127
|
-
<IconRefresh
|
|
128
|
-
className={cn("h-4 w-4", isFetching && "animate-spin")}
|
|
129
|
-
/>
|
|
130
|
-
{t("sessions.refresh")}
|
|
131
|
-
</Button>
|
|
132
107
|
</div>
|
|
133
108
|
|
|
134
109
|
<Card>
|
|
135
|
-
<
|
|
136
|
-
<div className="
|
|
137
|
-
<div>
|
|
138
|
-
<
|
|
139
|
-
|
|
140
|
-
{t("sessions.filters")}
|
|
141
|
-
</CardTitle>
|
|
142
|
-
<CardDescription>
|
|
143
|
-
{t("sessions.filtersDescription")}
|
|
144
|
-
</CardDescription>
|
|
145
|
-
</div>
|
|
146
|
-
<div className="flex flex-wrap items-center gap-2 text-xs text-muted-foreground">
|
|
147
|
-
{data ? (
|
|
148
|
-
<Badge variant="outline">
|
|
149
|
-
{t("sessions.showing", {
|
|
150
|
-
count: String(recordings.length),
|
|
151
|
-
})}
|
|
152
|
-
</Badge>
|
|
153
|
-
) : null}
|
|
110
|
+
<CardContent className="p-3">
|
|
111
|
+
<div className="grid gap-2 lg:grid-cols-[150px_minmax(260px,1fr)_160px_auto_auto] lg:items-center">
|
|
112
|
+
<div className="flex items-center gap-2 px-1 text-sm font-medium">
|
|
113
|
+
<IconFilter className="h-4 w-4 text-muted-foreground" />
|
|
114
|
+
{t("sessions.segmentFilters")}
|
|
154
115
|
</div>
|
|
155
|
-
</div>
|
|
156
|
-
</CardHeader>
|
|
157
|
-
<CardContent>
|
|
158
|
-
<div className="grid gap-3 md:grid-cols-[minmax(0,1.4fr)_minmax(160px,0.7fr)_160px]">
|
|
159
116
|
<div className="relative">
|
|
160
117
|
<IconSearch className="pointer-events-none absolute start-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
|
161
118
|
<Input
|
|
162
119
|
value={query}
|
|
163
120
|
onChange={(event) => updateFilter("q", event.target.value)}
|
|
164
121
|
placeholder={t("sessions.searchPlaceholder")}
|
|
165
|
-
className="ps-9"
|
|
122
|
+
className="h-9 ps-9"
|
|
166
123
|
/>
|
|
167
124
|
</div>
|
|
168
|
-
<Input
|
|
169
|
-
value={app}
|
|
170
|
-
onChange={(event) => updateFilter("app", event.target.value)}
|
|
171
|
-
placeholder={t("sessions.appPlaceholder")}
|
|
172
|
-
/>
|
|
173
125
|
<Select
|
|
174
126
|
value={range}
|
|
175
127
|
onValueChange={(value) => updateFilter("range", value)}
|
|
176
128
|
>
|
|
177
|
-
<SelectTrigger aria-label={t("sessions.range")}>
|
|
129
|
+
<SelectTrigger className="h-9" aria-label={t("sessions.range")}>
|
|
178
130
|
<SelectValue />
|
|
179
131
|
</SelectTrigger>
|
|
180
132
|
<SelectContent>
|
|
@@ -185,7 +137,59 @@ export default function SessionsPage() {
|
|
|
185
137
|
))}
|
|
186
138
|
</SelectContent>
|
|
187
139
|
</Select>
|
|
140
|
+
<Button
|
|
141
|
+
type="button"
|
|
142
|
+
variant="outline"
|
|
143
|
+
size="sm"
|
|
144
|
+
className="h-9 justify-between gap-2"
|
|
145
|
+
onClick={() => setFiltersOpen((value) => !value)}
|
|
146
|
+
aria-expanded={filtersOpen}
|
|
147
|
+
>
|
|
148
|
+
{t("sessions.filters")}
|
|
149
|
+
<IconChevronDown
|
|
150
|
+
className={cn(
|
|
151
|
+
"h-4 w-4 transition-transform",
|
|
152
|
+
filtersOpen && "rotate-180",
|
|
153
|
+
)}
|
|
154
|
+
/>
|
|
155
|
+
</Button>
|
|
156
|
+
<Button
|
|
157
|
+
type="button"
|
|
158
|
+
variant="ghost"
|
|
159
|
+
size="icon"
|
|
160
|
+
className="h-9 w-9"
|
|
161
|
+
onClick={() => void refetch()}
|
|
162
|
+
disabled={isFetching}
|
|
163
|
+
aria-label={t("sessions.refresh")}
|
|
164
|
+
>
|
|
165
|
+
<IconRefresh
|
|
166
|
+
className={cn("h-4 w-4", isFetching && "animate-spin")}
|
|
167
|
+
/>
|
|
168
|
+
</Button>
|
|
188
169
|
</div>
|
|
170
|
+
{filtersOpen ? (
|
|
171
|
+
<div className="mt-3 grid gap-3 border-t pt-3 lg:grid-cols-2">
|
|
172
|
+
<div className="rounded-md border bg-muted/20 p-3">
|
|
173
|
+
<div className="mb-2 text-[11px] font-semibold uppercase text-muted-foreground">
|
|
174
|
+
{t("sessions.userFilters")}
|
|
175
|
+
</div>
|
|
176
|
+
<Input
|
|
177
|
+
value={app}
|
|
178
|
+
onChange={(event) => updateFilter("app", event.target.value)}
|
|
179
|
+
placeholder={t("sessions.appPlaceholder")}
|
|
180
|
+
className="h-9"
|
|
181
|
+
/>
|
|
182
|
+
</div>
|
|
183
|
+
<div className="rounded-md border bg-muted/20 p-3">
|
|
184
|
+
<div className="mb-2 text-[11px] font-semibold uppercase text-muted-foreground">
|
|
185
|
+
{t("sessions.eventFilters")}
|
|
186
|
+
</div>
|
|
187
|
+
<div className="flex h-9 items-center rounded-md border bg-background px-3 text-sm text-muted-foreground">
|
|
188
|
+
{t("sessions.anyActivity")}
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
) : null}
|
|
189
193
|
</CardContent>
|
|
190
194
|
</Card>
|
|
191
195
|
|
|
@@ -200,40 +204,65 @@ export default function SessionsPage() {
|
|
|
200
204
|
) : recordings.length === 0 ? (
|
|
201
205
|
<EmptySessionsState />
|
|
202
206
|
) : (
|
|
203
|
-
<div
|
|
204
|
-
<
|
|
205
|
-
<
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
207
|
+
<div>
|
|
208
|
+
<div className="flex items-center justify-between border-b px-4 py-3">
|
|
209
|
+
<div className="text-sm font-medium">
|
|
210
|
+
{t("sessions.sessionPlaylist")}
|
|
211
|
+
</div>
|
|
212
|
+
<div className="text-xs text-muted-foreground">
|
|
213
|
+
{t("sessions.showing", {
|
|
214
|
+
count: String(recordings.length),
|
|
215
|
+
})}
|
|
216
|
+
</div>
|
|
217
|
+
</div>
|
|
218
|
+
<div className="divide-y">
|
|
219
|
+
{recordings.map((recording) => {
|
|
220
|
+
const href = `/sessions/${encodeURIComponent(recording.id)}`;
|
|
221
|
+
const lastSeen =
|
|
222
|
+
recording.endedAt ??
|
|
223
|
+
recording.lastIngestedAt ??
|
|
224
|
+
recording.startedAt;
|
|
225
|
+
return (
|
|
226
|
+
<button
|
|
227
|
+
key={recording.id}
|
|
228
|
+
type="button"
|
|
229
|
+
className="grid w-full gap-3 px-4 py-3 text-left transition-colors hover:bg-muted/35 focus-visible:bg-muted/35 focus-visible:outline-none md:grid-cols-[104px_minmax(170px,0.85fr)_minmax(260px,1.25fr)_minmax(130px,0.55fr)] md:items-center"
|
|
230
|
+
onClick={() => navigate(href)}
|
|
231
|
+
aria-label={t("sessions.watchReplay")}
|
|
232
|
+
>
|
|
233
|
+
<span className="inline-flex h-10 w-[92px] items-center justify-center gap-2 rounded-md bg-primary/10 font-medium text-primary">
|
|
234
|
+
<IconPlayerPlay className="h-4 w-4 fill-current" />
|
|
235
|
+
{formatDuration(recording.durationMs)}
|
|
236
|
+
</span>
|
|
237
|
+
<span className="min-w-0">
|
|
238
|
+
<span className="block truncate text-sm font-medium text-foreground">
|
|
239
|
+
{visitorLabel(recording, t)}
|
|
240
|
+
</span>
|
|
241
|
+
<span className="mt-0.5 block truncate text-xs text-muted-foreground">
|
|
242
|
+
{formatDateTime(lastSeen)} ·{" "}
|
|
243
|
+
{t("sessions.eventCountCompact", {
|
|
244
|
+
count: formatNumber(recording.eventCount),
|
|
245
|
+
})}
|
|
246
|
+
</span>
|
|
247
|
+
</span>
|
|
248
|
+
<span className="min-w-0">
|
|
249
|
+
<span className="block truncate text-sm font-medium text-primary">
|
|
250
|
+
{pathLabel(recording)}
|
|
251
|
+
</span>
|
|
252
|
+
<span className="mt-0.5 block truncate text-xs text-muted-foreground">
|
|
253
|
+
{recording.hostname ||
|
|
254
|
+
recording.app ||
|
|
255
|
+
recording.template ||
|
|
256
|
+
shortId(recording.sessionId)}
|
|
257
|
+
</span>
|
|
258
|
+
</span>
|
|
259
|
+
<span className="min-w-0 text-left md:text-right">
|
|
260
|
+
<span className="block truncate text-sm text-muted-foreground">
|
|
261
|
+
{recording.app ||
|
|
262
|
+
recording.template ||
|
|
263
|
+
t("sessions.unknownApp")}
|
|
264
|
+
</span>
|
|
265
|
+
<span className="mt-1 flex flex-wrap gap-1.5 md:justify-end">
|
|
237
266
|
{recording.errorCount > 0 ? (
|
|
238
267
|
<Badge variant="destructive">
|
|
239
268
|
{t("sessions.errorCount", {
|
|
@@ -248,39 +277,20 @@ export default function SessionsPage() {
|
|
|
248
277
|
})}
|
|
249
278
|
</Badge>
|
|
250
279
|
) : null}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
</
|
|
260
|
-
</
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
recording.startedAt,
|
|
266
|
-
)}
|
|
267
|
-
</TableCell>
|
|
268
|
-
<TableCell>
|
|
269
|
-
{formatDuration(recording.durationMs)}
|
|
270
|
-
</TableCell>
|
|
271
|
-
<TableCell className="text-end">
|
|
272
|
-
{formatNumber(recording.eventCount)}
|
|
273
|
-
</TableCell>
|
|
274
|
-
<TableCell className="text-end">
|
|
275
|
-
{formatNumber(recording.chunkCount)}
|
|
276
|
-
</TableCell>
|
|
277
|
-
<TableCell className="text-end">
|
|
278
|
-
{formatNumber(recording.pageCount)}
|
|
279
|
-
</TableCell>
|
|
280
|
-
</TableRow>
|
|
281
|
-
))}
|
|
282
|
-
</TableBody>
|
|
283
|
-
</Table>
|
|
280
|
+
{recording.errorCount === 0 &&
|
|
281
|
+
recording.rageClickCount === 0 ? (
|
|
282
|
+
<span className="text-xs text-muted-foreground">
|
|
283
|
+
{t("sessions.pageCountCompact", {
|
|
284
|
+
count: formatNumber(recording.pageCount),
|
|
285
|
+
})}
|
|
286
|
+
</span>
|
|
287
|
+
) : null}
|
|
288
|
+
</span>
|
|
289
|
+
</span>
|
|
290
|
+
</button>
|
|
291
|
+
);
|
|
292
|
+
})}
|
|
293
|
+
</div>
|
|
284
294
|
</div>
|
|
285
295
|
)}
|
|
286
296
|
</CardContent>
|
|
@@ -374,6 +384,22 @@ function shortId(value: string): string {
|
|
|
374
384
|
: value;
|
|
375
385
|
}
|
|
376
386
|
|
|
387
|
+
function pathLabel(recording: SessionRecordingSummary): string {
|
|
388
|
+
if (recording.path) return recording.path;
|
|
389
|
+
if (recording.lastUrl) return safePathFromUrl(recording.lastUrl);
|
|
390
|
+
if (recording.firstUrl) return safePathFromUrl(recording.firstUrl);
|
|
391
|
+
return shortId(recording.sessionId);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function safePathFromUrl(value: string): string {
|
|
395
|
+
try {
|
|
396
|
+
const url = new URL(value);
|
|
397
|
+
return `${url.pathname}${url.search}` || url.hostname;
|
|
398
|
+
} catch {
|
|
399
|
+
return value;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
377
403
|
function formatDateTime(value: string): string {
|
|
378
404
|
const date = new Date(value);
|
|
379
405
|
if (!Number.isFinite(date.getTime())) return value;
|
|
@@ -409,7 +435,8 @@ configureTracking({
|
|
|
409
435
|
endpoint: "https://analytics.example.com/api/analytics/track",
|
|
410
436
|
sessionReplay: {
|
|
411
437
|
enabled: true,
|
|
412
|
-
|
|
438
|
+
requireSignedInUser: true,
|
|
439
|
+
sampleRate: 0.1,
|
|
413
440
|
},
|
|
414
441
|
getDefaultProps: (_event, props) => ({
|
|
415
442
|
...props,
|
|
@@ -452,6 +452,27 @@ export async function claimDashboardReportSubscription(
|
|
|
452
452
|
return rows[0] ? rowToSubscription(rows[0]) : null;
|
|
453
453
|
}
|
|
454
454
|
|
|
455
|
+
export async function queueDashboardReportSubscriptionNow(
|
|
456
|
+
id: string,
|
|
457
|
+
ctx: AccessCtx,
|
|
458
|
+
now: Date = new Date(),
|
|
459
|
+
): Promise<DashboardReportSubscription | null> {
|
|
460
|
+
const db = getDb() as any;
|
|
461
|
+
const queuedAt = now.toISOString();
|
|
462
|
+
const rows = await db
|
|
463
|
+
.update(schema.dashboardReportSubscriptions)
|
|
464
|
+
.set({
|
|
465
|
+
enabled: true,
|
|
466
|
+
nextRunAt: queuedAt,
|
|
467
|
+
lastStatus: null,
|
|
468
|
+
lastError: null,
|
|
469
|
+
updatedAt: queuedAt,
|
|
470
|
+
})
|
|
471
|
+
.where(ownerWhere(ctx, undefined, id))
|
|
472
|
+
.returning();
|
|
473
|
+
return rows[0] ? rowToSubscription(rows[0]) : null;
|
|
474
|
+
}
|
|
475
|
+
|
|
455
476
|
export async function markDashboardReportResult(
|
|
456
477
|
sub: DashboardReportSubscription,
|
|
457
478
|
status: "success" | "error",
|
|
@@ -205,11 +205,17 @@ async function launchScreenshotBrowser() {
|
|
|
205
205
|
|
|
206
206
|
if (isServerlessBrowserRuntime()) {
|
|
207
207
|
const { default: chromium } = await import("@sparticuz/chromium-min");
|
|
208
|
+
chromium.setGraphicsMode = false;
|
|
208
209
|
const packUrl =
|
|
209
210
|
process.env.DASHBOARD_REPORT_CHROMIUM_PACK_URL?.trim() ||
|
|
210
211
|
DEFAULT_SERVERLESS_CHROMIUM_PACK_URL;
|
|
211
212
|
return playwright.launch({
|
|
212
|
-
args:
|
|
213
|
+
args: [
|
|
214
|
+
...chromium.args,
|
|
215
|
+
"--disable-dev-shm-usage",
|
|
216
|
+
"--disable-gpu",
|
|
217
|
+
"--hide-scrollbars",
|
|
218
|
+
],
|
|
213
219
|
executablePath: await chromium.executablePath(packUrl),
|
|
214
220
|
headless: true,
|
|
215
221
|
});
|
|
@@ -219,22 +225,40 @@ async function launchScreenshotBrowser() {
|
|
|
219
225
|
}
|
|
220
226
|
|
|
221
227
|
async function waitForDashboardReportReady(page: any): Promise<void> {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
await
|
|
236
|
-
|
|
237
|
-
|
|
228
|
+
try {
|
|
229
|
+
await page.waitForFunction(
|
|
230
|
+
`(() => {
|
|
231
|
+
const root = document.querySelector("[data-dashboard-report-capture]");
|
|
232
|
+
if (!root) return false;
|
|
233
|
+
if (root.getAttribute("data-dashboard-report-ready") !== "true") {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
return !root.querySelector("[data-dashboard-report-loading='true']");
|
|
237
|
+
})()`,
|
|
238
|
+
undefined,
|
|
239
|
+
{ timeout: 90_000 },
|
|
240
|
+
);
|
|
241
|
+
await page.evaluate(`(async () => {
|
|
242
|
+
await document.fonts?.ready;
|
|
243
|
+
})()`);
|
|
244
|
+
await page.waitForTimeout(750);
|
|
245
|
+
} catch (err: any) {
|
|
246
|
+
const detail = await page
|
|
247
|
+
.evaluate(`(() => {
|
|
248
|
+
const root = document.querySelector("[data-dashboard-report-capture]");
|
|
249
|
+
return {
|
|
250
|
+
ready: root?.getAttribute("data-dashboard-report-ready") ?? null,
|
|
251
|
+
loadingCount: root?.querySelectorAll("[data-dashboard-report-loading='true']").length ?? null,
|
|
252
|
+
text: document.body?.innerText?.slice(0, 1000) ?? "",
|
|
253
|
+
url: location.href,
|
|
254
|
+
};
|
|
255
|
+
})()`)
|
|
256
|
+
.catch(() => null);
|
|
257
|
+
const message = detail
|
|
258
|
+
? `${err?.message ?? String(err)}; dashboard state: ${JSON.stringify(detail)}`
|
|
259
|
+
: `${err?.message ?? String(err)}; dashboard page was not inspectable`;
|
|
260
|
+
throw new Error(message);
|
|
261
|
+
}
|
|
238
262
|
}
|
|
239
263
|
|
|
240
264
|
async function scrollDashboardForLazyRendering(page: any): Promise<void> {
|
|
@@ -36,6 +36,8 @@ const INITIAL_TOOL_NAMES = [
|
|
|
36
36
|
"save-analysis",
|
|
37
37
|
"rename-analysis",
|
|
38
38
|
"delete-analysis",
|
|
39
|
+
"get-sql-dashboard",
|
|
40
|
+
"mutate-dashboard",
|
|
39
41
|
"generate-chart",
|
|
40
42
|
"bigquery",
|
|
41
43
|
"search-bigquery-schema",
|
|
@@ -172,6 +174,9 @@ export default createAgentChatPlugin({
|
|
|
172
174
|
"Apply real-data requirements only when presenting analytics results, source records, or derived metrics. Do not call data-source tools for workflow migration, recurring-job setup, UI/code fixes, settings help, conceptual planning, or other non-data tasks unless the user explicitly asks for data. " +
|
|
173
175
|
"SURFACE DIFFERENTIATION — You are the analytics assistant for definitions, deep-dive analysis, and action. For questions about what a metric, model, or table means, use the Data Dictionary and configured schema tools first. For trends, comparisons, anomalies, current data, or anything that requires querying live data, answer directly in chat with the relevant provider query, dashboard analysis, and inline charts when useful. " +
|
|
174
176
|
"DASHBOARD CREATION RULE — You may create dashboards, analyses, SQL panels, or other resources only when the user explicitly asks you to (e.g. 'build me a dashboard for...', 'create a new analysis', 'add a chart for...'). Never create any resource proactively during research, trend analysis, or answering questions. If you think a dashboard would be useful, suggest it and wait for explicit confirmation before creating anything. Never add new items to the sidebar or modify existing dashboards without an explicit user directive. " +
|
|
177
|
+
"DASHBOARD MUTATION RULE — For dashboard edits, default to `mutate-dashboard` with the typed `dashboard.*` script API so the main payload is a string and avoids native-array serialization traps. It can move panels by id, edit titles/SQL/config, insert, duplicate, remove, and patch dashboard fields in one atomic save. The script API is constrained: no variables/imports/loops/functions, only JSON-compatible arguments on documented dashboard methods. Do not count shifting `/panels/<index>` positions for ordinary dashboard edits unless the user specifically asks for low-level JSON-pointer operations. " +
|
|
178
|
+
"DASHBOARD READ RULE — `get-sql-dashboard` is compact by default: use its `panels` summaries plus `layout.panelOrder` and `layout.firstPanelIds` for orientation and verification. Pass `includeConfig: true` only when you truly need full panel SQL/config. " +
|
|
179
|
+
'DASHBOARD REORDER RULE — For simple chart/section moves, use `mutate-dashboard` code such as `dashboard.panels(["panel-a","panel-b"]).moveToTop();`. Never count shifting `/panels/<index>` positions for ordinary \'move this chart\' requests. Use `get-sql-dashboard.layout.firstPanelIds` or `mutate-dashboard.panelOrder` as proof of placement. ' +
|
|
175
180
|
"Use configured data sources and actions only. Call `data-source-status` when you need to know which providers are connected, and treat provider actions as unavailable for analysis if they return missing credentials, permission, syntax, quota, or network errors. " +
|
|
176
181
|
"The built-in `demo` dashboard source is a demo-environment Prometheus source reserved for the Node Exporter demo. It must never satisfy REAL_DATA_REQUIRED or be cited as user analytics evidence unless the user explicitly asks to inspect the demo dashboard. " +
|
|
177
182
|
"When the user names a provider or tool such as Jira, Pylon, HubSpot, Gong, Slack, Sentry, GA4, or BigQuery, that named source is authoritative for the turn: use that provider's real tool/API surface, not a warehouse or different-provider substitute, unless the user explicitly asks for the copy/fallback. For bounded lookups where a first-class action fully models the requested source, object, filter, and pagination need, that shortcut is fine. For broad provider searches, cross-source joins, corpus-wide counts, exact cohort coverage, or any answer where absence matters, do not start and stop with shortcuts; use the broad provider API/MCP and corpus/code workflow as the primary path. " +
|
package/dist/action-ui.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export declare const ACTION_CHAT_UI_DATA_TABLE_RENDERER = "core.data-table";
|
|
|
2
2
|
export declare const ACTION_CHAT_UI_DATA_CHART_RENDERER = "core.data-chart";
|
|
3
3
|
export declare const ACTION_CHAT_UI_DATA_INSIGHTS_RENDERER = "core.data-insights";
|
|
4
4
|
export declare const ACTION_CHAT_UI_DATA_WIDGET_RENDERER = "core.data-widget";
|
|
5
|
+
export declare const ACTION_CHAT_UI_INLINE_EXTENSION_RENDERER = "core.inline-extension";
|
|
5
6
|
export interface ActionChatUIConfig {
|
|
6
7
|
/**
|
|
7
8
|
* Exact renderer id to use in Agent-Native chat. This is native first-party
|
package/dist/action-ui.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action-ui.d.ts","sourceRoot":"","sources":["../src/action-ui.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kCAAkC,oBAAoB,CAAC;AACpE,eAAO,MAAM,kCAAkC,oBAAoB,CAAC;AACpE,eAAO,MAAM,qCAAqC,uBAAuB,CAAC;AAC1E,eAAO,MAAM,mCAAmC,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"action-ui.d.ts","sourceRoot":"","sources":["../src/action-ui.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kCAAkC,oBAAoB,CAAC;AACpE,eAAO,MAAM,kCAAkC,oBAAoB,CAAC;AACpE,eAAO,MAAM,qCAAqC,uBAAuB,CAAC;AAC1E,eAAO,MAAM,mCAAmC,qBAAqB,CAAC;AACtE,eAAO,MAAM,wCAAwC,0BAA0B,CAAC;AAEhF,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,OAAO,GACb,kBAAkB,GAAG,SAAS,CAiBhC"}
|
package/dist/action-ui.js
CHANGED
|
@@ -2,6 +2,7 @@ export const ACTION_CHAT_UI_DATA_TABLE_RENDERER = "core.data-table";
|
|
|
2
2
|
export const ACTION_CHAT_UI_DATA_CHART_RENDERER = "core.data-chart";
|
|
3
3
|
export const ACTION_CHAT_UI_DATA_INSIGHTS_RENDERER = "core.data-insights";
|
|
4
4
|
export const ACTION_CHAT_UI_DATA_WIDGET_RENDERER = "core.data-widget";
|
|
5
|
+
export const ACTION_CHAT_UI_INLINE_EXTENSION_RENDERER = "core.inline-extension";
|
|
5
6
|
export function normalizeActionChatUIConfig(value) {
|
|
6
7
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
7
8
|
return undefined;
|
package/dist/action-ui.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action-ui.js","sourceRoot":"","sources":["../src/action-ui.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,kCAAkC,GAAG,iBAAiB,CAAC;AACpE,MAAM,CAAC,MAAM,kCAAkC,GAAG,iBAAiB,CAAC;AACpE,MAAM,CAAC,MAAM,qCAAqC,GAAG,oBAAoB,CAAC;AAC1E,MAAM,CAAC,MAAM,mCAAmC,GAAG,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"action-ui.js","sourceRoot":"","sources":["../src/action-ui.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,kCAAkC,GAAG,iBAAiB,CAAC;AACpE,MAAM,CAAC,MAAM,kCAAkC,GAAG,iBAAiB,CAAC;AACpE,MAAM,CAAC,MAAM,qCAAqC,GAAG,oBAAoB,CAAC;AAC1E,MAAM,CAAC,MAAM,mCAAmC,GAAG,kBAAkB,CAAC;AACtE,MAAM,CAAC,MAAM,wCAAwC,GAAG,uBAAuB,CAAC;AAchF,MAAM,UAAU,2BAA2B,CACzC,KAAc;IAEd,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAChE,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,MAAM,GAAG,KAAgC,CAAC;IAChD,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;QACnE,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO;QACL,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE;QAChC,GAAG,CAAC,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE;YACzD,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE;YACzB,CAAC,CAAC,EAAE,CAAC;QACP,GAAG,CAAC,OAAO,MAAM,CAAC,WAAW,KAAK,QAAQ,IAAI,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE;YACrE,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE;YACrC,CAAC,CAAC,EAAE,CAAC;KACR,CAAC;AACJ,CAAC","sourcesContent":["export const ACTION_CHAT_UI_DATA_TABLE_RENDERER = \"core.data-table\";\nexport const ACTION_CHAT_UI_DATA_CHART_RENDERER = \"core.data-chart\";\nexport const ACTION_CHAT_UI_DATA_INSIGHTS_RENDERER = \"core.data-insights\";\nexport const ACTION_CHAT_UI_DATA_WIDGET_RENDERER = \"core.data-widget\";\nexport const ACTION_CHAT_UI_INLINE_EXTENSION_RENDERER = \"core.inline-extension\";\n\nexport interface ActionChatUIConfig {\n /**\n * Exact renderer id to use in Agent-Native chat. This is native first-party\n * React UI, distinct from MCP Apps resources for external hosts.\n */\n renderer: string;\n /** Optional label for catalogs, docs, or custom renderer chrome. */\n title?: string;\n /** Optional developer-facing description for catalogs/docs. */\n description?: string;\n}\n\nexport function normalizeActionChatUIConfig(\n value: unknown,\n): ActionChatUIConfig | undefined {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n return undefined;\n }\n const record = value as Record<string, unknown>;\n if (typeof record.renderer !== \"string\" || !record.renderer.trim()) {\n return undefined;\n }\n return {\n renderer: record.renderer.trim(),\n ...(typeof record.title === \"string\" && record.title.trim()\n ? { title: record.title }\n : {}),\n ...(typeof record.description === \"string\" && record.description.trim()\n ? { description: record.description }\n : {}),\n };\n}\n"]}
|