@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
|
@@ -2,16 +2,21 @@
|
|
|
2
2
|
* Opt-in analytics injection for SSR streams.
|
|
3
3
|
* Supported environment variables:
|
|
4
4
|
* - `GA_MEASUREMENT_ID` — Google Analytics 4 measurement ID
|
|
5
|
+
* - `GTM_CONTAINER_ID` — Google Tag Manager web container ID
|
|
5
6
|
*
|
|
6
7
|
* Netlify configuration-file env vars are build-time only for serverless
|
|
7
8
|
* functions, so the Vite/Nitro build paths also bake this public value into
|
|
8
9
|
* SSR bundles.
|
|
9
10
|
*
|
|
10
11
|
* Amplitude and Sentry are initialized client-side via their npm packages
|
|
11
|
-
* (see `packages/core/src/client/analytics.ts`).
|
|
12
|
-
* tag injection because
|
|
12
|
+
* (see `packages/core/src/client/analytics.ts`). GTM and GA require script
|
|
13
|
+
* tag injection because their loaders must be `<script>` elements.
|
|
13
14
|
*
|
|
14
|
-
* When set,
|
|
15
|
+
* When GTM is set, its head bootstrap is injected before `</head>` and its
|
|
16
|
+
* noscript fallback immediately after `<body>`. GTM takes precedence over GA
|
|
17
|
+
* so pageviews are not double-counted; configure the GA tag inside GTM.
|
|
18
|
+
* When only GA is set, the corresponding script tags are injected before
|
|
19
|
+
* `</head>`.
|
|
15
20
|
* When not set, the stream passes through untouched (zero overhead).
|
|
16
21
|
*
|
|
17
22
|
* Usage in entry.server.tsx:
|
|
@@ -22,6 +27,7 @@
|
|
|
22
27
|
*/
|
|
23
28
|
|
|
24
29
|
declare const __AGENT_NATIVE_BUILD_GA_MEASUREMENT_ID__: string | undefined;
|
|
30
|
+
declare const __AGENT_NATIVE_BUILD_GTM_CONTAINER_ID__: string | undefined;
|
|
25
31
|
|
|
26
32
|
function normalizeMeasurementId(value: string | undefined): string | null {
|
|
27
33
|
const trimmed = value?.trim();
|
|
@@ -34,6 +40,25 @@ function getViteBakedGaMeasurementId(): string | undefined {
|
|
|
34
40
|
: undefined;
|
|
35
41
|
}
|
|
36
42
|
|
|
43
|
+
function normalizeContainerId(value: string | undefined): string | null {
|
|
44
|
+
const trimmed = value?.trim().toUpperCase();
|
|
45
|
+
return trimmed && /^GTM-[A-Z0-9]+$/.test(trimmed) ? trimmed : null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function getViteBakedGtmContainerId(): string | undefined {
|
|
49
|
+
return typeof __AGENT_NATIVE_BUILD_GTM_CONTAINER_ID__ === "string"
|
|
50
|
+
? __AGENT_NATIVE_BUILD_GTM_CONTAINER_ID__
|
|
51
|
+
: undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getGtmContainerId(): string | null {
|
|
55
|
+
return (
|
|
56
|
+
normalizeContainerId(process.env.GTM_CONTAINER_ID) ||
|
|
57
|
+
normalizeContainerId(process.env.AGENT_NATIVE_BUILD_GTM_CONTAINER_ID) ||
|
|
58
|
+
normalizeContainerId(getViteBakedGtmContainerId())
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
37
62
|
function getGaMeasurementId(): string | null {
|
|
38
63
|
return (
|
|
39
64
|
normalizeMeasurementId(process.env.GA_MEASUREMENT_ID) ||
|
|
@@ -74,32 +99,151 @@ function getGaScript(): string | null {
|
|
|
74
99
|
);
|
|
75
100
|
}
|
|
76
101
|
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
102
|
+
function getGtmHeadScript(containerId: string): string {
|
|
103
|
+
const jsId = JSON.stringify(containerId);
|
|
104
|
+
return `<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer',${jsId});</script>`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function getGtmBodyFallback(containerId: string): string {
|
|
108
|
+
const src = encodeURIComponent(containerId);
|
|
109
|
+
return `<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=${src}" height="0" width="0" style="display:none;visibility:hidden" title="Google Tag Manager"></iframe></noscript>`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
type AnalyticsInjection = {
|
|
113
|
+
head: string;
|
|
114
|
+
body: string;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
function getAnalyticsInjection(): AnalyticsInjection | null {
|
|
118
|
+
const containerId = getGtmContainerId();
|
|
119
|
+
if (containerId) {
|
|
120
|
+
return {
|
|
121
|
+
head: getGtmHeadScript(containerId),
|
|
122
|
+
body: getGtmBodyFallback(containerId),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const gaScript = getGaScript();
|
|
127
|
+
return gaScript ? { head: gaScript, body: "" } : null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const HEAD_CLOSE_PATTERN = /<\/head>/i;
|
|
131
|
+
const BODY_OPEN_PATTERN = /<body\b[^>]*>/i;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Add the configured analytics scripts to a complete HTML document.
|
|
135
|
+
*
|
|
136
|
+
* The normal app document is streamed through `wrapWithAnalytics`, but
|
|
137
|
+
* framework-owned documents such as `/signup` are returned as strings by the
|
|
138
|
+
* auth guard and need the same injection path.
|
|
139
|
+
*/
|
|
140
|
+
export function injectAnalyticsIntoHtml(html: string): string {
|
|
141
|
+
const injection = getAnalyticsInjection();
|
|
142
|
+
if (!injection) return html;
|
|
143
|
+
|
|
144
|
+
const headCloseMatch = HEAD_CLOSE_PATTERN.exec(html);
|
|
145
|
+
if (!headCloseMatch || headCloseMatch.index === undefined) return html;
|
|
146
|
+
|
|
147
|
+
let output =
|
|
148
|
+
html.slice(0, headCloseMatch.index) +
|
|
149
|
+
injection.head +
|
|
150
|
+
html.slice(headCloseMatch.index);
|
|
151
|
+
|
|
152
|
+
if (injection.body) {
|
|
153
|
+
const bodyOpenMatch = BODY_OPEN_PATTERN.exec(output);
|
|
154
|
+
if (bodyOpenMatch && bodyOpenMatch.index !== undefined) {
|
|
155
|
+
const bodyEnd = bodyOpenMatch.index + bodyOpenMatch[0].length;
|
|
156
|
+
output =
|
|
157
|
+
output.slice(0, bodyEnd) + injection.body + output.slice(bodyEnd);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return output;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function wrapWithAnalytics(
|
|
165
|
+
body: ReadableStream<Uint8Array>,
|
|
166
|
+
): ReadableStream<Uint8Array> {
|
|
167
|
+
const injection = getAnalyticsInjection();
|
|
168
|
+
if (!injection) return body;
|
|
80
169
|
|
|
81
170
|
const decoder = new TextDecoder();
|
|
82
171
|
const encoder = new TextEncoder();
|
|
83
|
-
let
|
|
172
|
+
let pending = "";
|
|
173
|
+
let headInjected = false;
|
|
174
|
+
let bodyInjected = !injection.body;
|
|
84
175
|
|
|
85
176
|
return body.pipeThrough(
|
|
86
|
-
new TransformStream({
|
|
177
|
+
new TransformStream<Uint8Array, Uint8Array>({
|
|
87
178
|
transform(chunk, controller) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
179
|
+
pending += decoder.decode(chunk, { stream: true });
|
|
180
|
+
|
|
181
|
+
if (!headInjected) {
|
|
182
|
+
const headCloseMatch = HEAD_CLOSE_PATTERN.exec(pending);
|
|
183
|
+
if (!headCloseMatch || headCloseMatch.index === undefined) return;
|
|
184
|
+
|
|
185
|
+
const headEnd = headCloseMatch.index + headCloseMatch[0].length;
|
|
186
|
+
controller.enqueue(
|
|
187
|
+
encoder.encode(
|
|
188
|
+
pending.slice(0, headCloseMatch.index) +
|
|
189
|
+
injection.head +
|
|
190
|
+
pending.slice(headCloseMatch.index, headEnd),
|
|
191
|
+
),
|
|
192
|
+
);
|
|
193
|
+
pending = pending.slice(headEnd);
|
|
194
|
+
headInjected = true;
|
|
91
195
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
196
|
+
|
|
197
|
+
if (!bodyInjected) {
|
|
198
|
+
const bodyOpenMatch = BODY_OPEN_PATTERN.exec(pending);
|
|
199
|
+
if (!bodyOpenMatch || bodyOpenMatch.index === undefined) return;
|
|
200
|
+
|
|
201
|
+
const bodyEnd = bodyOpenMatch.index + bodyOpenMatch[0].length;
|
|
202
|
+
controller.enqueue(
|
|
203
|
+
encoder.encode(pending.slice(0, bodyEnd) + injection.body),
|
|
204
|
+
);
|
|
205
|
+
pending = pending.slice(bodyEnd);
|
|
206
|
+
bodyInjected = true;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (pending) {
|
|
210
|
+
controller.enqueue(encoder.encode(pending));
|
|
211
|
+
pending = "";
|
|
101
212
|
}
|
|
102
213
|
},
|
|
214
|
+
flush(controller) {
|
|
215
|
+
pending += decoder.decode();
|
|
216
|
+
|
|
217
|
+
if (!headInjected) {
|
|
218
|
+
const headCloseMatch = HEAD_CLOSE_PATTERN.exec(pending);
|
|
219
|
+
if (headCloseMatch && headCloseMatch.index !== undefined) {
|
|
220
|
+
const headEnd = headCloseMatch.index + headCloseMatch[0].length;
|
|
221
|
+
controller.enqueue(
|
|
222
|
+
encoder.encode(
|
|
223
|
+
pending.slice(0, headCloseMatch.index) +
|
|
224
|
+
injection.head +
|
|
225
|
+
pending.slice(headCloseMatch.index, headEnd),
|
|
226
|
+
),
|
|
227
|
+
);
|
|
228
|
+
pending = pending.slice(headEnd);
|
|
229
|
+
headInjected = true;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (headInjected && !bodyInjected) {
|
|
234
|
+
const bodyOpenMatch = BODY_OPEN_PATTERN.exec(pending);
|
|
235
|
+
if (bodyOpenMatch && bodyOpenMatch.index !== undefined) {
|
|
236
|
+
const bodyEnd = bodyOpenMatch.index + bodyOpenMatch[0].length;
|
|
237
|
+
controller.enqueue(
|
|
238
|
+
encoder.encode(pending.slice(0, bodyEnd) + injection.body),
|
|
239
|
+
);
|
|
240
|
+
pending = pending.slice(bodyEnd);
|
|
241
|
+
bodyInjected = true;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (pending) controller.enqueue(encoder.encode(pending));
|
|
246
|
+
},
|
|
103
247
|
}),
|
|
104
248
|
);
|
|
105
249
|
}
|
|
@@ -98,6 +98,7 @@ import {
|
|
|
98
98
|
type WorkspaceAppAudience,
|
|
99
99
|
} from "../shared/workspace-app-audience.js";
|
|
100
100
|
import { isValidWorkspaceAppIdFormat } from "../shared/workspace-app-id.js";
|
|
101
|
+
import { injectAnalyticsIntoHtml } from "./analytics.js";
|
|
101
102
|
import { signupAttributionFromCookieHeader } from "./attribution.js";
|
|
102
103
|
import { getBetterAuth, getBetterAuthSync } from "./better-auth-instance.js";
|
|
103
104
|
import type { BetterAuthConfig } from "./better-auth-instance.js";
|
|
@@ -1646,20 +1647,23 @@ function injectLoginSocialImageMeta(loginHtml: string, event: H3Event): string {
|
|
|
1646
1647
|
}
|
|
1647
1648
|
|
|
1648
1649
|
function loginHtmlResponse(loginHtml: string, event: H3Event): Response {
|
|
1649
|
-
return new Response(
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1650
|
+
return new Response(
|
|
1651
|
+
injectAnalyticsIntoHtml(injectLoginSocialImageMeta(loginHtml, event)),
|
|
1652
|
+
{
|
|
1653
|
+
status: 200,
|
|
1654
|
+
headers: {
|
|
1655
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
1656
|
+
// The sign-in document is part of the public server shell. Keep it on the
|
|
1657
|
+
// same long-fresh/long-SWR CDN policy as React Router SSR so hosted
|
|
1658
|
+
// template roots do not invoke origin just to render anonymous login UI.
|
|
1659
|
+
// The login markup is env-INDEPENDENT (a Google-only app always renders
|
|
1660
|
+
// a working button); the analytics script is public build configuration,
|
|
1661
|
+
// not user/session state. Never downgrade this to private/no-store.
|
|
1662
|
+
...DEFAULT_SSR_CACHE_HEADERS,
|
|
1663
|
+
"X-Robots-Tag": "noindex, nofollow",
|
|
1664
|
+
},
|
|
1661
1665
|
},
|
|
1662
|
-
|
|
1666
|
+
);
|
|
1663
1667
|
}
|
|
1664
1668
|
|
|
1665
1669
|
function isHtmlDocumentRequest(event: H3Event, pathname: string): boolean {
|
|
@@ -1109,6 +1109,18 @@ async function createBetterAuthInstance(
|
|
|
1109
1109
|
return auth as unknown as BetterAuthInstance;
|
|
1110
1110
|
}
|
|
1111
1111
|
|
|
1112
|
+
/**
|
|
1113
|
+
* Configure the local auth connection with the same write contention settings
|
|
1114
|
+
* as the shared app connection. Better Auth uses its own SQLite handle, so the
|
|
1115
|
+
* app connection's busy timeout does not protect first-run account creation.
|
|
1116
|
+
*/
|
|
1117
|
+
export function configureLocalSqlite(sqlite: {
|
|
1118
|
+
pragma(statement: string): unknown;
|
|
1119
|
+
}): void {
|
|
1120
|
+
sqlite.pragma("busy_timeout = 10000");
|
|
1121
|
+
sqlite.pragma("journal_mode = WAL");
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1112
1124
|
async function buildDatabaseConfig(
|
|
1113
1125
|
dialect: string,
|
|
1114
1126
|
): Promise<BetterAuthOptions["database"]> {
|
|
@@ -1182,7 +1194,7 @@ async function buildDatabaseConfig(
|
|
|
1182
1194
|
const { default: Database } = await import("better-sqlite3");
|
|
1183
1195
|
const filePath = url.replace(/^file:/, "");
|
|
1184
1196
|
const sqlite = new Database(filePath);
|
|
1185
|
-
sqlite
|
|
1197
|
+
configureLocalSqlite(sqlite);
|
|
1186
1198
|
const { drizzle } = await import("drizzle-orm/better-sqlite3");
|
|
1187
1199
|
const db = drizzle(sqlite, { schema: sqliteAuthSchema });
|
|
1188
1200
|
const { drizzleAdapter } = await import("better-auth/adapters/drizzle");
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
type ChatThreadSummary,
|
|
5
5
|
} from "@agent-native/core/client/agent-chat";
|
|
6
6
|
import { appPath } from "@agent-native/core/client/api-path";
|
|
7
|
-
import { ExtensionsSidebarSection } from "@agent-native/core/client/extensions";
|
|
8
7
|
import { useT } from "@agent-native/core/client/i18n";
|
|
9
8
|
import { OrgSwitcher } from "@agent-native/core/client/org";
|
|
10
9
|
import { FeedbackButton } from "@agent-native/core/client/ui";
|
|
@@ -38,6 +37,9 @@ const navItems = [
|
|
|
38
37
|
href: "/",
|
|
39
38
|
view: "chat",
|
|
40
39
|
},
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const bottomNavItems = [
|
|
41
43
|
{
|
|
42
44
|
icon: IconHierarchy2,
|
|
43
45
|
labelKey: "settings.agentTitle",
|
|
@@ -419,11 +421,38 @@ export function Sidebar({
|
|
|
419
421
|
</nav>
|
|
420
422
|
|
|
421
423
|
<div className={cn("mt-auto shrink-0", collapsed && "py-2")}>
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
424
|
+
<nav
|
|
425
|
+
className={cn(
|
|
426
|
+
"grid",
|
|
427
|
+
collapsed ? "gap-0 px-1 py-1" : "gap-1 px-2 py-1",
|
|
428
|
+
)}
|
|
429
|
+
>
|
|
430
|
+
{bottomNavItems.map((item) => {
|
|
431
|
+
const Icon = item.icon;
|
|
432
|
+
const isActive = location.pathname.startsWith(item.href);
|
|
433
|
+
const link = (
|
|
434
|
+
<Link
|
|
435
|
+
to={item.href}
|
|
436
|
+
className={navClass({ isActive })}
|
|
437
|
+
aria-current={isActive ? "page" : undefined}
|
|
438
|
+
aria-label={collapsed ? t(item.labelKey) : undefined}
|
|
439
|
+
>
|
|
440
|
+
<Icon className="size-4 shrink-0" />
|
|
441
|
+
<span className={collapsed ? "sr-only" : "truncate"}>
|
|
442
|
+
{t(item.labelKey)}
|
|
443
|
+
</span>
|
|
444
|
+
</Link>
|
|
445
|
+
);
|
|
446
|
+
return collapsed ? (
|
|
447
|
+
<Tooltip key={item.href}>
|
|
448
|
+
<TooltipTrigger asChild>{link}</TooltipTrigger>
|
|
449
|
+
<TooltipContent side="right">{t(item.labelKey)}</TooltipContent>
|
|
450
|
+
</Tooltip>
|
|
451
|
+
) : (
|
|
452
|
+
<div key={item.href}>{link}</div>
|
|
453
|
+
);
|
|
454
|
+
})}
|
|
455
|
+
</nav>
|
|
427
456
|
|
|
428
457
|
<div className={cn(collapsed ? "px-1 py-1" : "px-3 py-2")}>
|
|
429
458
|
<OrgSwitcher
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Navigate } from "react-router";
|
|
2
2
|
|
|
3
3
|
import { APP_TITLE } from "@/lib/app-config";
|
|
4
4
|
|
|
@@ -7,5 +7,5 @@ export function meta() {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export default function ExtensionsRoute() {
|
|
10
|
-
return <
|
|
10
|
+
return <Navigate to="/settings#extensions" replace />;
|
|
11
11
|
}
|
|
@@ -76,6 +76,22 @@ Full source-code customization is a core Agent Native capability. Extensions
|
|
|
76
76
|
are the fast, sandboxed, no-deploy layer—not the limit of what the app can
|
|
77
77
|
become.
|
|
78
78
|
|
|
79
|
+
Use this decision rule when either path could solve the request:
|
|
80
|
+
|
|
81
|
+
- Start with an extension for a self-contained, relatively one-off widget,
|
|
82
|
+
custom visualization or interaction, dashboard tile, standalone utility, or
|
|
83
|
+
add-on in an existing slot.
|
|
84
|
+
- Use app code for a core template feature, native UI/layout/routes/business
|
|
85
|
+
logic, a new slot, or behavior that should be reused broadly across dashboards
|
|
86
|
+
or users.
|
|
87
|
+
- If the same extension keeps getting rebuilt, promote it into app code as one
|
|
88
|
+
reusable native feature instead of maintaining copies.
|
|
89
|
+
|
|
90
|
+
As a rule of thumb, if an extension can solve the problem, generally start
|
|
91
|
+
there. An isolated custom dashboard visualization belongs in an extension; a
|
|
92
|
+
new chart type that should be available across dashboards belongs in the
|
|
93
|
+
template code.
|
|
94
|
+
|
|
79
95
|
**When a user asks to "make an extension", "create an extension", or "build
|
|
80
96
|
a ... extension" (or the older phrasings "make a tool" / "create a tool"):**
|
|
81
97
|
|
|
@@ -3066,6 +3066,12 @@ function createAgentNativeConfig(
|
|
|
3066
3066
|
"process.env.AGENT_NATIVE_BUILD_GA_MEASUREMENT_ID": JSON.stringify(
|
|
3067
3067
|
process.env.GA_MEASUREMENT_ID?.trim() || "",
|
|
3068
3068
|
),
|
|
3069
|
+
__AGENT_NATIVE_BUILD_GTM_CONTAINER_ID__: JSON.stringify(
|
|
3070
|
+
process.env.GTM_CONTAINER_ID?.trim() || "",
|
|
3071
|
+
),
|
|
3072
|
+
"process.env.AGENT_NATIVE_BUILD_GTM_CONTAINER_ID": JSON.stringify(
|
|
3073
|
+
process.env.GTM_CONTAINER_ID?.trim() || "",
|
|
3074
|
+
),
|
|
3069
3075
|
// Framework route warmup controls how SSR `.data` routes are fetched:
|
|
3070
3076
|
// ordinary fetches keep them CDN-cacheable, while native prefetch headers
|
|
3071
3077
|
// can be refused before the CDN/origin sees the request. Keep this value
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
name: adhoc-analysis
|
|
3
3
|
description: >-
|
|
4
4
|
How to conduct ad-hoc analyses: gather data from multiple sources, synthesize
|
|
5
|
-
findings, save
|
|
5
|
+
findings, and save durable results as dashboard artifacts that anyone can re-run.
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Ad-Hoc Analysis
|
|
9
9
|
|
|
10
|
-
Ad-hoc analyses are deep-dive investigations that cross-reference multiple data sources and produce a written report with findings.
|
|
10
|
+
Ad-hoc analyses are deep-dive investigations that cross-reference multiple data sources and produce a written report with findings. The durable user-facing result is a dashboard. Answer in chat first unless the user explicitly asks to save the result, create a reusable artifact, or re-run/update an existing saved analysis.
|
|
11
11
|
|
|
12
12
|
## When to Use
|
|
13
13
|
|
|
@@ -17,21 +17,20 @@ Use the ad-hoc analysis workflow when:
|
|
|
17
17
|
- The investigation involves cross-referencing (e.g., CRM deals matched against call recordings)
|
|
18
18
|
- The user explicitly asks for an "analysis" or "deep dive"
|
|
19
19
|
|
|
20
|
-
Save a reusable
|
|
20
|
+
Save a reusable dashboard artifact when:
|
|
21
21
|
|
|
22
22
|
- The user explicitly asks to save, create, publish, or re-run a saved analysis
|
|
23
23
|
- The user is already viewing or re-running an existing saved analysis
|
|
24
24
|
- The output needs a durable artifact because it includes generated chart images or a reusable refresh workflow
|
|
25
25
|
|
|
26
|
-
For one-off questions and exploratory deep dives, query the data and answer in chat. Do not call `save-analysis` just because the user said "analysis" or "deep dive".
|
|
26
|
+
For one-off questions and exploratory deep dives, query the data and answer in chat. Do not create a dashboard or call the legacy `save-analysis` action just because the user said "analysis" or "deep dive".
|
|
27
27
|
|
|
28
28
|
If the user asks for an analysis output that needs a bespoke interactive
|
|
29
29
|
surface, custom visualization, multi-step workflow, or UI that cannot be
|
|
30
|
-
faithfully represented
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
you built it as an extension.
|
|
30
|
+
faithfully represented by native dashboard panels, create an extension and
|
|
31
|
+
immediately embed it in the dashboard as one or more `chartType: "extension"`
|
|
32
|
+
panels with `config.extensionId`. Never leave the extension as a standalone
|
|
33
|
+
Analytics result or direct the user to an Extensions page.
|
|
35
34
|
|
|
36
35
|
## Workflow
|
|
37
36
|
|
|
@@ -159,46 +158,33 @@ Time range: Jan 1 – Mar 31, 2026
|
|
|
159
158
|
Filters: S1+ pipeline, closed-lost only
|
|
160
159
|
```
|
|
161
160
|
|
|
162
|
-
### Step 6: Save the
|
|
161
|
+
### Step 6: Save the Dashboard Artifact (only when requested)
|
|
163
162
|
|
|
164
|
-
Call `
|
|
163
|
+
Call `update-dashboard` with a complete dashboard config when the user asks for a saved/re-runnable analysis or this turn is creating a durable report. If the requested report needs bespoke UI, create the extension first and include its id in an extension panel.
|
|
165
164
|
|
|
166
165
|
```
|
|
167
|
-
|
|
168
|
-
--
|
|
169
|
-
--
|
|
170
|
-
--description "Deep dive into 54 Fusion deals closed-lost in Q1, cross-referenced with Gong calls and Slack activity"
|
|
171
|
-
--question "Why are we losing deals in Q1? Cross-reference with Gong calls and Slack."
|
|
172
|
-
--dataSources '["hubspot", "gong", "slack"]'
|
|
173
|
-
--instructions "1. Fetch closed-lost S1+ deals from HubSpot (pipeline: Fusion, close date: Q1 2026)
|
|
174
|
-
2. For each deal, fetch associated contacts and their emails
|
|
175
|
-
3. Search Gong calls matching those contact emails (lookback: 6 months before close date)
|
|
176
|
-
4. For top 15 deals by amount, search Slack for '{company name} fusion'
|
|
177
|
-
5. Calculate: total deals, avg deal size, win rate, Gong coverage rate
|
|
178
|
-
6. Break down by: lost reason, stage reached, deal size tier
|
|
179
|
-
7. Highlight deals with no Gong coverage (blind spots)
|
|
180
|
-
8. Save results with save-analysis using id='closed-lost-q1-2026'"
|
|
181
|
-
--resultMarkdown "[the full markdown report]"
|
|
182
|
-
--resultData '{"deals": [...], "metrics": {...}}'
|
|
166
|
+
update-dashboard
|
|
167
|
+
--dashboardId "closed-lost-q1-2026"
|
|
168
|
+
--config '{"name":"Q1 2026 Closed-Lost Analysis","panels":[{"id":"findings","title":"Findings","source":"first-party","chartType":"table","width":2,"sql":"...","config":{"description":"Evidence-backed report for the requested cohort."}}]}'
|
|
183
169
|
```
|
|
184
170
|
|
|
185
|
-
|
|
171
|
+
The dashboard config must preserve compact evidence from the real data-source action results you used: row samples, aggregate metrics, match decisions, call/message IDs, short transcript/message excerpts, coded themes, sentiment labels, and explicit provider errors for any gaps. Do not include full Gong transcripts, full tool outputs, or raw provider payload dumps. If you cannot query a source, do not save guessed dashboard content; report the unavailable/error result instead.
|
|
186
172
|
|
|
187
|
-
**Critical: Write good
|
|
173
|
+
**Critical: Write good dashboard definitions.** The dashboard description/config is what the agent uses on re-run. Be specific:
|
|
188
174
|
|
|
189
175
|
- Which actions to call with which parameters
|
|
190
176
|
- What filters to apply
|
|
191
177
|
- How to match records across sources
|
|
192
178
|
- What metrics to calculate
|
|
193
179
|
- What structure the output should have
|
|
194
|
-
- End with "
|
|
180
|
+
- End with "Update dashboard with id='...'"
|
|
195
181
|
|
|
196
182
|
### Step 7: Navigate to the Result
|
|
197
183
|
|
|
198
|
-
After saving, navigate the user to see the saved
|
|
184
|
+
After saving, navigate the user to see the saved dashboard:
|
|
199
185
|
|
|
200
186
|
```
|
|
201
|
-
navigate --view=
|
|
187
|
+
navigate --view=adhoc --dashboardId=closed-lost-q1-2026
|
|
202
188
|
```
|
|
203
189
|
|
|
204
190
|
## Re-Running an Analysis
|
|
@@ -209,7 +195,7 @@ When a user clicks "Re-run" on a saved analysis, the agent receives:
|
|
|
209
195
|
- The saved instructions (step-by-step)
|
|
210
196
|
- The analysis ID to update
|
|
211
197
|
|
|
212
|
-
Follow the instructions to gather fresh data, then call `
|
|
198
|
+
Follow the instructions to gather fresh data, then call `update-dashboard` with the same `dashboardId` to update the panels/report. Existing legacy analysis deep links remain readable, but new durable results must be written to dashboards.
|
|
213
199
|
|
|
214
200
|
If the user challenges the coverage of a chat answer or saved analysis ("why
|
|
215
201
|
aren't you pulling more deals?", "where is the updated response?"), rerun the
|
|
@@ -221,21 +207,19 @@ showing it or saving it.
|
|
|
221
207
|
|
|
222
208
|
| Action | Purpose |
|
|
223
209
|
| ----------------- | ---------------------------------------------------------------- |
|
|
224
|
-
| `
|
|
225
|
-
| `get-
|
|
226
|
-
| `list-
|
|
227
|
-
| `
|
|
228
|
-
| `navigate` | Navigate to analyses view: `--view=analyses [--analysisId=<id>]` |
|
|
210
|
+
| `update-dashboard` | Save or update the dashboard artifact and its panels |
|
|
211
|
+
| `get-sql-dashboard` | Retrieve a dashboard by ID |
|
|
212
|
+
| `list-sql-dashboards` | List dashboard artifacts |
|
|
213
|
+
| `navigate` | Navigate to a dashboard: `--view=adhoc --dashboardId=<id>` |
|
|
229
214
|
|
|
230
215
|
## Storage
|
|
231
216
|
|
|
232
|
-
|
|
217
|
+
Dashboard artifacts are stored in SQL and respect the normal dashboard access model. Legacy analyses remain in their existing SQL tables for compatibility and should not be used for new artifacts.
|
|
233
218
|
|
|
234
219
|
API endpoints (for UI consumption):
|
|
235
220
|
|
|
236
|
-
- `GET /api/
|
|
237
|
-
- `GET /api/
|
|
238
|
-
- `DELETE /api/analyses/{id}` — delete one
|
|
221
|
+
- `GET /api/sql-dashboards` — list dashboard artifacts
|
|
222
|
+
- `GET /api/sql-dashboards/{id}` — get one
|
|
239
223
|
|
|
240
224
|
## Best Practices
|
|
241
225
|
|
|
@@ -257,7 +241,7 @@ hold everything in one pass:
|
|
|
257
241
|
from the primary source (HubSpot, BigQuery, etc.).
|
|
258
242
|
2. **Chunk and persist** — process 5-10 items per iteration. For each chunk,
|
|
259
243
|
write a short per-item findings note (key signals, gaps, theme tags) as an
|
|
260
|
-
intermediate result to
|
|
244
|
+
intermediate result to the dashboard definition or agent scratch.
|
|
261
245
|
3. **Synthesize** — after all chunks are complete, read the intermediate notes
|
|
262
246
|
back in and produce the final cross-item synthesis (patterns, rankings,
|
|
263
247
|
themes, recommendations).
|
|
@@ -7,7 +7,7 @@ description: >-
|
|
|
7
7
|
|
|
8
8
|
# Dashboard Management
|
|
9
9
|
|
|
10
|
-
Dashboards are SQL-backed resources. New dashboards
|
|
10
|
+
Dashboards are the canonical SQL-backed Analytics resources. New dashboards, saved analyses, reports, and bespoke extensions are represented as dashboard artifacts—not as separate user-facing Analytics objects. Legacy analysis tables and actions remain available only for compatibility.
|
|
11
11
|
|
|
12
12
|
## Storage
|
|
13
13
|
|
|
@@ -25,6 +25,15 @@ Current storage:
|
|
|
25
25
|
|
|
26
26
|
Legacy settings keys such as `u:<email>:dashboard-*`, `u:<email>:sql-dashboard-*`, `o:<orgId>:sql-dashboard-*`, and `adhoc-analysis-*` are still read as a fallback and copied into SQL on access. Do not create new dashboard settings rows.
|
|
27
27
|
|
|
28
|
+
For organization-wide consolidation, use `migrate-analytics-artifacts` first
|
|
29
|
+
with `dryRun: true`. The write requires an organization owner/admin and the
|
|
30
|
+
exact confirmation token `MIGRATE_ANALYTICS_ARTIFACTS`. It materializes
|
|
31
|
+
organization-scoped legacy settings, creates dashboard blocks for saved
|
|
32
|
+
analyses and standalone extensions, archives exact duplicates, copies shares,
|
|
33
|
+
and removes legacy settings keys only after SQL rows are materialized. Source
|
|
34
|
+
rows remain recoverable. Private member-only rows are intentionally outside
|
|
35
|
+
this organization-scoped operation.
|
|
36
|
+
|
|
28
37
|
Use `mutate-dashboard` for existing dashboard edits. It resolves the current
|
|
29
38
|
user/org context, validates the resulting config, writes the SQL-backed record,
|
|
30
39
|
syncs collab, and returns compact proof. Use `update-dashboard` for new
|
|
@@ -104,13 +113,15 @@ standard panels, supported chart types, filters, variables, sections, and grid
|
|
|
104
113
|
layout.
|
|
105
114
|
|
|
106
115
|
If the user asks for a dashboard or analytical surface that needs bespoke UI or
|
|
107
|
-
code beyond the dashboard JSON/component model, create an extension
|
|
108
|
-
Examples include custom interaction flows, non-standard
|
|
109
|
-
multi-step workflows, highly custom layouts, custom
|
|
110
|
-
dashboard-like app that needs behavior the built-in
|
|
111
|
-
production mode, call `create-extension`
|
|
112
|
-
|
|
113
|
-
|
|
116
|
+
code beyond the dashboard JSON/component model, create an extension and embed it
|
|
117
|
+
in the dashboard. Examples include custom interaction flows, non-standard
|
|
118
|
+
visualizations, complex multi-step workflows, highly custom layouts, custom
|
|
119
|
+
client-side state, or a dashboard-like app that needs behavior the built-in
|
|
120
|
+
renderer cannot express. In production mode, call `create-extension`
|
|
121
|
+
automatically, then call `update-dashboard` with one or more
|
|
122
|
+
`chartType: "extension"` panels using `config.extensionId`. Never leave the
|
|
123
|
+
extension as a standalone Analytics result or direct the user to the Extensions
|
|
124
|
+
page.
|
|
114
125
|
|
|
115
126
|
## Embedding An Extension As A Panel
|
|
116
127
|
|
|
@@ -36,7 +36,7 @@ through `2026-05-02T04:00:00Z`.
|
|
|
36
36
|
|
|
37
37
|
## Showing Charts In Chat
|
|
38
38
|
|
|
39
|
-
For an in-chat answer, **emit a live `/chart` embed** — never `generate-chart`. The embed mounts a live `SqlChart` that re-queries when its source changes, and it doesn't choke on rigid JSON params the way the PNG action does. Full shape in `AGENTS.md` ("Inline Charts in Chat" section). Reach for `generate-chart` only when you're building a
|
|
39
|
+
For an in-chat answer, **emit a live `/chart` embed** — never `generate-chart`. The embed mounts a live `SqlChart` that re-queries when its source changes, and it doesn't choke on rigid JSON params the way the PNG action does. Full shape in `AGENTS.md` ("Inline Charts in Chat" section). Reach for `generate-chart` only when you're building a dashboard artifact that needs a persisted report image.
|
|
40
40
|
|
|
41
41
|
If `generate-chart` returns an error in any chat-answering flow, the recovery is to switch to the live embed, not to retry with reformatted params.
|
|
42
42
|
|
|
@@ -136,7 +136,7 @@ future analyses.
|
|
|
136
136
|
- Before finalizing an analytics answer, make the evidence trail explicit enough
|
|
137
137
|
to audit: source(s), time window, filters, sample size or row count, join or
|
|
138
138
|
match method, caveats/gaps, and what action to take next when useful.
|
|
139
|
-
- Data-source status, data-dictionary reads, dashboard dry-runs, `update-dashboard`, `generate-chart
|
|
139
|
+
- Data-source status, data-dictionary reads, dashboard dry-runs, `update-dashboard`, and `generate-chart` are not data queries. For dashboard artifacts, run at least one provider query action and preserve the result evidence in the final answer or dashboard config/description.
|
|
140
140
|
- Use action arguments such as `query`, `objectType`, `properties`, `owner`, `limit`, or provider-specific filters to narrow output; if an action returns a broad batch, filter it in your analysis and cite the records used.
|
|
141
141
|
- Update the relevant `.agents/skills/<provider>/SKILL.md` when you discover new patterns.
|
|
142
142
|
- For BigQuery queries, check `.agents/skills/bigquery/SKILL.md` first; if the data dictionary does not contain the exact table/columns, call `search-bigquery-schema`.
|