@agent-native/core 0.86.0 → 0.86.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.
Files changed (183) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +9 -0
  3. package/corpus/core/package.json +2 -1
  4. package/corpus/core/src/client/AgentPanel.tsx +10 -0
  5. package/corpus/core/src/client/AssistantChat.tsx +91 -27
  6. package/corpus/core/src/client/ErrorBoundary.tsx +67 -12
  7. package/corpus/core/src/client/ErrorReportActions.tsx +69 -0
  8. package/corpus/core/src/client/FeedbackButton.tsx +8 -4
  9. package/corpus/core/src/client/db-admin/DbAdminPage.tsx +41 -13
  10. package/corpus/core/src/client/db-admin/SqlEditor.tsx +10 -3
  11. package/corpus/core/src/client/db-admin/TableEditor.tsx +12 -5
  12. package/corpus/core/src/client/db-admin/useAgentSync.ts +10 -3
  13. package/corpus/core/src/client/db-admin/useDbAdmin.ts +65 -16
  14. package/corpus/core/src/client/error-reporting.ts +100 -0
  15. package/corpus/core/src/client/index.ts +11 -0
  16. package/corpus/core/src/client/org/RequireActiveOrg.tsx +12 -0
  17. package/corpus/core/src/client/visual-style-controls.tsx +615 -2
  18. package/corpus/core/src/db-admin/index.ts +26 -0
  19. package/corpus/core/src/db-admin/operations.ts +87 -49
  20. package/corpus/core/src/localization/default-messages.ts +4 -0
  21. package/corpus/core/src/server/builder-design-systems.ts +328 -9
  22. package/corpus/core/src/server/index.ts +15 -0
  23. package/corpus/core/src/templates/default/app/root.tsx +47 -0
  24. package/corpus/templates/analytics/AGENTS.md +6 -4
  25. package/corpus/templates/analytics/actions/delete-db-admin-connection.ts +19 -0
  26. package/corpus/templates/analytics/actions/list-db-admin-connections.ts +18 -0
  27. package/corpus/templates/analytics/actions/navigate.ts +17 -3
  28. package/corpus/templates/analytics/actions/save-db-admin-connection.ts +33 -0
  29. package/corpus/templates/analytics/actions/view-screen.ts +11 -3
  30. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +11 -2
  31. package/corpus/templates/analytics/app/i18n/zh-TW.ts +23 -2
  32. package/corpus/templates/analytics/app/i18n-data.ts +230 -20
  33. package/corpus/templates/analytics/app/pages/Agents.tsx +398 -4
  34. package/corpus/templates/analytics/changelog/2026-07-03-connect-other-app-databases-from-analytics.md +6 -0
  35. package/corpus/templates/analytics/server/db/schema.ts +27 -0
  36. package/corpus/templates/analytics/server/lib/db-admin-connections.ts +458 -0
  37. package/corpus/templates/analytics/server/plugins/db.ts +35 -0
  38. package/corpus/templates/analytics/server/routes/[base]/_agent-native/analytics-db-admin/[...path].ts +1 -0
  39. package/corpus/templates/analytics/server/routes/_agent-native/analytics-db-admin/[...path].ts +160 -0
  40. package/corpus/templates/content/app/root.tsx +9 -0
  41. package/corpus/templates/content/changelog/2026-07-03-error-screens-now-include-a-feedback-button-with-debug-info.md +6 -0
  42. package/corpus/templates/design/AGENTS.md +13 -7
  43. package/corpus/templates/design/actions/get-design-system.ts +21 -0
  44. package/corpus/templates/design/actions/index-design-system-with-builder.ts +14 -37
  45. package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +3 -3
  46. package/corpus/templates/design/app/components/design/code-workbench/SideBar.tsx +70 -9
  47. package/corpus/templates/design/app/components/design/code-workbench/editor/EditorTabs.tsx +11 -2
  48. package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +1 -1
  49. package/corpus/templates/design/app/components/design/code-workbench/editor/StatusBar.tsx +1 -1
  50. package/corpus/templates/design/app/components/design/code-workbench/explorer/ExplorerView.tsx +8 -2
  51. package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +7 -3
  52. package/corpus/templates/design/app/components/design/code-workbench/store.tsx +48 -0
  53. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +6 -0
  54. package/corpus/templates/design/app/i18n/zh-TW.ts +15 -14
  55. package/corpus/templates/design/app/i18n-data.ts +154 -142
  56. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +32 -14
  57. package/corpus/templates/design/changelog/2026-07-03-design-system-setup-now-indexes-figma-code-and-design-md-sou.md +6 -0
  58. package/corpus/templates/design/changelog/2026-07-03-the-code-editor-is-easier-to-use-with-top-sidebar-tabs-clear.md +6 -0
  59. package/corpus/templates/design/server/lib/builder-design-system-proxy.ts +16 -106
  60. package/corpus/templates/mail/app/root.tsx +60 -1
  61. package/corpus/templates/mail/changelog/2026-07-03-error-screens-now-include-a-feedback-button-with-debug-info.md +6 -0
  62. package/corpus/templates/plan/app/i18n/ar-SA.ts +3 -0
  63. package/corpus/templates/plan/app/i18n/de-DE.ts +4 -0
  64. package/corpus/templates/plan/app/i18n/en-US.ts +4 -0
  65. package/corpus/templates/plan/app/i18n/es-ES.ts +4 -0
  66. package/corpus/templates/plan/app/i18n/fr-FR.ts +4 -0
  67. package/corpus/templates/plan/app/i18n/hi-IN.ts +3 -0
  68. package/corpus/templates/plan/app/i18n/ja-JP.ts +4 -0
  69. package/corpus/templates/plan/app/i18n/ko-KR.ts +4 -0
  70. package/corpus/templates/plan/app/i18n/pt-BR.ts +4 -0
  71. package/corpus/templates/plan/app/i18n/zh-CN.ts +3 -0
  72. package/corpus/templates/plan/app/i18n/zh-TW.ts +3 -0
  73. package/corpus/templates/plan/app/pages/PlansPage.tsx +74 -3
  74. package/corpus/templates/plan/changelog/2026-07-03-error-screens-now-include-a-feedback-button-with-debug-info.md +6 -0
  75. package/corpus/templates/slides/AGENTS.md +7 -3
  76. package/corpus/templates/slides/actions/get-design-system.ts +21 -0
  77. package/corpus/templates/slides/actions/import-file.ts +15 -7
  78. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +14 -37
  79. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +32 -8
  80. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +2 -2
  81. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +2 -4
  82. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +163 -154
  83. package/corpus/templates/slides/app/components/editor/SlideStyleInspector.tsx +19 -15
  84. package/corpus/templates/slides/app/components/editor/SpeakerNotesPanel.tsx +56 -6
  85. package/corpus/templates/slides/app/components/layout/Layout.tsx +6 -1
  86. package/corpus/templates/slides/app/i18n/ar-SA.ts +8 -7
  87. package/corpus/templates/slides/app/i18n/de-DE.ts +7 -8
  88. package/corpus/templates/slides/app/i18n/en-US.ts +8 -7
  89. package/corpus/templates/slides/app/i18n/es-ES.ts +7 -8
  90. package/corpus/templates/slides/app/i18n/fr-FR.ts +7 -8
  91. package/corpus/templates/slides/app/i18n/hi-IN.ts +8 -7
  92. package/corpus/templates/slides/app/i18n/ja-JP.ts +7 -8
  93. package/corpus/templates/slides/app/i18n/ko-KR.ts +7 -7
  94. package/corpus/templates/slides/app/i18n/pt-BR.ts +7 -8
  95. package/corpus/templates/slides/app/i18n/zh-CN.ts +8 -7
  96. package/corpus/templates/slides/app/i18n/zh-TW.ts +8 -7
  97. package/corpus/templates/slides/app/lib/canvas-zoom.ts +16 -1
  98. package/corpus/templates/slides/app/pages/DeckEditor.tsx +3 -3
  99. package/corpus/templates/slides/changelog/2026-07-03-design-system-setup-now-indexes-figma-code-and-design-md-sou.md +6 -0
  100. package/corpus/templates/slides/changelog/2026-07-03-slide-editing-now-keeps-thumbnails-speaker-notes-styling-and.md +6 -0
  101. package/corpus/templates/slides/server/lib/builder-design-system-proxy.ts +16 -104
  102. package/dist/client/AgentPanel.d.ts.map +1 -1
  103. package/dist/client/AgentPanel.js +2 -1
  104. package/dist/client/AgentPanel.js.map +1 -1
  105. package/dist/client/AssistantChat.d.ts.map +1 -1
  106. package/dist/client/AssistantChat.js +77 -25
  107. package/dist/client/AssistantChat.js.map +1 -1
  108. package/dist/client/ErrorBoundary.d.ts.map +1 -1
  109. package/dist/client/ErrorBoundary.js +37 -2
  110. package/dist/client/ErrorBoundary.js.map +1 -1
  111. package/dist/client/ErrorReportActions.d.ts +13 -0
  112. package/dist/client/ErrorReportActions.d.ts.map +1 -0
  113. package/dist/client/ErrorReportActions.js +14 -0
  114. package/dist/client/ErrorReportActions.js.map +1 -0
  115. package/dist/client/FeedbackButton.d.ts +3 -1
  116. package/dist/client/FeedbackButton.d.ts.map +1 -1
  117. package/dist/client/FeedbackButton.js +6 -5
  118. package/dist/client/FeedbackButton.js.map +1 -1
  119. package/dist/client/db-admin/DbAdminPage.d.ts +9 -1
  120. package/dist/client/db-admin/DbAdminPage.d.ts.map +1 -1
  121. package/dist/client/db-admin/DbAdminPage.js +17 -13
  122. package/dist/client/db-admin/DbAdminPage.js.map +1 -1
  123. package/dist/client/db-admin/SqlEditor.d.ts +3 -1
  124. package/dist/client/db-admin/SqlEditor.d.ts.map +1 -1
  125. package/dist/client/db-admin/SqlEditor.js +3 -3
  126. package/dist/client/db-admin/SqlEditor.js.map +1 -1
  127. package/dist/client/db-admin/TableEditor.d.ts +3 -1
  128. package/dist/client/db-admin/TableEditor.d.ts.map +1 -1
  129. package/dist/client/db-admin/TableEditor.js +6 -6
  130. package/dist/client/db-admin/TableEditor.js.map +1 -1
  131. package/dist/client/db-admin/useAgentSync.d.ts +3 -2
  132. package/dist/client/db-admin/useAgentSync.d.ts.map +1 -1
  133. package/dist/client/db-admin/useAgentSync.js +8 -4
  134. package/dist/client/db-admin/useAgentSync.js.map +1 -1
  135. package/dist/client/db-admin/useDbAdmin.d.ts +11 -7
  136. package/dist/client/db-admin/useDbAdmin.d.ts.map +1 -1
  137. package/dist/client/db-admin/useDbAdmin.js +45 -20
  138. package/dist/client/db-admin/useDbAdmin.js.map +1 -1
  139. package/dist/client/error-reporting.d.ts +19 -0
  140. package/dist/client/error-reporting.d.ts.map +1 -0
  141. package/dist/client/error-reporting.js +71 -0
  142. package/dist/client/error-reporting.js.map +1 -0
  143. package/dist/client/index.d.ts +3 -1
  144. package/dist/client/index.d.ts.map +1 -1
  145. package/dist/client/index.js +3 -1
  146. package/dist/client/index.js.map +1 -1
  147. package/dist/client/org/RequireActiveOrg.d.ts.map +1 -1
  148. package/dist/client/org/RequireActiveOrg.js +2 -1
  149. package/dist/client/org/RequireActiveOrg.js.map +1 -1
  150. package/dist/client/visual-style-controls.d.ts +11 -0
  151. package/dist/client/visual-style-controls.d.ts.map +1 -1
  152. package/dist/client/visual-style-controls.js +350 -1
  153. package/dist/client/visual-style-controls.js.map +1 -1
  154. package/dist/collab/routes.d.ts +1 -1
  155. package/dist/collab/struct-routes.d.ts +1 -1
  156. package/dist/db-admin/index.d.ts +3 -0
  157. package/dist/db-admin/index.d.ts.map +1 -0
  158. package/dist/db-admin/index.js +2 -0
  159. package/dist/db-admin/index.js.map +1 -0
  160. package/dist/db-admin/operations.d.ts +26 -5
  161. package/dist/db-admin/operations.d.ts.map +1 -1
  162. package/dist/db-admin/operations.js +66 -55
  163. package/dist/db-admin/operations.js.map +1 -1
  164. package/dist/localization/default-messages.d.ts +3 -0
  165. package/dist/localization/default-messages.d.ts.map +1 -1
  166. package/dist/localization/default-messages.js +3 -0
  167. package/dist/localization/default-messages.js.map +1 -1
  168. package/dist/notifications/routes.d.ts +1 -1
  169. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  170. package/dist/resources/handlers.d.ts +1 -1
  171. package/dist/secrets/routes.d.ts +9 -9
  172. package/dist/server/builder-design-systems.d.ts +61 -0
  173. package/dist/server/builder-design-systems.d.ts.map +1 -1
  174. package/dist/server/builder-design-systems.js +219 -14
  175. package/dist/server/builder-design-systems.js.map +1 -1
  176. package/dist/server/index.d.ts +1 -1
  177. package/dist/server/index.d.ts.map +1 -1
  178. package/dist/server/index.js +1 -1
  179. package/dist/server/index.js.map +1 -1
  180. package/dist/server/transcribe-voice.d.ts +1 -1
  181. package/dist/templates/default/app/root.tsx +47 -0
  182. package/package.json +2 -1
  183. package/src/templates/default/app/root.tsx +47 -0
