@agent-native/core 0.120.0 → 0.120.2
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 +16 -0
- package/corpus/core/docs/content/extensions.mdx +23 -4
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +4 -0
- package/corpus/core/src/agent/run-manager.ts +36 -10
- package/corpus/core/src/agent/types.ts +2 -0
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +45 -0
- package/corpus/core/src/client/extensions/ExtensionsListPage.tsx +16 -3
- package/corpus/core/src/client/settings/ExtensionsSettingsContent.tsx +24 -0
- package/corpus/core/src/client/settings/SettingsPanel.tsx +10 -0
- package/corpus/core/src/client/use-chat-models.ts +45 -0
- package/corpus/core/src/deploy/build.ts +3 -0
- package/corpus/core/src/server/analytics.ts +164 -20
- package/corpus/core/src/server/auth.ts +17 -13
- package/corpus/core/src/server/better-auth-instance.ts +13 -1
- package/corpus/core/src/templates/chat/app/components/layout/Sidebar.tsx +35 -6
- package/corpus/core/src/templates/chat/app/routes/extensions._index.tsx +2 -2
- package/corpus/core/src/templates/chat/netlify.toml +1 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +16 -0
- package/corpus/core/src/vite/client.ts +6 -0
- package/corpus/templates/analytics/.agents/skills/adhoc-analysis/SKILL.md +27 -43
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +19 -8
- package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +2 -2
- package/corpus/templates/analytics/AGENTS.md +20 -8
- package/corpus/templates/analytics/actions/get-explorer-dashboard.ts +3 -0
- package/corpus/templates/analytics/actions/list-dashboard-usage-stats.ts +140 -46
- package/corpus/templates/analytics/actions/migrate-analytics-artifacts.ts +48 -0
- package/corpus/templates/analytics/actions/save-analysis.ts +2 -3
- package/corpus/templates/analytics/actions/view-screen.ts +1 -1
- package/corpus/templates/analytics/app/components/dashboard/DashboardMetadata.tsx +77 -0
- package/corpus/templates/analytics/app/components/dashboard/DashboardPanelSkeleton.tsx +7 -1
- package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +1 -0
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +103 -502
- package/corpus/templates/analytics/app/global.css +30 -0
- package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +2 -2
- package/corpus/templates/analytics/app/i18n-data.ts +80 -0
- package/corpus/templates/analytics/app/pages/Agents.tsx +49 -2
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +54 -11
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +3 -53
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +23 -25
- package/corpus/templates/analytics/app/pages/analyses/AnalysisDetail.tsx +2 -2
- package/corpus/templates/analytics/app/routes/analyses._index.tsx +3 -2
- package/corpus/templates/analytics/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/analytics/changelog/2026-07-23-analytics-now-groups-saved-analyses-and-embedded-extensions-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-23-dashboard-menus-show-who-created-and-last-updated-each-dashb.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-23-dashboard-usage-now-loads-reliably-for-organizations-with-ma.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-23-long-dashboard-building-requests-now-continue-without-stoppi.md +6 -0
- package/corpus/templates/analytics/netlify.toml +4 -0
- package/corpus/templates/analytics/server/db/schema.ts +4 -0
- package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +0 -1
- package/corpus/templates/analytics/server/lib/agent-readable-resource-context.ts +1 -0
- package/corpus/templates/analytics/server/lib/migrate-analytics-artifacts.ts +1062 -0
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +6 -52
- package/corpus/templates/analytics/server/plugins/core-routes.ts +1 -1
- package/corpus/templates/analytics/server/plugins/db.ts +10 -0
- package/corpus/templates/assets/actions/generate-image-batch.ts +36 -1
- package/corpus/templates/assets/actions/generate-image.ts +28 -1
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +47 -9
- package/corpus/templates/assets/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/assets/changelog/2026-07-23-assets-image-generation-now-recovers-after-a-completed-tool-.md +6 -0
- package/corpus/templates/assets/changelog/2026-07-23-new-chat-aligns-with-sidebar-controls.md +6 -0
- package/corpus/templates/assets/netlify.toml +1 -0
- package/corpus/templates/brain/app/components/layout/Sidebar.tsx +38 -8
- package/corpus/templates/brain/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/brain/netlify.toml +1 -0
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +43 -5
- package/corpus/templates/calendar/app/routes/_app.extensions._index.tsx +2 -16
- package/corpus/templates/calendar/netlify.toml +1 -0
- package/corpus/templates/chat/app/components/layout/Sidebar.tsx +35 -6
- package/corpus/templates/chat/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/chat/netlify.toml +1 -0
- package/corpus/templates/clips/app/components/library/library-layout.tsx +52 -6
- package/corpus/templates/clips/app/routes/_app.extensions._index.tsx +2 -16
- package/corpus/templates/clips/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-23-meeting-recording-meters-and-dismissed-prompts.md +6 -0
- package/corpus/templates/clips/desktop/src/hooks/useMicMeter.ts +1 -1
- package/corpus/templates/clips/desktop/src/lib/meeting-notification-dismissal.ts +2 -0
- package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +1 -3
- package/corpus/templates/clips/desktop/src/overlays/live-audio-bars.tsx +86 -0
- package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +24 -0
- package/corpus/templates/clips/desktop/src/overlays/recording-pill.tsx +5 -69
- package/corpus/templates/clips/desktop/src/styles.css +35 -3
- package/corpus/templates/clips/desktop/src-tauri/src/adhoc_meetings_watcher.rs +13 -44
- package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +119 -17
- package/corpus/templates/clips/netlify.toml +1 -0
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +3 -7
- package/corpus/templates/content/app/routes/_app.extensions._index.tsx +2 -2
- package/corpus/templates/content/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
- package/corpus/templates/content/netlify.toml +1 -0
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +55 -17
- package/corpus/templates/design/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/design/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
- package/corpus/templates/design/netlify.toml +1 -0
- package/corpus/templates/dispatch/app/dispatch-extensions.tsx +1 -1
- package/corpus/templates/dispatch/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
- package/corpus/templates/dispatch/netlify.toml +1 -0
- package/corpus/templates/forms/.agents/skills/form-publishing/SKILL.md +9 -3
- package/corpus/templates/forms/AGENTS.md +11 -1
- package/corpus/templates/forms/README.md +24 -1
- package/corpus/templates/forms/actions/update-form.ts +12 -5
- package/corpus/templates/forms/app/components/builder/FieldPropertiesPanel.tsx +199 -1
- package/corpus/templates/forms/app/components/layout/Sidebar.tsx +41 -45
- package/corpus/templates/forms/app/i18n/en-US.ts +13 -1
- package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +35 -4
- package/corpus/templates/forms/app/pages/FormFillPage.tsx +7 -16
- package/corpus/templates/forms/app/routes/_app.extensions._index.tsx +2 -2
- package/corpus/templates/forms/changelog/2026-07-23-agent-settings-are-clearly-labeled-manage-agent-in-the-sideb.md +6 -0
- package/corpus/templates/forms/changelog/2026-07-23-forms-can-branch-into-follow-up-questions-and-route-response.md +6 -0
- package/corpus/templates/forms/netlify.toml +1 -0
- package/corpus/templates/forms/server/handlers/submissions.ts +22 -31
- package/corpus/templates/forms/server/lib/integrations.ts +11 -2
- package/corpus/templates/forms/server/lib/public-form-ssr.ts +26 -5
- package/corpus/templates/forms/server/lib/validate-fields.ts +48 -11
- package/corpus/templates/forms/server/plugins/agent-chat.ts +1 -0
- package/corpus/templates/forms/shared/conditional.ts +67 -0
- package/corpus/templates/macros/app/components/layout/AppLayout.tsx +36 -5
- package/corpus/templates/macros/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/macros/netlify.toml +1 -0
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +0 -9
- package/corpus/templates/mail/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/mail/netlify.toml +1 -0
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +38 -5
- package/corpus/templates/plan/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/plan/netlify.toml +1 -0
- package/corpus/templates/slides/app/components/layout/Sidebar.tsx +47 -4
- package/corpus/templates/slides/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/slides/netlify.toml +1 -0
- package/corpus/templates/tasks/app/components/layout/Sidebar.tsx +37 -6
- package/corpus/templates/tasks/app/routes/extensions._index.tsx +2 -2
- package/corpus/templates/tasks/app/routes/settings.tsx +31 -0
- package/corpus/templates/tasks/netlify.toml +1 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +4 -0
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts +9 -0
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +25 -10
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/types.d.ts +2 -2
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +35 -0
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/extensions/ExtensionsListPage.d.ts +5 -1
- package/dist/client/extensions/ExtensionsListPage.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionsListPage.js +5 -3
- package/dist/client/extensions/ExtensionsListPage.js.map +1 -1
- package/dist/client/settings/ExtensionsSettingsContent.d.ts +2 -0
- package/dist/client/settings/ExtensionsSettingsContent.d.ts.map +1 -0
- package/dist/client/settings/ExtensionsSettingsContent.js +10 -0
- package/dist/client/settings/ExtensionsSettingsContent.js.map +1 -0
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +10 -1
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/use-chat-models.d.ts +1 -0
- package/dist/client/use-chat-models.d.ts.map +1 -1
- package/dist/client/use-chat-models.js +33 -0
- package/dist/client/use-chat-models.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/deploy/build.js +1 -0
- package/dist/deploy/build.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/provider-api/actions/custom-provider-registration.d.ts +4 -4
- package/dist/provider-api/actions/provider-api.d.ts +2 -2
- package/dist/server/analytics.d.ts +17 -4
- package/dist/server/analytics.d.ts.map +1 -1
- package/dist/server/analytics.js +122 -17
- package/dist/server/analytics.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +5 -4
- package/dist/server/auth.js.map +1 -1
- package/dist/server/better-auth-instance.d.ts +8 -0
- package/dist/server/better-auth-instance.d.ts.map +1 -1
- package/dist/server/better-auth-instance.js +10 -1
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/templates/chat/app/components/layout/Sidebar.tsx +35 -6
- package/dist/templates/chat/app/routes/extensions._index.tsx +2 -2
- package/dist/templates/chat/netlify.toml +1 -0
- package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +16 -0
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +2 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/extensions.mdx +23 -4
- package/package.json +1 -1
- package/src/agent/production-agent.ts +4 -0
- package/src/agent/run-manager.ts +36 -10
- package/src/agent/types.ts +2 -0
- package/src/client/MultiTabAssistantChat.tsx +45 -0
- package/src/client/extensions/ExtensionsListPage.tsx +16 -3
- package/src/client/settings/ExtensionsSettingsContent.tsx +24 -0
- package/src/client/settings/SettingsPanel.tsx +10 -0
- package/src/client/use-chat-models.ts +45 -0
- package/src/deploy/build.ts +3 -0
- package/src/server/analytics.ts +164 -20
- package/src/server/auth.ts +17 -13
- package/src/server/better-auth-instance.ts +13 -1
- package/src/templates/chat/app/components/layout/Sidebar.tsx +35 -6
- package/src/templates/chat/app/routes/extensions._index.tsx +2 -2
- package/src/templates/chat/netlify.toml +1 -0
- package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +16 -0
- package/src/vite/client.ts +6 -0
- package/corpus/templates/analytics/app/components/layout/NewAnalysisDialog.tsx +0 -165
- package/corpus/templates/analytics/app/pages/analyses/AnalysesList.tsx +0 -175
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Analytics — Agent Guide
|
|
2
2
|
|
|
3
3
|
Analytics is an agent-native BI workspace. The agent manages data sources,
|
|
4
|
-
queries, dashboards, charts,
|
|
5
|
-
|
|
4
|
+
queries, dashboards, charts, and connected warehouse integrations through
|
|
5
|
+
actions and SQL-backed state. Dashboards are the canonical user-facing
|
|
6
|
+
analytics artifact; legacy analyses remain readable only for compatibility.
|
|
6
7
|
|
|
7
8
|
Keep this file essential. Querying, dashboard, warehouse, and implementation
|
|
8
9
|
details live in `.agents/skills/`.
|
|
@@ -175,13 +176,22 @@ membership id when its native update status reports `update-available`.
|
|
|
175
176
|
`/agents?view=dashboards` shows the
|
|
176
177
|
admin-only dashboard usage audit; call `list-dashboard-usage-stats` when
|
|
177
178
|
admins ask about dashboard created/modified dates, owners, last tracked
|
|
178
|
-
modifier, views, engagements, saved views, or cleanup candidates. The
|
|
179
|
+
modifier, views, edits, engagements, saved views, or cleanup candidates. The
|
|
180
|
+
dashboard overflow menu shows created/updated timestamps and their tracked
|
|
181
|
+
actors for both SQL and Explorer dashboards. The
|
|
179
182
|
Advanced menu opens `/agents?view=database`, where organization owners/admins
|
|
180
183
|
can connect other agent-native app databases and use the shared database admin
|
|
181
184
|
tool for table browsing, row editing, and SQL inspection. This database
|
|
182
185
|
surface is for connected target app databases, not broad access to all
|
|
183
186
|
Analytics data. Keep future admin additions inside this route instead of
|
|
184
187
|
adding many top-level sidebar tabs.
|
|
188
|
+
- The `migrate-analytics-artifacts` action is the organization-scoped
|
|
189
|
+
consolidation path. Run it first with `dryRun: true`, then use the exact
|
|
190
|
+
`confirm: "MIGRATE_ANALYTICS_ARTIFACTS"` token for an owner/admin-approved
|
|
191
|
+
write. It materializes legacy settings, turns saved analyses and standalone
|
|
192
|
+
extensions into dashboard blocks, archives exact duplicates, copies shares,
|
|
193
|
+
and keeps source rows recoverable. It intentionally covers organization-
|
|
194
|
+
scoped rows, not private member-only rows.
|
|
185
195
|
- For dashboard edits, default to `mutate-dashboard` with its typed
|
|
186
196
|
`dashboard.*` script API. It supports id-based panel moves, title/SQL/config
|
|
187
197
|
edits, inserts, duplication, removal, and dashboard field patches in one
|
|
@@ -212,11 +222,13 @@ membership id when its native update status reports `update-available`.
|
|
|
212
222
|
- `get-sql-dashboard` is compact by default for agents. Use its `panels`
|
|
213
223
|
summaries and `layout.panelOrder` / `layout.firstPanelIds` for orientation and
|
|
214
224
|
proof. Pass `includeConfig: true` only when full panel SQL/config is needed.
|
|
215
|
-
- Native dashboards
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
config/components
|
|
219
|
-
|
|
225
|
+
- Native dashboards are constrained artifacts. If a requested dashboard,
|
|
226
|
+
analysis/report surface, visualization, interaction model, custom layout, or
|
|
227
|
+
bespoke workflow cannot be done faithfully with the built-in dashboard JSON
|
|
228
|
+
config/components, automatically create an extension and embed it as one or
|
|
229
|
+
more `chartType: "extension"` panels using `config.extensionId`. Never leave
|
|
230
|
+
the extension standalone or direct the user to an Extensions page from
|
|
231
|
+
Analytics.
|
|
220
232
|
- For an existing extension-backed dashboard or migrated surface such as Risk
|
|
221
233
|
Meeting, separate data repair from visual redesign. Inspect the dashboard and
|
|
222
234
|
extension first, then call `update-extension` with exactly `id`,
|
|
@@ -33,6 +33,9 @@ export default defineAction({
|
|
|
33
33
|
ownerEmail: dash.ownerEmail,
|
|
34
34
|
orgId: dash.orgId,
|
|
35
35
|
visibility: dash.visibility,
|
|
36
|
+
createdAt: dash.createdAt,
|
|
37
|
+
updatedAt: dash.updatedAt,
|
|
38
|
+
updatedBy: dash.updatedBy,
|
|
36
39
|
role: dash.role,
|
|
37
40
|
canEdit: dash.canEdit,
|
|
38
41
|
canManage: dash.canManage,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core/action";
|
|
2
2
|
import { buildDeepLink } from "@agent-native/core/server";
|
|
3
|
-
import { and, eq, like, or, sql } from "drizzle-orm";
|
|
3
|
+
import { and, eq, isNull, like, or, sql } from "drizzle-orm";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
|
|
6
6
|
import { getDb, schema } from "../server/db/index.js";
|
|
@@ -21,6 +21,7 @@ type DashboardUsageStats = {
|
|
|
21
21
|
viewCount: number;
|
|
22
22
|
engagementCount: number;
|
|
23
23
|
eventEngagementCount: number;
|
|
24
|
+
editCount: number;
|
|
24
25
|
savedViewCount: number;
|
|
25
26
|
uniqueUserCount: number;
|
|
26
27
|
lastViewedAt: string | null;
|
|
@@ -96,6 +97,8 @@ export function dashboardIdFromEventLocation(
|
|
|
96
97
|
if (!raw) return null;
|
|
97
98
|
try {
|
|
98
99
|
const parsed = new URL(raw, "https://analytics.local");
|
|
100
|
+
const parsedDashboardId = dashboardIdFromPath(parsed.pathname);
|
|
101
|
+
if (parsedDashboardId) return parsedDashboardId;
|
|
99
102
|
if (
|
|
100
103
|
/(?:^|\/)(?:dashboards|adhoc)\/explorer-dashboard$/.test(parsed.pathname)
|
|
101
104
|
) {
|
|
@@ -124,7 +127,7 @@ function dashboardUrl(row: { id: string; kind: string }): string {
|
|
|
124
127
|
|
|
125
128
|
export default defineAction({
|
|
126
129
|
description:
|
|
127
|
-
"List admin-only dashboard usage and cleanup stats for every dashboard in the active organization. Includes lifecycle metadata, owner, last tracked modifier, pageviews, engagement events, unique viewers, saved view counts, archive/hidden state, and dashboard links. Requires active organization owner/admin role.",
|
|
130
|
+
"List admin-only dashboard usage and cleanup stats for every dashboard in the active organization. Includes lifecycle metadata, owner, last tracked modifier, pageviews, edit counts, engagement events, unique viewers, saved view counts, archive/hidden state, and dashboard links. Requires active organization owner/admin role.",
|
|
128
131
|
schema: z.object({}),
|
|
129
132
|
http: { method: "GET" },
|
|
130
133
|
readOnly: true,
|
|
@@ -144,24 +147,120 @@ export default defineAction({
|
|
|
144
147
|
// guard:allow-unscoped — org owner/admin audit intentionally spans all
|
|
145
148
|
// dashboard rows in the active org after requireDbAdminContextFromRequest.
|
|
146
149
|
const dashboardRows = await db
|
|
147
|
-
.select(
|
|
150
|
+
.select({
|
|
151
|
+
id: schema.dashboards.id,
|
|
152
|
+
kind: schema.dashboards.kind,
|
|
153
|
+
title: schema.dashboards.title,
|
|
154
|
+
config: schema.dashboards.config,
|
|
155
|
+
ownerEmail: schema.dashboards.ownerEmail,
|
|
156
|
+
visibility: schema.dashboards.visibility,
|
|
157
|
+
createdAt: schema.dashboards.createdAt,
|
|
158
|
+
updatedAt: schema.dashboards.updatedAt,
|
|
159
|
+
updatedBy: schema.dashboards.updatedBy,
|
|
160
|
+
archivedAt: schema.dashboards.archivedAt,
|
|
161
|
+
hiddenAt: schema.dashboards.hiddenAt,
|
|
162
|
+
hiddenBy: schema.dashboards.hiddenBy,
|
|
163
|
+
})
|
|
148
164
|
.from(schema.dashboards)
|
|
149
165
|
.where(eq(schema.dashboards.orgId, admin.orgId));
|
|
150
166
|
|
|
167
|
+
if (dashboardRows.length === 0) return [];
|
|
168
|
+
|
|
151
169
|
const dashboardIds = new Set<string>(
|
|
152
170
|
dashboardRows.map((row: { id: string }) => row.id),
|
|
153
171
|
);
|
|
154
172
|
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
173
|
+
const analyticsUserIdentity = sql<
|
|
174
|
+
string | null
|
|
175
|
+
>`coalesce(nullif(${schema.analyticsEvents.userKey}, ''), nullif(${schema.analyticsEvents.userId}, ''), nullif(${schema.analyticsEvents.anonymousId}, ''), nullif(${schema.analyticsEvents.sessionId}, ''))`;
|
|
176
|
+
const indexedDashboardEventLocation = or(
|
|
177
|
+
like(schema.analyticsEvents.path, "/dashboards/%"),
|
|
178
|
+
like(schema.analyticsEvents.path, "/adhoc/%"),
|
|
179
|
+
);
|
|
180
|
+
const legacyAbsoluteUrlDashboardEventLocation = and(
|
|
181
|
+
or(
|
|
182
|
+
isNull(schema.analyticsEvents.path),
|
|
183
|
+
eq(schema.analyticsEvents.path, ""),
|
|
184
|
+
),
|
|
185
|
+
or(
|
|
186
|
+
like(schema.analyticsEvents.url, "%/dashboards/%"),
|
|
187
|
+
like(schema.analyticsEvents.url, "%/adhoc/%"),
|
|
188
|
+
),
|
|
189
|
+
);
|
|
190
|
+
const dashboardEventLocation = or(
|
|
191
|
+
indexedDashboardEventLocation,
|
|
192
|
+
legacyAbsoluteUrlDashboardEventLocation,
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
const [savedViewRows, revisionRows, eventRows, eventUserRows] =
|
|
196
|
+
await Promise.all([
|
|
197
|
+
db
|
|
198
|
+
.select({
|
|
199
|
+
dashboardId: schema.dashboardViews.dashboardId,
|
|
200
|
+
count: sql<number>`count(*)`,
|
|
201
|
+
lastSavedViewAt: sql<
|
|
202
|
+
string | null
|
|
203
|
+
>`max(${schema.dashboardViews.createdAt})`,
|
|
204
|
+
})
|
|
205
|
+
.from(schema.dashboardViews)
|
|
206
|
+
.innerJoin(
|
|
207
|
+
schema.dashboards,
|
|
208
|
+
and(
|
|
209
|
+
eq(schema.dashboardViews.dashboardId, schema.dashboards.id),
|
|
210
|
+
eq(schema.dashboards.orgId, admin.orgId),
|
|
211
|
+
),
|
|
212
|
+
)
|
|
213
|
+
.groupBy(schema.dashboardViews.dashboardId),
|
|
214
|
+
db
|
|
215
|
+
.select({
|
|
216
|
+
dashboardId: schema.dashboardRevisions.dashboardId,
|
|
217
|
+
count: sql<number>`count(*)`,
|
|
218
|
+
})
|
|
219
|
+
.from(schema.dashboardRevisions)
|
|
220
|
+
.where(eq(schema.dashboardRevisions.orgId, admin.orgId))
|
|
221
|
+
.groupBy(schema.dashboardRevisions.dashboardId),
|
|
222
|
+
db
|
|
223
|
+
.select({
|
|
224
|
+
path: schema.analyticsEvents.path,
|
|
225
|
+
url: schema.analyticsEvents.url,
|
|
226
|
+
eventName: schema.analyticsEvents.eventName,
|
|
227
|
+
count: sql<number>`count(*)`,
|
|
228
|
+
lastSeenAt: sql<
|
|
229
|
+
string | null
|
|
230
|
+
>`max(${schema.analyticsEvents.receivedAt})`,
|
|
231
|
+
})
|
|
232
|
+
.from(schema.analyticsEvents)
|
|
233
|
+
.where(
|
|
234
|
+
and(
|
|
235
|
+
eq(schema.analyticsEvents.orgId, admin.orgId),
|
|
236
|
+
dashboardEventLocation,
|
|
237
|
+
),
|
|
238
|
+
)
|
|
239
|
+
.groupBy(
|
|
240
|
+
schema.analyticsEvents.path,
|
|
241
|
+
schema.analyticsEvents.url,
|
|
242
|
+
schema.analyticsEvents.eventName,
|
|
243
|
+
),
|
|
244
|
+
db
|
|
245
|
+
.select({
|
|
246
|
+
path: schema.analyticsEvents.path,
|
|
247
|
+
url: schema.analyticsEvents.url,
|
|
248
|
+
userIdentity: analyticsUserIdentity,
|
|
249
|
+
})
|
|
250
|
+
.from(schema.analyticsEvents)
|
|
251
|
+
.where(
|
|
252
|
+
and(
|
|
253
|
+
eq(schema.analyticsEvents.orgId, admin.orgId),
|
|
254
|
+
dashboardEventLocation,
|
|
255
|
+
sql`${analyticsUserIdentity} IS NOT NULL`,
|
|
256
|
+
),
|
|
257
|
+
)
|
|
258
|
+
.groupBy(
|
|
259
|
+
schema.analyticsEvents.path,
|
|
260
|
+
schema.analyticsEvents.url,
|
|
261
|
+
analyticsUserIdentity,
|
|
262
|
+
),
|
|
263
|
+
]);
|
|
165
264
|
|
|
166
265
|
const savedViewsByDashboard = new Map<
|
|
167
266
|
string,
|
|
@@ -179,38 +278,15 @@ export default defineAction({
|
|
|
179
278
|
});
|
|
180
279
|
}
|
|
181
280
|
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
userIdentity: analyticsUserIdentity,
|
|
192
|
-
lastSeenAt: sql<
|
|
193
|
-
string | null
|
|
194
|
-
>`max(${schema.analyticsEvents.receivedAt})`,
|
|
195
|
-
})
|
|
196
|
-
.from(schema.analyticsEvents)
|
|
197
|
-
.where(
|
|
198
|
-
and(
|
|
199
|
-
eq(schema.analyticsEvents.orgId, admin.orgId),
|
|
200
|
-
or(
|
|
201
|
-
like(schema.analyticsEvents.path, "%/dashboards/%"),
|
|
202
|
-
like(schema.analyticsEvents.path, "%/adhoc/%"),
|
|
203
|
-
like(schema.analyticsEvents.url, "%/dashboards/%"),
|
|
204
|
-
like(schema.analyticsEvents.url, "%/adhoc/%"),
|
|
205
|
-
),
|
|
206
|
-
),
|
|
207
|
-
)
|
|
208
|
-
.groupBy(
|
|
209
|
-
schema.analyticsEvents.path,
|
|
210
|
-
schema.analyticsEvents.url,
|
|
211
|
-
schema.analyticsEvents.eventName,
|
|
212
|
-
analyticsUserIdentity,
|
|
213
|
-
);
|
|
281
|
+
const editsByDashboard = new Map<string, number>();
|
|
282
|
+
for (const row of revisionRows as Array<{
|
|
283
|
+
dashboardId: string;
|
|
284
|
+
count: unknown;
|
|
285
|
+
}>) {
|
|
286
|
+
if (dashboardIds.has(row.dashboardId)) {
|
|
287
|
+
editsByDashboard.set(row.dashboardId, toNumber(row.count));
|
|
288
|
+
}
|
|
289
|
+
}
|
|
214
290
|
|
|
215
291
|
const eventsByDashboard = new Map<
|
|
216
292
|
string,
|
|
@@ -226,7 +302,6 @@ export default defineAction({
|
|
|
226
302
|
url: string | null;
|
|
227
303
|
eventName: string;
|
|
228
304
|
count: unknown;
|
|
229
|
-
userIdentity: string | null;
|
|
230
305
|
lastSeenAt: string | null;
|
|
231
306
|
}>) {
|
|
232
307
|
const dashboardId = dashboardIdFromEventLocation(row.path, row.url);
|
|
@@ -248,6 +323,22 @@ export default defineAction({
|
|
|
248
323
|
} else {
|
|
249
324
|
current.eventEngagementCount += count;
|
|
250
325
|
}
|
|
326
|
+
eventsByDashboard.set(dashboardId, current);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
for (const row of eventUserRows as Array<{
|
|
330
|
+
path: string | null;
|
|
331
|
+
url: string | null;
|
|
332
|
+
userIdentity: string | null;
|
|
333
|
+
}>) {
|
|
334
|
+
const dashboardId = dashboardIdFromEventLocation(row.path, row.url);
|
|
335
|
+
if (!dashboardId || !dashboardIds.has(dashboardId)) continue;
|
|
336
|
+
const current = eventsByDashboard.get(dashboardId) ?? {
|
|
337
|
+
viewCount: 0,
|
|
338
|
+
eventEngagementCount: 0,
|
|
339
|
+
uniqueUsers: new Set<string>(),
|
|
340
|
+
lastViewedAt: null,
|
|
341
|
+
};
|
|
251
342
|
if (row.userIdentity) current.uniqueUsers.add(row.userIdentity);
|
|
252
343
|
eventsByDashboard.set(dashboardId, current);
|
|
253
344
|
}
|
|
@@ -281,6 +372,7 @@ export default defineAction({
|
|
|
281
372
|
hiddenBy: row.hiddenBy ?? null,
|
|
282
373
|
viewCount: events.viewCount,
|
|
283
374
|
eventEngagementCount: events.eventEngagementCount,
|
|
375
|
+
editCount: editsByDashboard.get(row.id) ?? 0,
|
|
284
376
|
savedViewCount: savedViews.count,
|
|
285
377
|
engagementCount: events.eventEngagementCount + savedViews.count,
|
|
286
378
|
uniqueUserCount: events.uniqueUsers.size,
|
|
@@ -293,6 +385,8 @@ export default defineAction({
|
|
|
293
385
|
.sort((a, b) => {
|
|
294
386
|
const views = b.viewCount - a.viewCount;
|
|
295
387
|
if (views !== 0) return views;
|
|
388
|
+
const edits = b.editCount - a.editCount;
|
|
389
|
+
if (edits !== 0) return edits;
|
|
296
390
|
return b.updatedAt.localeCompare(a.updatedAt);
|
|
297
391
|
});
|
|
298
392
|
},
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core/action";
|
|
2
|
+
import {
|
|
3
|
+
getRequestOrgId,
|
|
4
|
+
getRequestUserEmail,
|
|
5
|
+
} from "@agent-native/core/server";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
|
|
8
|
+
import { requireAnalyticsAdminContext } from "../server/lib/db-admin-connections";
|
|
9
|
+
import {
|
|
10
|
+
migrateAnalyticsArtifacts,
|
|
11
|
+
type AnalyticsArtifactMigrationContext,
|
|
12
|
+
} from "../server/lib/migrate-analytics-artifacts.js";
|
|
13
|
+
|
|
14
|
+
const confirmation = "MIGRATE_ANALYTICS_ARTIFACTS" as const;
|
|
15
|
+
|
|
16
|
+
export default defineAction({
|
|
17
|
+
description:
|
|
18
|
+
"Inventory or consolidate all organization-scoped Analytics dashboards, saved analyses, and extensions into the canonical Dashboards list. The default is a read-only dry run. A write requires an organization owner/admin and confirm=MIGRATE_ANALYTICS_ARTIFACTS; exact duplicates are archived/hidden, source rows remain recoverable, shares are copied, and legacy settings keys are removed only after SQL materialization.",
|
|
19
|
+
schema: z.object({
|
|
20
|
+
dryRun: z
|
|
21
|
+
.boolean()
|
|
22
|
+
.optional()
|
|
23
|
+
.default(true)
|
|
24
|
+
.describe("Inspect the migration without writing when true (default)."),
|
|
25
|
+
confirm: z
|
|
26
|
+
.literal(confirmation)
|
|
27
|
+
.optional()
|
|
28
|
+
.describe(
|
|
29
|
+
"Required for writes: MIGRATE_ANALYTICS_ARTIFACTS. Do not send this for a dry run.",
|
|
30
|
+
),
|
|
31
|
+
}),
|
|
32
|
+
needsApproval: ({ dryRun }) => !dryRun,
|
|
33
|
+
run: async ({ dryRun, confirm }, ctx) => {
|
|
34
|
+
const userEmail = getRequestUserEmail() || ctx?.userEmail;
|
|
35
|
+
const orgId = getRequestOrgId() || ctx?.orgId || null;
|
|
36
|
+
const admin = await requireAnalyticsAdminContext({ userEmail, orgId });
|
|
37
|
+
if (!dryRun && confirm !== confirmation) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`Refusing the Analytics organization migration without confirm=${confirmation}. Run a dry run first, then repeat with the exact confirmation token.`,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
const migrationContext: AnalyticsArtifactMigrationContext = {
|
|
43
|
+
userEmail: admin.userEmail,
|
|
44
|
+
orgId: admin.orgId,
|
|
45
|
+
};
|
|
46
|
+
return migrateAnalyticsArtifacts(migrationContext, { dryRun });
|
|
47
|
+
},
|
|
48
|
+
});
|
|
@@ -113,9 +113,8 @@ function assertCompactAnalysisPayload(args: {
|
|
|
113
113
|
|
|
114
114
|
export default defineAction({
|
|
115
115
|
description:
|
|
116
|
-
"
|
|
117
|
-
"
|
|
118
|
-
"Saved analyses appear in the Analyses sidebar, so do not use this as scratch storage, as a transient summary, or as a duplicate companion artifact when creating a dashboard or extension unless the user explicitly asked for a saved analysis too. " +
|
|
116
|
+
"Legacy compatibility only: use this to re-run or update an existing saved analysis when the user explicitly names its legacy analysis id or asks to preserve that legacy record. Do not call this to create a new saved analysis, report, visualization, or bespoke workflow; new requests become dashboards, using update-dashboard and embedding any bespoke extension as one or more dashboard panels. Stores the legacy analysis question, instructions for re-running, data sources used, and compact results. " +
|
|
117
|
+
"Do not use this as scratch storage, a transient summary, or a duplicate companion artifact for a dashboard. " +
|
|
119
118
|
"Call this only after you've gathered real evidence and include non-empty, compact resultData with structured evidence from those data-source action results. For qualitative analyses, resultData may include call/message IDs, short transcript excerpts, coded themes, mention counts, and sentiment labels derived from actual source records. Never include full Gong transcripts, full tool outputs, or bulk raw provider payloads.",
|
|
120
119
|
schema: z.object({
|
|
121
120
|
id: z
|
|
@@ -438,7 +438,7 @@ export default defineAction({
|
|
|
438
438
|
includes: [
|
|
439
439
|
"dashboard created and modified dates",
|
|
440
440
|
"last tracked modifier",
|
|
441
|
-
"view and engagement counts",
|
|
441
|
+
"view, edit, and engagement counts",
|
|
442
442
|
"saved view counts",
|
|
443
443
|
"hidden and archived state",
|
|
444
444
|
],
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { useFormatters, useT } from "@agent-native/core/client/i18n";
|
|
2
|
+
import { IconCalendar, IconClock, IconUser } from "@tabler/icons-react";
|
|
3
|
+
|
|
4
|
+
interface DashboardMetadataProps {
|
|
5
|
+
createdAt: string | null;
|
|
6
|
+
createdBy: string | null;
|
|
7
|
+
updatedAt: string | null;
|
|
8
|
+
updatedBy: string | null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function MetadataRow({
|
|
12
|
+
icon: Icon,
|
|
13
|
+
label,
|
|
14
|
+
value,
|
|
15
|
+
}: {
|
|
16
|
+
icon: typeof IconCalendar;
|
|
17
|
+
label: string;
|
|
18
|
+
value: string;
|
|
19
|
+
}) {
|
|
20
|
+
return (
|
|
21
|
+
<div className="flex items-start gap-1.5">
|
|
22
|
+
<Icon className="mt-0.5 h-3 w-3 shrink-0" />
|
|
23
|
+
<span className="shrink-0">{label}</span>
|
|
24
|
+
<span className="min-w-0 truncate text-foreground/80">{value}</span>
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function DashboardMetadata({
|
|
30
|
+
createdAt,
|
|
31
|
+
createdBy,
|
|
32
|
+
updatedAt,
|
|
33
|
+
updatedBy,
|
|
34
|
+
}: DashboardMetadataProps) {
|
|
35
|
+
const t = useT();
|
|
36
|
+
const { formatDate } = useFormatters();
|
|
37
|
+
|
|
38
|
+
function formatMetadataDate(value: string | null): string {
|
|
39
|
+
if (!value) return t("agents.notTracked");
|
|
40
|
+
try {
|
|
41
|
+
return formatDate(value, {
|
|
42
|
+
year: "numeric",
|
|
43
|
+
month: "short",
|
|
44
|
+
day: "numeric",
|
|
45
|
+
hour: "numeric",
|
|
46
|
+
minute: "2-digit",
|
|
47
|
+
});
|
|
48
|
+
} catch {
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div className="flex flex-col gap-1.5 text-xs text-muted-foreground">
|
|
55
|
+
<MetadataRow
|
|
56
|
+
icon={IconCalendar}
|
|
57
|
+
label={t("agents.dashboardMetadataCreated")}
|
|
58
|
+
value={formatMetadataDate(createdAt)}
|
|
59
|
+
/>
|
|
60
|
+
<MetadataRow
|
|
61
|
+
icon={IconUser}
|
|
62
|
+
label={t("agents.dashboardMetadataCreatedBy")}
|
|
63
|
+
value={createdBy || t("agents.notTracked")}
|
|
64
|
+
/>
|
|
65
|
+
<MetadataRow
|
|
66
|
+
icon={IconClock}
|
|
67
|
+
label={t("agents.dashboardMetadataUpdated")}
|
|
68
|
+
value={formatMetadataDate(updatedAt)}
|
|
69
|
+
/>
|
|
70
|
+
<MetadataRow
|
|
71
|
+
icon={IconUser}
|
|
72
|
+
label={t("agents.dashboardMetadataUpdatedBy")}
|
|
73
|
+
value={updatedBy || t("agents.notTracked")}
|
|
74
|
+
/>
|
|
75
|
+
</div>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
@@ -8,6 +8,12 @@ export function DashboardPanelSkeleton({
|
|
|
8
8
|
...props
|
|
9
9
|
}: ComponentProps<typeof Skeleton>) {
|
|
10
10
|
return (
|
|
11
|
-
<Skeleton
|
|
11
|
+
<Skeleton
|
|
12
|
+
{...props}
|
|
13
|
+
className={cn(
|
|
14
|
+
"dashboard-panel-skeleton bg-muted-foreground/25",
|
|
15
|
+
className,
|
|
16
|
+
)}
|
|
17
|
+
/>
|
|
12
18
|
);
|
|
13
19
|
}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
|
|
13
13
|
const DASHBOARD_CONTEXT =
|
|
14
14
|
"The user wants to create a new analytics dashboard. " +
|
|
15
|
+
'DASHBOARD ARTIFACT RULE — Dashboards are the only user-facing Analytics artifact. For deep dives, saved reports, custom visualizations, or bespoke workflows, always create or update a dashboard. If bespoke UI is needed, create the extension and immediately add it as one or more `chartType: "extension"` panels in that dashboard; never leave the extension standalone or direct the user to an Extensions page. ' +
|
|
15
16
|
"TEMPLATE FIRST — If the user names an existing dashboard as a template to clone/base this on, resolve its id first (use `list-sql-dashboards` if you only have a title), then call `get-sql-dashboard` with `includeConfig: true` immediately and inspect `panels[].chartType`. " +
|
|
16
17
|
'If any panel is `chartType: "extension"`, this is an extension-backed dashboard: call `get-extension` for that panel\'s `config.extensionId`, clone/adapt it with `create-extension` (apply the requested customer/org filters), then save a new dashboard via `update-dashboard` that embeds the new extension panel (`chartType: "extension"`, `config.extensionId`). Do not rebuild an extension template as guessed SQL/BigQuery panels. ' +
|
|
17
18
|
"LARGE EXTENSION CLONE — Extension bodies can be very large (tens of thousands of characters). Call `get-extension` with `forceContent: true` exactly ONCE and reuse that body; a second same-run read intentionally omits `content` (you'll see `contentOmitted`), so don't treat that as the content being gone. Call `create-extension` / `update-extension` as NATIVE tools — they are mutating actions and cannot be invoked from `run-code`/`appAction`. For customer-specific clones, change only the small static config block (e.g. `ACCOUNT_USAGE_STATIC`) and prefer a focused `update-extension` edit over regenerating the whole HTML. Never shovel the full body through `run-code` or chat; if you stage it in a workspace scratch file, read it back with `workspaceRead` (which returns the whole file). " +
|