@agent-native/core 0.131.9 → 0.132.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +105 -0
- package/corpus/core/docs/content/durable-background-runs.mdx +15 -6
- package/corpus/core/docs/design/durable-agent-runs.md +15 -11
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/activity.ts +127 -10
- package/corpus/core/src/a2a/index.ts +3 -0
- package/corpus/core/src/action.ts +52 -11
- package/corpus/core/src/agent/durable-background.ts +38 -28
- package/corpus/core/src/agent/harness/background.ts +12 -2
- package/corpus/core/src/agent/production-agent.ts +210 -43
- package/corpus/core/src/agent/run-store.ts +45 -3
- package/corpus/core/src/application-state/handlers.ts +45 -0
- package/corpus/core/src/application-state/index.ts +3 -0
- package/corpus/core/src/application-state/store.ts +28 -11
- package/corpus/core/src/audit/redact.ts +104 -18
- package/corpus/core/src/client/AgentPanel.tsx +2 -7
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +3 -0
- package/corpus/core/src/client/RunStuckBanner.tsx +29 -6
- package/corpus/core/src/client/agent-chat.ts +4 -9
- package/corpus/core/src/client/application-state.ts +115 -12
- package/corpus/core/src/client/dynamic-suggestions.ts +4 -12
- package/corpus/core/src/client/guided-questions.tsx +12 -23
- package/corpus/core/src/db/client.ts +148 -28
- package/corpus/core/src/db/create-get-db.ts +39 -6
- package/corpus/core/src/db/runtime-diagnostics.ts +41 -6
- package/corpus/core/src/deploy/build.ts +26 -33
- package/corpus/core/src/feature-flags/store.ts +10 -5
- package/corpus/core/src/integrations/a2a-continuations-store.ts +11 -0
- package/corpus/core/src/integrations/config-store.ts +13 -0
- package/corpus/core/src/integrations/google-docs-poller.ts +26 -2
- package/corpus/core/src/mcp-client/routes.ts +33 -2
- package/corpus/core/src/onboarding/plugin.ts +31 -25
- package/corpus/core/src/org/accept-pending.ts +2 -0
- package/corpus/core/src/org/auto-join-domain.ts +2 -0
- package/corpus/core/src/org/context.ts +35 -10
- package/corpus/core/src/org/handlers.ts +6 -0
- package/corpus/core/src/org/request-org-cache.ts +71 -0
- package/corpus/core/src/resources/store.ts +10 -9
- package/corpus/core/src/scripts/runner.ts +6 -3
- package/corpus/core/src/secrets/storage.ts +97 -5
- package/corpus/core/src/server/action-change.ts +33 -0
- package/corpus/core/src/server/action-routes.ts +11 -9
- package/corpus/core/src/server/agent-teams.ts +10 -2
- package/corpus/core/src/server/better-auth-instance.ts +39 -12
- package/corpus/core/src/server/core-routes-plugin.ts +19 -2
- package/corpus/core/src/server/credential-provider.ts +57 -14
- package/corpus/core/src/server/http-response-telemetry.ts +1 -0
- package/corpus/core/src/server/poll.ts +95 -57
- package/corpus/core/src/server/voice-providers-status.ts +19 -4
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +38 -0
- package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +1 -1
- package/corpus/templates/analytics/actions/ensure-native-v2-dashboards.ts +299 -0
- package/corpus/templates/analytics/actions/list-native-v2-dashboard-templates.ts +34 -0
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +82 -0
- package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +2 -2
- package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +40 -34
- package/corpus/templates/analytics/app/hooks/use-user-pref.ts +3 -3
- package/corpus/templates/analytics/app/i18n-data.ts +40 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +18 -10
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +14 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +1 -0
- package/corpus/templates/analytics/app/routes/chart.tsx +3 -0
- package/corpus/templates/analytics/changelog/2026-07-30-analytics-dashboards-now-expose-native-funnel-heatmap-callou.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-30-analytics-now-ships-reusable-native-v2-dashboard-manifests-w.md +6 -0
- package/corpus/templates/analytics/server/lib/dashboard-report-render.ts +81 -1
- package/corpus/templates/analytics/server/lib/native-v2-dashboards.ts +457 -0
- package/corpus/templates/analytics/server/lib/report-chart-svg.ts +73 -2
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +1 -1
- package/corpus/templates/analytics/shared/dashboard-funnel.ts +83 -0
- package/corpus/templates/content/changelog/2026-07-30-long-running-delegated-requests-now-continue-reliably.md +6 -0
- package/corpus/templates/content/netlify.toml +3 -0
- package/corpus/templates/content/server/plugins/agent-chat.ts +1 -0
- package/corpus/templates/slides/.agents/skills/create-deck/SKILL.md +2 -1
- package/corpus/templates/slides/actions/patch-deck.ts +25 -1
- package/corpus/templates/slides/app/components/deck/DeckCard.tsx +17 -4
- package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +9 -1
- package/corpus/templates/slides/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +11 -3
- package/corpus/templates/slides/app/lib/sanitize-slide-html.ts +39 -11
- package/corpus/templates/slides/app/pages/Index.tsx +2 -2
- package/corpus/templates/slides/changelog/2026-07-30-slide-previews-keep-their-aspect-ratio-isolate-embedded-styl.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-30-slides-no-longer-lose-decks-while-organization-access-is-loa.md +6 -0
- package/corpus/toolkit/CHANGELOG.md +8 -0
- package/corpus/toolkit/package.json +1 -1
- package/corpus/toolkit/src/composer/runtime-adapters.tsx +16 -11
- package/dist/a2a/activity.d.ts +22 -4
- package/dist/a2a/activity.d.ts.map +1 -1
- package/dist/a2a/activity.js +97 -11
- package/dist/a2a/activity.js.map +1 -1
- package/dist/a2a/index.d.ts +1 -1
- package/dist/a2a/index.d.ts.map +1 -1
- package/dist/a2a/index.js +1 -1
- package/dist/a2a/index.js.map +1 -1
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js +41 -11
- package/dist/action.js.map +1 -1
- package/dist/agent/durable-background.d.ts +13 -8
- package/dist/agent/durable-background.d.ts.map +1 -1
- package/dist/agent/durable-background.js +36 -28
- package/dist/agent/durable-background.js.map +1 -1
- package/dist/agent/harness/background.d.ts.map +1 -1
- package/dist/agent/harness/background.js +12 -2
- package/dist/agent/harness/background.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +195 -44
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-store.d.ts +2 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +44 -3
- package/dist/agent/run-store.js.map +1 -1
- package/dist/application-state/handlers.d.ts +15 -0
- package/dist/application-state/handlers.d.ts.map +1 -1
- package/dist/application-state/handlers.js +42 -2
- package/dist/application-state/handlers.js.map +1 -1
- package/dist/application-state/index.d.ts +2 -2
- package/dist/application-state/index.d.ts.map +1 -1
- package/dist/application-state/index.js +2 -2
- package/dist/application-state/index.js.map +1 -1
- package/dist/application-state/store.d.ts +11 -0
- package/dist/application-state/store.d.ts.map +1 -1
- package/dist/application-state/store.js +23 -10
- package/dist/application-state/store.js.map +1 -1
- package/dist/audit/redact.d.ts +25 -2
- package/dist/audit/redact.d.ts.map +1 -1
- package/dist/audit/redact.js +85 -21
- package/dist/audit/redact.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +2 -7
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +1 -1
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/RunStuckBanner.d.ts +11 -1
- package/dist/client/RunStuckBanner.d.ts.map +1 -1
- package/dist/client/RunStuckBanner.js +20 -7
- package/dist/client/RunStuckBanner.js.map +1 -1
- package/dist/client/agent-chat.d.ts.map +1 -1
- package/dist/client/agent-chat.js +4 -6
- package/dist/client/agent-chat.js.map +1 -1
- package/dist/client/application-state.d.ts +11 -0
- package/dist/client/application-state.d.ts.map +1 -1
- package/dist/client/application-state.js +87 -9
- package/dist/client/application-state.js.map +1 -1
- package/dist/client/dynamic-suggestions.d.ts.map +1 -1
- package/dist/client/dynamic-suggestions.js +4 -13
- package/dist/client/dynamic-suggestions.js.map +1 -1
- package/dist/client/guided-questions.d.ts.map +1 -1
- package/dist/client/guided-questions.js +6 -22
- package/dist/client/guided-questions.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/db/client.d.ts +57 -10
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +122 -31
- package/dist/db/client.js.map +1 -1
- package/dist/db/create-get-db.d.ts.map +1 -1
- package/dist/db/create-get-db.js +31 -7
- package/dist/db/create-get-db.js.map +1 -1
- package/dist/db/runtime-diagnostics.d.ts +1 -0
- package/dist/db/runtime-diagnostics.d.ts.map +1 -1
- package/dist/db/runtime-diagnostics.js +31 -6
- package/dist/db/runtime-diagnostics.js.map +1 -1
- package/dist/deploy/build.d.ts +5 -20
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +18 -30
- package/dist/deploy/build.js.map +1 -1
- package/dist/feature-flags/store.d.ts.map +1 -1
- package/dist/feature-flags/store.js +10 -5
- package/dist/feature-flags/store.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/integrations/a2a-continuations-store.d.ts.map +1 -1
- package/dist/integrations/a2a-continuations-store.js +12 -0
- package/dist/integrations/a2a-continuations-store.js.map +1 -1
- package/dist/integrations/config-store.d.ts +1 -0
- package/dist/integrations/config-store.d.ts.map +1 -1
- package/dist/integrations/config-store.js +11 -0
- package/dist/integrations/config-store.js.map +1 -1
- package/dist/integrations/google-docs-poller.d.ts.map +1 -1
- package/dist/integrations/google-docs-poller.js +19 -2
- package/dist/integrations/google-docs-poller.js.map +1 -1
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/mcp/screen-memory-stdio.d.ts.map +1 -1
- package/dist/mcp-client/routes.d.ts.map +1 -1
- package/dist/mcp-client/routes.js +30 -2
- package/dist/mcp-client/routes.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +1 -1
- package/dist/onboarding/plugin.d.ts.map +1 -1
- package/dist/onboarding/plugin.js +12 -8
- package/dist/onboarding/plugin.js.map +1 -1
- package/dist/org/accept-pending.d.ts.map +1 -1
- package/dist/org/accept-pending.js +2 -0
- package/dist/org/accept-pending.js.map +1 -1
- package/dist/org/auto-join-domain.d.ts.map +1 -1
- package/dist/org/auto-join-domain.js +2 -0
- package/dist/org/auto-join-domain.js.map +1 -1
- package/dist/org/context.d.ts.map +1 -1
- package/dist/org/context.js +29 -9
- package/dist/org/context.js.map +1 -1
- package/dist/org/handlers.d.ts.map +1 -1
- package/dist/org/handlers.js +5 -0
- package/dist/org/handlers.js.map +1 -1
- package/dist/org/request-org-cache.d.ts +16 -0
- package/dist/org/request-org-cache.d.ts.map +1 -0
- package/dist/org/request-org-cache.js +62 -0
- package/dist/org/request-org-cache.js.map +1 -0
- package/dist/resources/store.d.ts.map +1 -1
- package/dist/resources/store.js +11 -9
- package/dist/resources/store.js.map +1 -1
- package/dist/scripts/runner.d.ts.map +1 -1
- package/dist/scripts/runner.js +3 -3
- package/dist/scripts/runner.js.map +1 -1
- package/dist/secrets/storage.d.ts.map +1 -1
- package/dist/secrets/storage.js +82 -5
- package/dist/secrets/storage.js.map +1 -1
- package/dist/server/action-change.d.ts +20 -0
- package/dist/server/action-change.d.ts.map +1 -1
- package/dist/server/action-change.js +29 -0
- package/dist/server/action-change.js.map +1 -1
- package/dist/server/action-routes.d.ts.map +1 -1
- package/dist/server/action-routes.js +7 -8
- package/dist/server/action-routes.js.map +1 -1
- package/dist/server/agent-teams.d.ts.map +1 -1
- package/dist/server/agent-teams.js +10 -2
- package/dist/server/agent-teams.js.map +1 -1
- package/dist/server/better-auth-instance.d.ts.map +1 -1
- package/dist/server/better-auth-instance.js +31 -14
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts +1 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +18 -4
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/credential-provider.d.ts +16 -0
- package/dist/server/credential-provider.d.ts.map +1 -1
- package/dist/server/credential-provider.js +49 -13
- package/dist/server/credential-provider.js.map +1 -1
- package/dist/server/http-response-telemetry.d.ts.map +1 -1
- package/dist/server/http-response-telemetry.js +1 -0
- package/dist/server/http-response-telemetry.js.map +1 -1
- package/dist/server/poll.d.ts +6 -0
- package/dist/server/poll.d.ts.map +1 -1
- package/dist/server/poll.js +84 -47
- package/dist/server/poll.js.map +1 -1
- package/dist/server/voice-providers-status.d.ts.map +1 -1
- package/dist/server/voice-providers-status.js +12 -3
- package/dist/server/voice-providers-status.js.map +1 -1
- package/docs/content/durable-background-runs.mdx +15 -6
- package/docs/design/durable-agent-runs.md +15 -11
- package/package.json +2 -2
- package/src/a2a/activity.ts +127 -10
- package/src/a2a/index.ts +3 -0
- package/src/action.ts +52 -11
- package/src/agent/durable-background.ts +38 -28
- package/src/agent/harness/background.ts +12 -2
- package/src/agent/production-agent.ts +210 -43
- package/src/agent/run-store.ts +45 -3
- package/src/application-state/handlers.ts +45 -0
- package/src/application-state/index.ts +3 -0
- package/src/application-state/store.ts +28 -11
- package/src/audit/redact.ts +104 -18
- package/src/client/AgentPanel.tsx +2 -7
- package/src/client/MultiTabAssistantChat.tsx +3 -0
- package/src/client/RunStuckBanner.tsx +29 -6
- package/src/client/agent-chat.ts +4 -9
- package/src/client/application-state.ts +115 -12
- package/src/client/dynamic-suggestions.ts +4 -12
- package/src/client/guided-questions.tsx +12 -23
- package/src/db/client.ts +148 -28
- package/src/db/create-get-db.ts +39 -6
- package/src/db/runtime-diagnostics.ts +41 -6
- package/src/deploy/build.ts +26 -33
- package/src/feature-flags/store.ts +10 -5
- package/src/integrations/a2a-continuations-store.ts +11 -0
- package/src/integrations/config-store.ts +13 -0
- package/src/integrations/google-docs-poller.ts +26 -2
- package/src/mcp-client/routes.ts +33 -2
- package/src/onboarding/plugin.ts +31 -25
- package/src/org/accept-pending.ts +2 -0
- package/src/org/auto-join-domain.ts +2 -0
- package/src/org/context.ts +35 -10
- package/src/org/handlers.ts +6 -0
- package/src/org/request-org-cache.ts +71 -0
- package/src/resources/store.ts +10 -9
- package/src/scripts/runner.ts +6 -3
- package/src/secrets/storage.ts +97 -5
- package/src/server/action-change.ts +33 -0
- package/src/server/action-routes.ts +11 -9
- package/src/server/agent-teams.ts +10 -2
- package/src/server/better-auth-instance.ts +39 -12
- package/src/server/core-routes-plugin.ts +19 -2
- package/src/server/credential-provider.ts +57 -14
- package/src/server/http-response-telemetry.ts +1 -0
- package/src/server/poll.ts +95 -57
- package/src/server/voice-providers-status.ts +19 -4
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
SqlDashboardConfig,
|
|
3
|
+
SqlPanel,
|
|
4
|
+
} from "../../app/pages/adhoc/sql-dashboard/types";
|
|
5
|
+
|
|
6
|
+
export const NATIVE_V2_DASHBOARD_VERSION = "2026-07-30-v1";
|
|
7
|
+
|
|
8
|
+
export const NATIVE_V2_DASHBOARD_IDS = [
|
|
9
|
+
"customer-roi-v2",
|
|
10
|
+
"account-engagement-v2",
|
|
11
|
+
"cross-sell-v2",
|
|
12
|
+
"win-loss-v2",
|
|
13
|
+
] as const;
|
|
14
|
+
|
|
15
|
+
export type NativeV2DashboardId = (typeof NATIVE_V2_DASHBOARD_IDS)[number];
|
|
16
|
+
|
|
17
|
+
export type NativeV2BindingKey =
|
|
18
|
+
| "roi.kpi"
|
|
19
|
+
| "roi.trend"
|
|
20
|
+
| "roi.detail"
|
|
21
|
+
| "engagement.kpi"
|
|
22
|
+
| "engagement.trend"
|
|
23
|
+
| "engagement.heatmap"
|
|
24
|
+
| "engagement.alerts"
|
|
25
|
+
| "cross_sell.funnel"
|
|
26
|
+
| "cross_sell.kpi"
|
|
27
|
+
| "cross_sell.pipeline"
|
|
28
|
+
| "cross_sell.trend"
|
|
29
|
+
| "win_loss.summary"
|
|
30
|
+
| "win_loss.reasons"
|
|
31
|
+
| "win_loss.evidence"
|
|
32
|
+
| "win_loss.alerts";
|
|
33
|
+
|
|
34
|
+
export interface NativeV2Binding {
|
|
35
|
+
programId: string;
|
|
36
|
+
params?: Record<string, unknown>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface NativeV2PanelManifest {
|
|
40
|
+
id: string;
|
|
41
|
+
title: string;
|
|
42
|
+
bindingKey?: NativeV2BindingKey;
|
|
43
|
+
chartType: SqlPanel["chartType"];
|
|
44
|
+
width: number;
|
|
45
|
+
columns?: number;
|
|
46
|
+
tab?: string;
|
|
47
|
+
config?: SqlPanel["config"];
|
|
48
|
+
requiredColumns?: string[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface NativeV2DashboardManifest {
|
|
52
|
+
id: NativeV2DashboardId;
|
|
53
|
+
name: string;
|
|
54
|
+
description: string;
|
|
55
|
+
category: "Acquisition" | "Product" | "Operations";
|
|
56
|
+
tags: string[];
|
|
57
|
+
requiredBindings: NativeV2BindingKey[];
|
|
58
|
+
panels: NativeV2PanelManifest[];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const section = (
|
|
62
|
+
id: string,
|
|
63
|
+
title: string,
|
|
64
|
+
tab: string,
|
|
65
|
+
columns = 6,
|
|
66
|
+
): NativeV2PanelManifest => ({
|
|
67
|
+
id,
|
|
68
|
+
title,
|
|
69
|
+
chartType: "section",
|
|
70
|
+
width: 1,
|
|
71
|
+
columns,
|
|
72
|
+
tab,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const boundPanel = (
|
|
76
|
+
panel: Omit<NativeV2PanelManifest, "bindingKey"> & {
|
|
77
|
+
bindingKey: NativeV2BindingKey;
|
|
78
|
+
requiredColumns: string[];
|
|
79
|
+
},
|
|
80
|
+
): NativeV2PanelManifest => panel;
|
|
81
|
+
|
|
82
|
+
export const nativeV2DashboardManifests: readonly NativeV2DashboardManifest[] =
|
|
83
|
+
[
|
|
84
|
+
{
|
|
85
|
+
id: "customer-roi-v2",
|
|
86
|
+
name: "Customer ROI v2",
|
|
87
|
+
description:
|
|
88
|
+
"Native value-realization dashboard: KPI, period trend, account detail, and evidence callouts.",
|
|
89
|
+
category: "Acquisition",
|
|
90
|
+
tags: ["native", "v2", "roi", "value-realization", "customer"],
|
|
91
|
+
requiredBindings: ["roi.kpi", "roi.trend", "roi.detail"],
|
|
92
|
+
panels: [
|
|
93
|
+
section("roi-overview", "Value realization", "Overview"),
|
|
94
|
+
boundPanel({
|
|
95
|
+
id: "roi-kpi",
|
|
96
|
+
title: "Realized value",
|
|
97
|
+
bindingKey: "roi.kpi",
|
|
98
|
+
chartType: "metric",
|
|
99
|
+
width: 1,
|
|
100
|
+
tab: "Overview",
|
|
101
|
+
config: {
|
|
102
|
+
yKey: "value",
|
|
103
|
+
yFormatter: "currency",
|
|
104
|
+
description: "The bound program's current realized-value KPI.",
|
|
105
|
+
},
|
|
106
|
+
requiredColumns: ["value"],
|
|
107
|
+
}),
|
|
108
|
+
boundPanel({
|
|
109
|
+
id: "roi-trend",
|
|
110
|
+
title: "Value over time",
|
|
111
|
+
bindingKey: "roi.trend",
|
|
112
|
+
chartType: "line",
|
|
113
|
+
width: 3,
|
|
114
|
+
tab: "Overview",
|
|
115
|
+
config: {
|
|
116
|
+
xKey: "period",
|
|
117
|
+
yKey: "value",
|
|
118
|
+
yFormatter: "currency",
|
|
119
|
+
legend: false,
|
|
120
|
+
},
|
|
121
|
+
requiredColumns: ["period", "value"],
|
|
122
|
+
}),
|
|
123
|
+
boundPanel({
|
|
124
|
+
id: "roi-detail",
|
|
125
|
+
title: "Account-level value evidence",
|
|
126
|
+
bindingKey: "roi.detail",
|
|
127
|
+
chartType: "table",
|
|
128
|
+
width: 3,
|
|
129
|
+
tab: "Accounts",
|
|
130
|
+
config: {
|
|
131
|
+
columns: [
|
|
132
|
+
{ key: "account", label: "Account" },
|
|
133
|
+
{ key: "period", label: "Period", format: "date" },
|
|
134
|
+
{ key: "investment", label: "Investment", format: "currency" },
|
|
135
|
+
{ key: "value", label: "Value", format: "currency" },
|
|
136
|
+
{ key: "roi", label: "ROI", format: "percent" },
|
|
137
|
+
{ key: "matched_by", label: "Matched by" },
|
|
138
|
+
{ key: "match_quality", label: "Match quality" },
|
|
139
|
+
],
|
|
140
|
+
limit: 100,
|
|
141
|
+
},
|
|
142
|
+
requiredColumns: [
|
|
143
|
+
"account",
|
|
144
|
+
"period",
|
|
145
|
+
"investment",
|
|
146
|
+
"value",
|
|
147
|
+
"roi",
|
|
148
|
+
"matched_by",
|
|
149
|
+
"match_quality",
|
|
150
|
+
],
|
|
151
|
+
}),
|
|
152
|
+
],
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
id: "account-engagement-v2",
|
|
156
|
+
name: "Account Engagement v2",
|
|
157
|
+
description:
|
|
158
|
+
"Native engagement coverage dashboard: headline coverage, daily trend, segment heatmap, and alerts.",
|
|
159
|
+
category: "Product",
|
|
160
|
+
tags: ["native", "v2", "engagement", "adoption", "customer"],
|
|
161
|
+
requiredBindings: [
|
|
162
|
+
"engagement.kpi",
|
|
163
|
+
"engagement.trend",
|
|
164
|
+
"engagement.heatmap",
|
|
165
|
+
"engagement.alerts",
|
|
166
|
+
],
|
|
167
|
+
panels: [
|
|
168
|
+
section("engagement-overview", "Account engagement", "Overview"),
|
|
169
|
+
boundPanel({
|
|
170
|
+
id: "engagement-kpi",
|
|
171
|
+
title: "Engaged accounts",
|
|
172
|
+
bindingKey: "engagement.kpi",
|
|
173
|
+
chartType: "metric",
|
|
174
|
+
width: 1,
|
|
175
|
+
tab: "Overview",
|
|
176
|
+
config: {
|
|
177
|
+
yKey: "value",
|
|
178
|
+
description: "The bound program's current engaged-account count.",
|
|
179
|
+
},
|
|
180
|
+
requiredColumns: ["value"],
|
|
181
|
+
}),
|
|
182
|
+
boundPanel({
|
|
183
|
+
id: "engagement-trend",
|
|
184
|
+
title: "Engagement over time",
|
|
185
|
+
bindingKey: "engagement.trend",
|
|
186
|
+
chartType: "line",
|
|
187
|
+
width: 3,
|
|
188
|
+
tab: "Overview",
|
|
189
|
+
config: {
|
|
190
|
+
xKey: "period",
|
|
191
|
+
yKey: "value",
|
|
192
|
+
legend: false,
|
|
193
|
+
},
|
|
194
|
+
requiredColumns: ["period", "value"],
|
|
195
|
+
}),
|
|
196
|
+
boundPanel({
|
|
197
|
+
id: "engagement-heatmap",
|
|
198
|
+
title: "Engagement by segment",
|
|
199
|
+
bindingKey: "engagement.heatmap",
|
|
200
|
+
chartType: "heatmap",
|
|
201
|
+
width: 3,
|
|
202
|
+
tab: "Segments",
|
|
203
|
+
config: {
|
|
204
|
+
xKey: "period",
|
|
205
|
+
yKey: "value",
|
|
206
|
+
color: "segment",
|
|
207
|
+
yFormatter: "percent",
|
|
208
|
+
},
|
|
209
|
+
requiredColumns: ["period", "segment", "value"],
|
|
210
|
+
}),
|
|
211
|
+
boundPanel({
|
|
212
|
+
id: "engagement-alerts",
|
|
213
|
+
title: "Engagement alerts",
|
|
214
|
+
bindingKey: "engagement.alerts",
|
|
215
|
+
chartType: "callout",
|
|
216
|
+
width: 3,
|
|
217
|
+
tab: "Alerts",
|
|
218
|
+
requiredColumns: ["severity", "message"],
|
|
219
|
+
}),
|
|
220
|
+
],
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
id: "cross-sell-v2",
|
|
224
|
+
name: "Cross-sell v2",
|
|
225
|
+
description:
|
|
226
|
+
"Native cross-sell dashboard: ordered opportunity funnel, pipeline value, account table, and trend.",
|
|
227
|
+
category: "Acquisition",
|
|
228
|
+
tags: ["native", "v2", "cross-sell", "pipeline", "gtm"],
|
|
229
|
+
requiredBindings: [
|
|
230
|
+
"cross_sell.funnel",
|
|
231
|
+
"cross_sell.kpi",
|
|
232
|
+
"cross_sell.pipeline",
|
|
233
|
+
"cross_sell.trend",
|
|
234
|
+
],
|
|
235
|
+
panels: [
|
|
236
|
+
section("cross-sell-overview", "Cross-sell pipeline", "Overview"),
|
|
237
|
+
boundPanel({
|
|
238
|
+
id: "cross-sell-funnel",
|
|
239
|
+
title: "Opportunity funnel",
|
|
240
|
+
bindingKey: "cross_sell.funnel",
|
|
241
|
+
chartType: "funnel",
|
|
242
|
+
width: 3,
|
|
243
|
+
tab: "Overview",
|
|
244
|
+
config: {
|
|
245
|
+
xKey: "stage",
|
|
246
|
+
yKey: "value",
|
|
247
|
+
yFormatter: "currency",
|
|
248
|
+
},
|
|
249
|
+
requiredColumns: ["stage", "value"],
|
|
250
|
+
}),
|
|
251
|
+
boundPanel({
|
|
252
|
+
id: "cross-sell-kpi",
|
|
253
|
+
title: "Pipeline value",
|
|
254
|
+
bindingKey: "cross_sell.kpi",
|
|
255
|
+
chartType: "metric",
|
|
256
|
+
width: 1,
|
|
257
|
+
tab: "Overview",
|
|
258
|
+
config: {
|
|
259
|
+
yKey: "value",
|
|
260
|
+
yFormatter: "currency",
|
|
261
|
+
},
|
|
262
|
+
requiredColumns: ["value"],
|
|
263
|
+
}),
|
|
264
|
+
boundPanel({
|
|
265
|
+
id: "cross-sell-pipeline",
|
|
266
|
+
title: "Account pipeline",
|
|
267
|
+
bindingKey: "cross_sell.pipeline",
|
|
268
|
+
chartType: "table",
|
|
269
|
+
width: 3,
|
|
270
|
+
tab: "Accounts",
|
|
271
|
+
config: {
|
|
272
|
+
columns: [
|
|
273
|
+
{ key: "account", label: "Account" },
|
|
274
|
+
{ key: "stage", label: "Stage" },
|
|
275
|
+
{ key: "value", label: "Value", format: "currency" },
|
|
276
|
+
{ key: "owner", label: "Owner" },
|
|
277
|
+
{ key: "matched_by", label: "Matched by" },
|
|
278
|
+
{ key: "match_quality", label: "Match quality" },
|
|
279
|
+
],
|
|
280
|
+
limit: 100,
|
|
281
|
+
},
|
|
282
|
+
requiredColumns: [
|
|
283
|
+
"account",
|
|
284
|
+
"stage",
|
|
285
|
+
"value",
|
|
286
|
+
"owner",
|
|
287
|
+
"matched_by",
|
|
288
|
+
"match_quality",
|
|
289
|
+
],
|
|
290
|
+
}),
|
|
291
|
+
boundPanel({
|
|
292
|
+
id: "cross-sell-trend",
|
|
293
|
+
title: "Pipeline over time",
|
|
294
|
+
bindingKey: "cross_sell.trend",
|
|
295
|
+
chartType: "area",
|
|
296
|
+
width: 3,
|
|
297
|
+
tab: "Trend",
|
|
298
|
+
config: {
|
|
299
|
+
xKey: "period",
|
|
300
|
+
yKey: "value",
|
|
301
|
+
yFormatter: "currency",
|
|
302
|
+
legend: false,
|
|
303
|
+
},
|
|
304
|
+
requiredColumns: ["period", "value"],
|
|
305
|
+
}),
|
|
306
|
+
],
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
id: "win-loss-v2",
|
|
310
|
+
name: "Win / Loss v2",
|
|
311
|
+
description:
|
|
312
|
+
"Native outcome dashboard: win/loss totals, reason breakdown, evidence table, and follow-up alerts.",
|
|
313
|
+
category: "Operations",
|
|
314
|
+
tags: ["native", "v2", "win-loss", "sales", "evidence"],
|
|
315
|
+
requiredBindings: [
|
|
316
|
+
"win_loss.summary",
|
|
317
|
+
"win_loss.reasons",
|
|
318
|
+
"win_loss.evidence",
|
|
319
|
+
"win_loss.alerts",
|
|
320
|
+
],
|
|
321
|
+
panels: [
|
|
322
|
+
section("win-loss-overview", "Win / loss outcomes", "Overview"),
|
|
323
|
+
boundPanel({
|
|
324
|
+
id: "win-loss-summary",
|
|
325
|
+
title: "Outcome totals",
|
|
326
|
+
bindingKey: "win_loss.summary",
|
|
327
|
+
chartType: "bar",
|
|
328
|
+
width: 3,
|
|
329
|
+
tab: "Overview",
|
|
330
|
+
config: {
|
|
331
|
+
xKey: "outcome",
|
|
332
|
+
yKey: "value",
|
|
333
|
+
yFormatter: "currency",
|
|
334
|
+
legend: false,
|
|
335
|
+
},
|
|
336
|
+
requiredColumns: ["outcome", "value"],
|
|
337
|
+
}),
|
|
338
|
+
boundPanel({
|
|
339
|
+
id: "win-loss-reasons",
|
|
340
|
+
title: "Reasons",
|
|
341
|
+
bindingKey: "win_loss.reasons",
|
|
342
|
+
chartType: "bar",
|
|
343
|
+
width: 3,
|
|
344
|
+
tab: "Reasons",
|
|
345
|
+
config: {
|
|
346
|
+
xKey: "reason",
|
|
347
|
+
yKey: "value",
|
|
348
|
+
legend: false,
|
|
349
|
+
},
|
|
350
|
+
requiredColumns: ["reason", "value"],
|
|
351
|
+
}),
|
|
352
|
+
boundPanel({
|
|
353
|
+
id: "win-loss-evidence",
|
|
354
|
+
title: "Outcome evidence",
|
|
355
|
+
bindingKey: "win_loss.evidence",
|
|
356
|
+
chartType: "table",
|
|
357
|
+
width: 3,
|
|
358
|
+
tab: "Evidence",
|
|
359
|
+
config: {
|
|
360
|
+
columns: [
|
|
361
|
+
{ key: "account", label: "Account" },
|
|
362
|
+
{ key: "outcome", label: "Outcome" },
|
|
363
|
+
{ key: "reason", label: "Reason" },
|
|
364
|
+
{ key: "value", label: "Value", format: "currency" },
|
|
365
|
+
{ key: "evidence", label: "Evidence" },
|
|
366
|
+
{ key: "match_quality", label: "Match quality" },
|
|
367
|
+
],
|
|
368
|
+
limit: 100,
|
|
369
|
+
},
|
|
370
|
+
requiredColumns: [
|
|
371
|
+
"account",
|
|
372
|
+
"outcome",
|
|
373
|
+
"reason",
|
|
374
|
+
"value",
|
|
375
|
+
"evidence",
|
|
376
|
+
"match_quality",
|
|
377
|
+
],
|
|
378
|
+
}),
|
|
379
|
+
boundPanel({
|
|
380
|
+
id: "win-loss-alerts",
|
|
381
|
+
title: "Follow-up alerts",
|
|
382
|
+
bindingKey: "win_loss.alerts",
|
|
383
|
+
chartType: "callout",
|
|
384
|
+
width: 3,
|
|
385
|
+
tab: "Alerts",
|
|
386
|
+
requiredColumns: ["severity", "message"],
|
|
387
|
+
}),
|
|
388
|
+
],
|
|
389
|
+
},
|
|
390
|
+
] as const;
|
|
391
|
+
|
|
392
|
+
export function getNativeV2DashboardManifest(
|
|
393
|
+
id: NativeV2DashboardId,
|
|
394
|
+
): NativeV2DashboardManifest {
|
|
395
|
+
const manifest = nativeV2DashboardManifests.find((entry) => entry.id === id);
|
|
396
|
+
if (!manifest) throw new Error(`Unknown native v2 dashboard: ${id}`);
|
|
397
|
+
return manifest;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
export function compileNativeV2Dashboard(
|
|
401
|
+
manifest: NativeV2DashboardManifest,
|
|
402
|
+
bindings: Partial<Record<NativeV2BindingKey, NativeV2Binding>>,
|
|
403
|
+
installedAt: string,
|
|
404
|
+
): SqlDashboardConfig {
|
|
405
|
+
const missing = manifest.requiredBindings.filter(
|
|
406
|
+
(bindingKey) => !bindings[bindingKey]?.programId.trim(),
|
|
407
|
+
);
|
|
408
|
+
if (missing.length > 0) {
|
|
409
|
+
throw new Error(
|
|
410
|
+
`${manifest.id} is missing required bindings: ${missing.join(", ")}`,
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return {
|
|
415
|
+
name: manifest.name,
|
|
416
|
+
description: manifest.description,
|
|
417
|
+
columns: 6,
|
|
418
|
+
catalog: {
|
|
419
|
+
templateId: manifest.id,
|
|
420
|
+
templateVersion: NATIVE_V2_DASHBOARD_VERSION,
|
|
421
|
+
installedAt,
|
|
422
|
+
},
|
|
423
|
+
panels: manifest.panels.map((panel) => {
|
|
424
|
+
if (!panel.bindingKey) {
|
|
425
|
+
return {
|
|
426
|
+
id: panel.id,
|
|
427
|
+
title: panel.title,
|
|
428
|
+
source: "first-party",
|
|
429
|
+
chartType: panel.chartType,
|
|
430
|
+
width: panel.width,
|
|
431
|
+
...(panel.columns ? { columns: panel.columns } : {}),
|
|
432
|
+
...(panel.tab ? { tab: panel.tab } : {}),
|
|
433
|
+
...(panel.config ? { config: panel.config } : {}),
|
|
434
|
+
sql: "",
|
|
435
|
+
} satisfies SqlPanel;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const binding = bindings[panel.bindingKey];
|
|
439
|
+
if (!binding) {
|
|
440
|
+
throw new Error(`Missing binding: ${panel.bindingKey}`);
|
|
441
|
+
}
|
|
442
|
+
return {
|
|
443
|
+
id: panel.id,
|
|
444
|
+
title: panel.title,
|
|
445
|
+
source: "program",
|
|
446
|
+
chartType: panel.chartType,
|
|
447
|
+
width: panel.width,
|
|
448
|
+
...(panel.tab ? { tab: panel.tab } : {}),
|
|
449
|
+
...(panel.config ? { config: panel.config } : {}),
|
|
450
|
+
sql: JSON.stringify({
|
|
451
|
+
programId: binding.programId,
|
|
452
|
+
...(binding.params ? { params: binding.params } : {}),
|
|
453
|
+
}),
|
|
454
|
+
} satisfies SqlPanel;
|
|
455
|
+
}),
|
|
456
|
+
};
|
|
457
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { OG_FONT_FAMILY } from "@agent-native/core/server";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import type { DashboardFunnelItem } from "../../shared/dashboard-funnel";
|
|
4
|
+
|
|
5
|
+
export type ReportChartType = "bar" | "line" | "area" | "pie" | "funnel";
|
|
4
6
|
|
|
5
7
|
export type ReportChartAxis = "left" | "right";
|
|
6
8
|
|
|
@@ -784,6 +786,75 @@ function renderPieChartSvg({
|
|
|
784
786
|
</svg>`;
|
|
785
787
|
}
|
|
786
788
|
|
|
789
|
+
export function renderFunnelChartSvg({
|
|
790
|
+
title,
|
|
791
|
+
subtitle,
|
|
792
|
+
rows,
|
|
793
|
+
width,
|
|
794
|
+
height,
|
|
795
|
+
formatter,
|
|
796
|
+
colors,
|
|
797
|
+
}: {
|
|
798
|
+
title?: string;
|
|
799
|
+
subtitle?: string;
|
|
800
|
+
rows: DashboardFunnelItem[];
|
|
801
|
+
width: number;
|
|
802
|
+
height: number;
|
|
803
|
+
formatter?: ReportChartValueFormatter;
|
|
804
|
+
colors?: string[];
|
|
805
|
+
}): string {
|
|
806
|
+
const theme = CHART_THEMES.light;
|
|
807
|
+
const palette = CHART_PALETTES.light;
|
|
808
|
+
const safeWidth = clampSize(width, 360, 2000);
|
|
809
|
+
const safeHeight = clampSize(height, 240, 1200);
|
|
810
|
+
const header = renderChartHeader({
|
|
811
|
+
title: title ?? "",
|
|
812
|
+
subtitle: subtitle ?? "",
|
|
813
|
+
width: safeWidth,
|
|
814
|
+
theme,
|
|
815
|
+
fontFamily: REPORT_CHART_FONT_FAMILY,
|
|
816
|
+
fit: true,
|
|
817
|
+
});
|
|
818
|
+
const top = header.headerBottom + 30;
|
|
819
|
+
const bottom = safeHeight - 24;
|
|
820
|
+
const labelWidth = Math.min(190, Math.max(120, safeWidth * 0.2));
|
|
821
|
+
const valueWidth = 150;
|
|
822
|
+
const barLeft = labelWidth;
|
|
823
|
+
const barRight = safeWidth - valueWidth;
|
|
824
|
+
const barWidth = Math.max(1, barRight - barLeft);
|
|
825
|
+
const rowHeight = 31;
|
|
826
|
+
const maxRows = Math.max(1, Math.floor((bottom - top) / rowHeight));
|
|
827
|
+
const visible = rows.slice(0, maxRows);
|
|
828
|
+
const hidden = rows.length - visible.length;
|
|
829
|
+
const maxValue = Math.max(...rows.map((row) => row.value), 1);
|
|
830
|
+
const bars = visible
|
|
831
|
+
.map((row, index) => {
|
|
832
|
+
const y = top + index * rowHeight;
|
|
833
|
+
const fillWidth = row.value > 0 ? (row.value / maxValue) * barWidth : 0;
|
|
834
|
+
const color = safeColor(colors?.[index], palette[index % palette.length]);
|
|
835
|
+
const dropOff =
|
|
836
|
+
row.dropOffPercent === null
|
|
837
|
+
? ""
|
|
838
|
+
: ` ${row.dropOffPercent < 0 ? "↑" : "↓"}${Math.abs(row.dropOffPercent).toFixed(1)}%`;
|
|
839
|
+
return `<text x="${(labelWidth - 12).toFixed(1)}" y="${(y + 20).toFixed(1)}" text-anchor="end" font-size="12" fill="${theme.labelColor}">${escapeXml(truncate(row.label, 24))}</text><rect x="${barLeft.toFixed(1)}" y="${(y + 7).toFixed(1)}" width="${barWidth.toFixed(1)}" height="16" rx="4" fill="${theme.gridColor}"/><rect x="${barLeft.toFixed(1)}" y="${(y + 7).toFixed(1)}" width="${fillWidth.toFixed(1)}" height="16" rx="4" fill="${color}"/><text x="${(barRight + 12).toFixed(1)}" y="${(y + 19).toFixed(1)}" font-size="12" fill="${theme.labelColor}">${escapeXml(`${formatTick(row.value, formatter)} ${row.percentOfFirst.toFixed(1)}%${dropOff}`)}</text>`;
|
|
840
|
+
})
|
|
841
|
+
.join("");
|
|
842
|
+
const overflow =
|
|
843
|
+
hidden > 0
|
|
844
|
+
? `<text x="${barLeft.toFixed(1)}" y="${(top + visible.length * rowHeight + 18).toFixed(1)}" font-size="12" fill="${theme.tickColor}">${escapeXml(`+${hidden} more stages`)}</text>`
|
|
845
|
+
: "";
|
|
846
|
+
|
|
847
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${safeWidth}" height="${safeHeight}" viewBox="0 0 ${safeWidth} ${safeHeight}" font-family="${REPORT_CHART_FONT_FAMILY}" role="img" aria-label="${escapeXml(title ?? "Funnel chart")}">
|
|
848
|
+
<rect width="${safeWidth}" height="${safeHeight}" rx="8" fill="${theme.background}"/>
|
|
849
|
+
${header.titleMarkup}
|
|
850
|
+
${header.subtitleMarkup}
|
|
851
|
+
<g font-family="${REPORT_CHART_FONT_FAMILY}">
|
|
852
|
+
${bars}
|
|
853
|
+
${overflow}
|
|
854
|
+
</g>
|
|
855
|
+
</svg>`;
|
|
856
|
+
}
|
|
857
|
+
|
|
787
858
|
/**
|
|
788
859
|
* Browser-facing renderer: clamps every value to a non-negative number and uses
|
|
789
860
|
* a system font stack. Kept for `generate-chart`'s saved-artifact fallback; the
|
|
@@ -867,7 +938,7 @@ export function renderReportChartSvg({
|
|
|
867
938
|
subtitle?: string;
|
|
868
939
|
labels: string[];
|
|
869
940
|
series: ReportChartSeries[];
|
|
870
|
-
type: ReportChartType
|
|
941
|
+
type: Exclude<ReportChartType, "funnel">;
|
|
871
942
|
width: number;
|
|
872
943
|
height: number;
|
|
873
944
|
stacked?: boolean;
|
|
@@ -58,7 +58,7 @@ export const NON_ANALYTICS_REQUEST_GUIDANCE =
|
|
|
58
58
|
export const ANALYTICS_CUSTOM_BLOCK_GUIDANCE =
|
|
59
59
|
"<analytics-artifact-guidance>\n" +
|
|
60
60
|
"Analytics has one user-facing artifact type: dashboards. Build with native dashboard panels and Data Programs first. A sandboxed extension embedded in a dashboard is presented to users as a Custom Block, not as a separate Analytics artifact. " +
|
|
61
|
-
"Use native chart, table, metric, section, filter, and layout capabilities whenever they can represent the request faithfully. Use a Data Program when the durable need is reusable fetching, transformation, or computed data that native panels can render. Do not create a Custom Block merely because a request says custom, asks for a dashboard, or would take more effort with native components. " +
|
|
61
|
+
"Use native chart, table, metric, section, funnel, heatmap, callout, filter, and layout capabilities whenever they can represent the request faithfully. Reusable ROI, engagement, cross-sell, and win/loss dashboards should compose these native panels around real SQL or Data Program results. Use a Data Program when the durable need is reusable fetching, transformation, or computed data that native panels can render. Do not create a Custom Block merely because a request says custom, asks for a dashboard, or would take more effort with native components. " +
|
|
62
62
|
'Create a Custom Block only when the user explicitly asks for a genuinely bespoke or one-off visualization or interaction, the native dashboard model cannot represent it faithfully, and its intended scope is this dashboard. Create it with `create-extension`, immediately embed it as a `chartType: "extension"` panel with `config.extensionId`, and set `config.customBlock` to `{ authoredBy: "agent", intent: "one-off", scope: "dashboard", nativeGapReason: "custom-visualization" | "custom-interaction" | "custom-layout" | "other" }`. Choose the narrow categorical reason; never put prompt text, customer data, or other free text in this metadata. Use the host theme CSS variables and match the dashboard typography, card spacing, and density so the sandboxed content reads as an agent-authored patch to Analytics instead of a foreign mini-app. Describe it as a sandboxed, agent-authored dashboard patch. Never leave it standalone or direct the user to an Extensions page. ' +
|
|
63
63
|
"A Custom Block is a fast runtime patch, not the durable destination for reusable product behavior. If the request should work across dashboards or users, changes app chrome or business logic, adds a reusable chart type, needs native accessibility/export/governance, or explicitly asks for app code, a PR, or a native feature, call `connect-builder` with the request verbatim instead of creating a Custom Block. If scope is ambiguous, ask whether the user wants a one-off block for this dashboard or a reusable app feature before choosing. " +
|
|
64
64
|
"When the user chooses Promote to app code, preserve the existing Custom Block and pass its dashboard id, panel id, extension id, and requested native placement through `connect-builder`; do not delete or replace the block until the native implementation is reviewed and deployed. Legacy analyses and existing extension-backed dashboards remain readable and editable for compatibility.\n" +
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export interface DashboardFunnelItem {
|
|
2
|
+
label: string;
|
|
3
|
+
value: number;
|
|
4
|
+
percentOfFirst: number;
|
|
5
|
+
dropOffPercent: number | null;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface DashboardFunnelRows {
|
|
9
|
+
labelKey: string;
|
|
10
|
+
valueKey: string;
|
|
11
|
+
items: DashboardFunnelItem[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function finiteNumber(value: unknown): number | null {
|
|
15
|
+
if (typeof value === "number") {
|
|
16
|
+
return Number.isFinite(value) ? value : null;
|
|
17
|
+
}
|
|
18
|
+
if (typeof value !== "string" || value.trim() === "") return null;
|
|
19
|
+
const parsed = Number(value);
|
|
20
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function hasNumericValue(
|
|
24
|
+
rows: Array<Record<string, unknown>>,
|
|
25
|
+
key: string,
|
|
26
|
+
): boolean {
|
|
27
|
+
return rows.some((row) => {
|
|
28
|
+
const value = finiteNumber(row[key]);
|
|
29
|
+
return value !== null && value >= 0;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function resolveDashboardFunnelRows(
|
|
34
|
+
rows: Array<Record<string, unknown>>,
|
|
35
|
+
configuredLabelKey?: string,
|
|
36
|
+
configuredValueKey?: string,
|
|
37
|
+
): DashboardFunnelRows {
|
|
38
|
+
if (rows.length === 0) {
|
|
39
|
+
return { labelKey: "", valueKey: "", items: [] };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const columns = Object.keys(rows[0]);
|
|
43
|
+
const columnSet = new Set(columns);
|
|
44
|
+
const labelKey =
|
|
45
|
+
(configuredLabelKey && columnSet.has(configuredLabelKey)
|
|
46
|
+
? configuredLabelKey
|
|
47
|
+
: undefined) ||
|
|
48
|
+
columns.find((key) =>
|
|
49
|
+
rows.some((row) => typeof row[key] === "string" && row[key].trim()),
|
|
50
|
+
) ||
|
|
51
|
+
columns[0] ||
|
|
52
|
+
"";
|
|
53
|
+
const valueKey =
|
|
54
|
+
(configuredValueKey && columnSet.has(configuredValueKey)
|
|
55
|
+
? configuredValueKey
|
|
56
|
+
: undefined) ||
|
|
57
|
+
columns.find((key) => key !== labelKey && hasNumericValue(rows, key)) ||
|
|
58
|
+
"";
|
|
59
|
+
|
|
60
|
+
const parsed = rows.flatMap((row) => {
|
|
61
|
+
const label = String(row[labelKey] ?? "").trim();
|
|
62
|
+
const value = finiteNumber(row[valueKey]);
|
|
63
|
+
if (!label || value === null || value < 0) return [];
|
|
64
|
+
return [{ label, value }];
|
|
65
|
+
});
|
|
66
|
+
const firstValue = parsed[0]?.value ?? 0;
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
labelKey,
|
|
70
|
+
valueKey,
|
|
71
|
+
items: parsed.map((item, index) => {
|
|
72
|
+
const previousValue = parsed[index - 1]?.value;
|
|
73
|
+
return {
|
|
74
|
+
...item,
|
|
75
|
+
percentOfFirst: firstValue > 0 ? (item.value / firstValue) * 100 : 0,
|
|
76
|
+
dropOffPercent:
|
|
77
|
+
previousValue && previousValue > 0
|
|
78
|
+
? ((previousValue - item.value) / previousValue) * 100
|
|
79
|
+
: null,
|
|
80
|
+
};
|
|
81
|
+
}),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
@@ -10,6 +10,9 @@ GTM_CONTAINER_ID = "GTM-N3WSTXZ"
|
|
|
10
10
|
NITRO_PRESET = "netlify"
|
|
11
11
|
NPM_CONFIG_PRODUCTION = "false"
|
|
12
12
|
AGENT_RUN_SOFT_TIMEOUT_MS = "55000"
|
|
13
|
+
# Content agent turns can delegate long-running work to sibling apps. Emit and
|
|
14
|
+
# use the async worker so those calls are not cut off by the foreground wall.
|
|
15
|
+
AGENT_CHAT_DURABLE_BACKGROUND = "true"
|
|
13
16
|
|
|
14
17
|
# A2A delegation calls run a full agent loop (LLM + tool calls + DB queries).
|
|
15
18
|
# The framework does not impose a default agent-run timeout; if a template sets
|
|
@@ -31,6 +31,7 @@ const INITIAL_TOOL_NAMES = [
|
|
|
31
31
|
|
|
32
32
|
export default createAgentChatPlugin({
|
|
33
33
|
appId: "content",
|
|
34
|
+
durableBackgroundRuns: true,
|
|
34
35
|
actions: loadActionsFromStaticRegistry(actionsRegistry),
|
|
35
36
|
initialToolNames: INITIAL_TOOL_NAMES,
|
|
36
37
|
anonymousOwner: resolvePublicViewerOwner,
|
|
@@ -23,7 +23,8 @@ description: How to create a new deck with slides from scratch. Read this before
|
|
|
23
23
|
When the UI has already created the empty deck, keep its id and rename it before
|
|
24
24
|
adding the first slide. Call `patch-deck` with a `patch-deck-fields` operation
|
|
25
25
|
whose `fields.title` is the generated title. Do not leave the pre-created deck's
|
|
26
|
-
placeholder title in place.
|
|
26
|
+
placeholder title in place. Include only `title` in that operation's `fields`;
|
|
27
|
+
omit all other optional fields.
|
|
27
28
|
|
|
28
29
|
Follow the creative-context reuse ladder before inventing a slide language:
|
|
29
30
|
reuse an approved native template unchanged, compose approved pieces, lightly
|