@@ -1,13 +1,42 @@
1
- import { ObservabilityDashboard, useT } from "@agent-native/core/client";
1
+ import {
2
+ agentNativePath,
3
+ ObservabilityDashboard,
4
+ useActionMutation,
5
+ useActionQuery,
6
+ useT,
7
+ } from "@agent-native/core/client";
2
8
  import { DbAdminPage } from "@agent-native/core/client/db-admin";
3
9
  import {
4
10
  IconActivity,
11
+ IconAlertTriangle,
5
12
  IconChevronDown,
6
13
  IconDatabase,
14
+ IconLoader2,
15
+ IconPlus,
16
+ IconTrash,
7
17
  } from "@tabler/icons-react";
18
+ import { useEffect, useMemo, useState, type FormEvent } from "react";
8
19
  import { Link, useSearchParams } from "react-router";
9
20
 
21
+ import {
22
+ AlertDialog,
23
+ AlertDialogAction,
24
+ AlertDialogCancel,
25
+ AlertDialogContent,
26
+ AlertDialogDescription,
27
+ AlertDialogFooter,
28
+ AlertDialogHeader,
29
+ AlertDialogTitle,
30
+ } from "@/components/ui/alert-dialog";
10
31
  import { Button } from "@/components/ui/button";
32
+ import {
33
+ Dialog,
34
+ DialogContent,
35
+ DialogDescription,
36
+ DialogFooter,
37
+ DialogHeader,
38
+ DialogTitle,
39
+ } from "@/components/ui/dialog";
11
40
  import {
12
41
  DropdownMenu,
13
42
  DropdownMenuContent,
@@ -16,10 +45,37 @@ import {
16
45
  DropdownMenuSeparator,
17
46
  DropdownMenuTrigger,
18
47
  } from "@/components/ui/dropdown-menu";
48
+ import { Input } from "@/components/ui/input";
49
+ import { Label } from "@/components/ui/label";
50
+ import {
51
+ Select,
52
+ SelectContent,
53
+ SelectItem,
54
+ SelectTrigger,
55
+ SelectValue,
56
+ } from "@/components/ui/select";
19
57
  import { cn } from "@/lib/utils";
20
58
 
21
59
  type AgentAdminView = "monitoring" | "database";
22
60
 
61
+ interface DbAdminConnection {
62
+ id: string;
63
+ name: string;
64
+ appId: string | null;
65
+ appUrl: string | null;
66
+ databaseUrlLast4: string | null;
67
+ hasDatabaseAuthToken: boolean;
68
+ databaseAuthTokenLast4: string | null;
69
+ }
70
+
71
+ interface SaveDbAdminConnectionInput {
72
+ name: string;
73
+ appId?: string;
74
+ appUrl?: string;
75
+ databaseUrl: string;
76
+ databaseAuthToken?: string;
77
+ }
78
+
23
79
  const AGENT_ADMIN_VIEWS: AgentAdminView[] = ["monitoring", "database"];
24
80
 
25
81
  function parseView(value: string | null): AgentAdminView {
@@ -32,17 +88,27 @@ export default function AgentsPage() {
32
88
  const t = useT();
33
89
  const [searchParams, setSearchParams] = useSearchParams();
34
90
  const view = parseView(searchParams.get("view"));
91
+ const selectedConnectionId = searchParams.get("db");
35
92
 
36
93
  function setView(next: AgentAdminView) {
37
94
  const params = new URLSearchParams(searchParams);
38
95
  if (next === "monitoring") {
39
96
  params.delete("view");
97
+ params.delete("db");
40
98
  } else {
41
99
  params.set("view", next);
42
100
  }
43
101
  setSearchParams(params, { replace: true });
44
102
  }
45
103
 
104
+ function setSelectedConnectionId(id: string | null) {
105
+ const params = new URLSearchParams(searchParams);
106
+ params.set("view", "database");
107
+ if (id) params.set("db", id);
108
+ else params.delete("db");
109
+ setSearchParams(params, { replace: true });
110
+ }
111
+
46
112
  return (
47
113
  <div className="mx-auto flex h-full w-full max-w-7xl flex-col gap-5 px-4 py-5 lg:px-6">
48
114
  <div className="flex flex-wrap items-start justify-between gap-3">
@@ -102,9 +168,10 @@ export default function AgentsPage() {
102
168
  </div>
103
169
 
104
170
  {view === "database" ? (
105
- <div className="min-h-[560px] flex-1 overflow-hidden rounded-lg border bg-background">
106
- <DbAdminPage />
107
- </div>
171
+ <AnalyticsDbAdminPanel
172
+ selectedConnectionId={selectedConnectionId}
173
+ onSelectConnection={setSelectedConnectionId}
174
+ />
108
175
  ) : (
109
176
  <div className="min-w-0">
110
177
  <div className="mb-4 max-w-3xl text-sm leading-6 text-muted-foreground">
@@ -116,3 +183,330 @@ export default function AgentsPage() {
116
183
  </div>
117
184
  );
118
185
  }
186
+
187
+ function AnalyticsDbAdminPanel({
188
+ selectedConnectionId,
189
+ onSelectConnection,
190
+ }: {
191
+ selectedConnectionId: string | null;
192
+ onSelectConnection: (id: string | null) => void;
193
+ }) {
194
+ const t = useT();
195
+ const [connectOpen, setConnectOpen] = useState(false);
196
+ const [deleteOpen, setDeleteOpen] = useState(false);
197
+ const [form, setForm] = useState<SaveDbAdminConnectionInput>({
198
+ name: "",
199
+ appId: "",
200
+ appUrl: "",
201
+ databaseUrl: "",
202
+ databaseAuthToken: "",
203
+ });
204
+ const [formError, setFormError] = useState<string | null>(null);
205
+
206
+ const {
207
+ data: connections = [],
208
+ isLoading,
209
+ error,
210
+ } = useActionQuery<DbAdminConnection[]>(
211
+ "list-db-admin-connections",
212
+ undefined,
213
+ { retry: false },
214
+ );
215
+ const saveConnection = useActionMutation<
216
+ DbAdminConnection,
217
+ SaveDbAdminConnectionInput
218
+ >("save-db-admin-connection");
219
+ const deleteConnection = useActionMutation<
220
+ { deleted: boolean },
221
+ { id: string }
222
+ >("delete-db-admin-connection");
223
+
224
+ const selectedConnection = useMemo(() => {
225
+ return (
226
+ connections.find(
227
+ (connection) => connection.id === selectedConnectionId,
228
+ ) ??
229
+ connections[0] ??
230
+ null
231
+ );
232
+ }, [connections, selectedConnectionId]);
233
+
234
+ useEffect(() => {
235
+ if (!selectedConnection) {
236
+ if (selectedConnectionId) onSelectConnection(null);
237
+ return;
238
+ }
239
+ if (selectedConnection.id !== selectedConnectionId) {
240
+ onSelectConnection(selectedConnection.id);
241
+ }
242
+ }, [onSelectConnection, selectedConnection, selectedConnectionId]);
243
+
244
+ const apiBasePath = selectedConnection
245
+ ? agentNativePath(
246
+ `/_agent-native/analytics-db-admin/${encodeURIComponent(
247
+ selectedConnection.id,
248
+ )}`,
249
+ )
250
+ : null;
251
+
252
+ async function handleSaveConnection(event: FormEvent) {
253
+ event.preventDefault();
254
+ setFormError(null);
255
+ try {
256
+ const saved = await saveConnection.mutateAsync(form);
257
+ setConnectOpen(false);
258
+ setForm({
259
+ name: "",
260
+ appId: "",
261
+ appUrl: "",
262
+ databaseUrl: "",
263
+ databaseAuthToken: "",
264
+ });
265
+ onSelectConnection(saved.id);
266
+ } catch (err) {
267
+ setFormError(err instanceof Error ? err.message : String(err));
268
+ }
269
+ }
270
+
271
+ async function handleDeleteConnection() {
272
+ if (!selectedConnection) return;
273
+ await deleteConnection.mutateAsync({ id: selectedConnection.id });
274
+ setDeleteOpen(false);
275
+ const next = connections.find(
276
+ (connection) => connection.id !== selectedConnection.id,
277
+ );
278
+ onSelectConnection(next?.id ?? null);
279
+ }
280
+
281
+ return (
282
+ <div className="flex min-h-[560px] flex-1 flex-col gap-3">
283
+ <div className="flex flex-wrap items-center justify-between gap-3">
284
+ <div className="min-w-0">
285
+ <h2 className="text-sm font-semibold">
286
+ {t("agents.dbConnectionsTitle")}
287
+ </h2>
288
+ <p className="mt-1 max-w-2xl text-sm leading-6 text-muted-foreground">
289
+ {t("agents.dbConnectionsDescription")}
290
+ </p>
291
+ </div>
292
+ <div className="flex min-w-0 flex-wrap items-center gap-2">
293
+ {connections.length > 0 ? (
294
+ <Select
295
+ value={selectedConnection?.id ?? ""}
296
+ onValueChange={onSelectConnection}
297
+ >
298
+ <SelectTrigger className="h-9 w-[260px] max-w-full">
299
+ <SelectValue placeholder={t("agents.selectConnection")} />
300
+ </SelectTrigger>
301
+ <SelectContent>
302
+ {connections.map((connection) => (
303
+ <SelectItem key={connection.id} value={connection.id}>
304
+ {connection.name}
305
+ </SelectItem>
306
+ ))}
307
+ </SelectContent>
308
+ </Select>
309
+ ) : null}
310
+ <Button size="sm" onClick={() => setConnectOpen(true)}>
311
+ <IconPlus className="me-2 h-4 w-4" />
312
+ {t("agents.connectDatabase")}
313
+ </Button>
314
+ {selectedConnection ? (
315
+ <Button
316
+ type="button"
317
+ variant="outline"
318
+ size="icon"
319
+ aria-label={t("agents.deleteConnection")}
320
+ onClick={() => setDeleteOpen(true)}
321
+ >
322
+ <IconTrash className="h-4 w-4" />
323
+ </Button>
324
+ ) : null}
325
+ </div>
326
+ </div>
327
+
328
+ {error ? (
329
+ <div className="flex items-start gap-2 rounded-md border border-destructive/30 bg-destructive/10 p-3 text-sm text-destructive">
330
+ <IconAlertTriangle className="mt-0.5 h-4 w-4 shrink-0" />
331
+ <span>{error.message}</span>
332
+ </div>
333
+ ) : isLoading ? (
334
+ <div className="flex min-h-[460px] items-center justify-center rounded-lg border bg-background">
335
+ <IconLoader2 className="h-5 w-5 animate-spin text-muted-foreground" />
336
+ </div>
337
+ ) : selectedConnection && apiBasePath ? (
338
+ <div className="min-h-[560px] flex-1 overflow-hidden rounded-lg border bg-background">
339
+ <DbAdminPage
340
+ apiBasePath={apiBasePath}
341
+ cacheScope={`analytics-db-admin:${selectedConnection.id}`}
342
+ title={selectedConnection.name}
343
+ subtitle={
344
+ selectedConnection.appId ??
345
+ selectedConnection.appUrl ??
346
+ t("agents.connectedDatabase")
347
+ }
348
+ codeModeGate={false}
349
+ syncNavigation={false}
350
+ />
351
+ </div>
352
+ ) : (
353
+ <div className="flex min-h-[460px] items-center justify-center rounded-lg border bg-background p-8 text-center">
354
+ <div className="max-w-sm">
355
+ <div className="mx-auto mb-3 flex h-10 w-10 items-center justify-center rounded-full bg-muted">
356
+ <IconDatabase className="h-5 w-5 text-muted-foreground" />
357
+ </div>
358
+ <h2 className="text-sm font-semibold">
359
+ {t("agents.noConnections")}
360
+ </h2>
361
+ <p className="mt-1.5 text-sm leading-6 text-muted-foreground">
362
+ {t("agents.noConnectionsDescription")}
363
+ </p>
364
+ <Button
365
+ className="mt-4"
366
+ size="sm"
367
+ onClick={() => setConnectOpen(true)}
368
+ >
369
+ <IconPlus className="me-2 h-4 w-4" />
370
+ {t("agents.connectDatabase")}
371
+ </Button>
372
+ </div>
373
+ </div>
374
+ )}
375
+
376
+ <Dialog open={connectOpen} onOpenChange={setConnectOpen}>
377
+ <DialogContent>
378
+ <DialogHeader>
379
+ <DialogTitle>{t("agents.connectDatabase")}</DialogTitle>
380
+ <DialogDescription>
381
+ {t("agents.connectDatabaseDescription")}
382
+ </DialogDescription>
383
+ </DialogHeader>
384
+ <form className="grid gap-4" onSubmit={handleSaveConnection}>
385
+ <div className="grid gap-2">
386
+ <Label htmlFor="db-connection-name">
387
+ {t("agents.connectionName")}
388
+ </Label>
389
+ <Input
390
+ id="db-connection-name"
391
+ value={form.name}
392
+ onChange={(event) =>
393
+ setForm((current) => ({
394
+ ...current,
395
+ name: event.target.value,
396
+ }))
397
+ }
398
+ required
399
+ />
400
+ </div>
401
+ <div className="grid gap-2 sm:grid-cols-2">
402
+ <div className="grid gap-2">
403
+ <Label htmlFor="db-connection-app-id">
404
+ {t("agents.connectionAppId")}
405
+ </Label>
406
+ <Input
407
+ id="db-connection-app-id"
408
+ value={form.appId}
409
+ onChange={(event) =>
410
+ setForm((current) => ({
411
+ ...current,
412
+ appId: event.target.value,
413
+ }))
414
+ }
415
+ />
416
+ </div>
417
+ <div className="grid gap-2">
418
+ <Label htmlFor="db-connection-app-url">
419
+ {t("agents.connectionAppUrl")}
420
+ </Label>
421
+ <Input
422
+ id="db-connection-app-url"
423
+ value={form.appUrl}
424
+ onChange={(event) =>
425
+ setForm((current) => ({
426
+ ...current,
427
+ appUrl: event.target.value,
428
+ }))
429
+ }
430
+ />
431
+ </div>
432
+ </div>
433
+ <div className="grid gap-2">
434
+ <Label htmlFor="db-connection-url">
435
+ {t("agents.connectionDatabaseUrl")}
436
+ </Label>
437
+ <Input
438
+ id="db-connection-url"
439
+ value={form.databaseUrl}
440
+ onChange={(event) =>
441
+ setForm((current) => ({
442
+ ...current,
443
+ databaseUrl: event.target.value,
444
+ }))
445
+ }
446
+ type="password"
447
+ autoComplete="off"
448
+ required
449
+ />
450
+ </div>
451
+ <div className="grid gap-2">
452
+ <Label htmlFor="db-connection-auth-token">
453
+ {t("agents.connectionAuthToken")}
454
+ </Label>
455
+ <Input
456
+ id="db-connection-auth-token"
457
+ value={form.databaseAuthToken}
458
+ onChange={(event) =>
459
+ setForm((current) => ({
460
+ ...current,
461
+ databaseAuthToken: event.target.value,
462
+ }))
463
+ }
464
+ type="password"
465
+ autoComplete="off"
466
+ />
467
+ </div>
468
+ {formError ? (
469
+ <p className="text-sm text-destructive">{formError}</p>
470
+ ) : null}
471
+ <DialogFooter>
472
+ <Button
473
+ type="button"
474
+ variant="outline"
475
+ onClick={() => setConnectOpen(false)}
476
+ >
477
+ {t("sidebar.cancel")}
478
+ </Button>
479
+ <Button type="submit" disabled={saveConnection.isPending}>
480
+ {saveConnection.isPending ? (
481
+ <IconLoader2 className="me-2 h-4 w-4 animate-spin" />
482
+ ) : null}
483
+ {t("agents.saveConnection")}
484
+ </Button>
485
+ </DialogFooter>
486
+ </form>
487
+ </DialogContent>
488
+ </Dialog>
489
+
490
+ <AlertDialog open={deleteOpen} onOpenChange={setDeleteOpen}>
491
+ <AlertDialogContent>
492
+ <AlertDialogHeader>
493
+ <AlertDialogTitle>
494
+ {t("agents.deleteConnectionTitle")}
495
+ </AlertDialogTitle>
496
+ <AlertDialogDescription>
497
+ {t("agents.deleteConnectionDescription", {
498
+ name: selectedConnection?.name ?? t("agents.connectedDatabase"),
499
+ })}
500
+ </AlertDialogDescription>
501
+ </AlertDialogHeader>
502
+ <AlertDialogFooter>
503
+ <AlertDialogCancel>{t("sidebar.cancel")}</AlertDialogCancel>
504
+ <AlertDialogAction onClick={handleDeleteConnection}>
505
+ {t("agents.deleteConnection")}
506
+ </AlertDialogAction>
507
+ </AlertDialogFooter>
508
+ </AlertDialogContent>
509
+ </AlertDialog>
510
+ </div>
511
+ );
512
+ }
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-07-03
4
+ ---
5
+
6
+ Analytics admins can now connect other agent-native app databases from Agents and inspect or repair those target databases without exposing Analytics data to all users.
@@ -234,6 +234,33 @@ export const analyticsAlertIncidents = table("analytics_alert_incidents", {
234
234
  orgId: text("org_id"),
235
235
  });
236
236
 
237
+ /**
238
+ * Admin-only registry of external agent-native app databases that Analytics can
239
+ * inspect. Secret values live in app_secrets; this table stores metadata and
240
+ * secret keys scoped to the active organization.
241
+ */
242
+ export const analyticsDbAdminConnections = table(
243
+ "analytics_db_admin_connections",
244
+ {
245
+ id: text("id").primaryKey(),
246
+ name: text("name").notNull(),
247
+ appId: text("app_id"),
248
+ appUrl: text("app_url"),
249
+ databaseUrlSecretKey: text("database_url_secret_key").notNull(),
250
+ databaseAuthTokenSecretKey: text("database_auth_token_secret_key"),
251
+ createdBy: text("created_by").notNull(),
252
+ createdAt: text("created_at").notNull().default(now()),
253
+ updatedAt: text("updated_at").notNull().default(now()),
254
+ orgId: text("org_id").notNull(),
255
+ },
256
+ (connection) => ({
257
+ orgUpdatedIdx: index("analytics_db_admin_connections_org_updated_idx").on(
258
+ connection.orgId,
259
+ connection.updatedAt,
260
+ ),
261
+ }),
262
+ );
263
+
237
264
  /**
238
265
  * Session replay summaries recorded through the first-party analytics replay
239
266
  * endpoint. Raw replay chunks live in session_replay_chunks and are only read