From e034faef97cf6a3d90a5205527e60cdb53ddcc1e Mon Sep 17 00:00:00 2001
From: iza <59828082+izadoesdev@users.noreply.github.com>
Date: Thu, 28 May 2026 02:05:34 +0300
Subject: [PATCH 1/4] feat(insights): investigation pipeline overhaul with
tools, actions, and model routing
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* refactor(ai): extract shared OAuth token helpers, clean up GSC integration
- Extract getOAuthToken + createCachedTokenFn into shared oauth-token.ts,
replacing identical copy-pasted token-fetch logic in search-console.ts,
github-tools.ts, and generation.ts
- Extract getUserProviderToken in RPC integrations router, deduplicating
the token lookup in checkSearchConsoleAccess and listGitHubRepos
- Extract useOAuthConnect hook in dashboard integrations UI, deduplicating
the linkSocial mutation between GSC and GitHub rows
- Extract querySearchAnalytics as a standalone testable function
- Move ALWAYS_ON_TOOLS set to module scope in generation.ts
- Add 8 unit tests for search console query mapping, error handling, and
request shape
* feat(slack): show thinking indicator immediately on stream start
Start the Slack stream eagerly with a task_update chunk (status:
in_progress) before the agent produces output, so users see the
animated thinking card instead of just a reaction emoji. Resolves
thinking to "complete" on first text flush and "error" on failures.
Also desloppified respond.ts: removed dead lazy-start logic
(tryStartStream, shouldStream, streamStartAttempted), extracted
flushAndStop/recoverFromError/logStreamError helpers, added early
bail-out for pre-aborted signals, and added a streaming-unavailable
fallback test.
* chore(deps): align ai sdk versions
* feat(insights): broaden investigation signals
* feat(dashboard): show insight investigation context
* chore(repo): tidy cleanup leftovers
* feat(db): per-table column validation in SQL validator
Replace BAD_EVENTS_COLUMN_REPLACEMENTS blocklist with proper
AGENT_TABLE_COLUMNS schema map. Validates alias.column patterns
against the actual table schema — catches cross-table misuse like
es.browser_name on error_spans before it hits ClickHouse.
* refactor(insights): extract prompts, deduplicate detection, improve readability
- Extract prompt builders and data fetchers into prompts.ts (266 lines)
- generation.ts: 1076 → 804 lines
- detection.ts: replace if/else filter chain with METRIC_FILTERS lookup
- detection.ts: deduplicate 5 signals.push() blocks with makeWowSignal()
- detection.ts: 584 → 510 lines
- validate.ts: replace inline array with SENTIMENT_DIVERGENCE_TYPES Set
- Share OrgWebsiteRow interface between generation and prompts
- Extract isEnabled() for tool filter readability
* feat(ai): shared investigation tools, actions pipeline, chat agent upgrade
- createInvestigationTools() shared across insights pipeline and chat agent
- Chat agent now has scrape, GSC, and GitHub tools (was scrape only)
- AgentContext gains organizationId for OAuth token lookups
- Add actions schema to insights (fix_goal, add_custom_event, etc.)
- Add actions column to analytics_insights table
- Chat agent prompt updated with investigation tool guidance
* feat(dashboard): render insight actions as clickable pills
Add InsightAction type and render action buttons on insight cards.
Actions include fix_goal, add_custom_event, create_funnel, etc.
Currently shows a toast on click — wire to actual mutations next.
* refactor(ai): unified toolkit, lint fixes, insights gets mutation tools
- createToolkit() assembles tools from capabilities (analytics,
investigation, mutations, memory, dashboard)
- Chat agent simplified from 12 imports to one createToolkit() call
- Insights pipeline gains mutation tools (create_annotation, fix_goal,
create_funnel) for executing actions during investigation
- Fix all lint errors (block statements, formatting)
* feat(insights): session investigation tools, mutation prompt, richer actions
- Add session query types to web_metrics description (interesting_sessions,
session_list, session_flow, session_pages)
- Investigation prompt now guides agent to use session-level queries for
user behavior analysis (step 6 in strategy)
- Agent told it can execute mutations directly (create_annotation with
confirmed=true, update_goal for target mismatches)
- Verified: agent generates specific add_custom_event actions with exact
event names, elements, and pages
* feat(insights): code_fix actions for cursor/claude-code integration
- Add code_fix action type with prompt, file_hint, error_message params
- Dashboard copies code_fix prompt to clipboard on click
- investigate_further also copies prompt to clipboard
- Agent generates cursor-ready prompts with exact files, changes, and
error context from GitHub search results
* refactor(insights): extract queryPeriodPair helper in enrichment
Deduplicate period-pair query boilerplate in enrichSegments and
enrichErrors. Both functions called queryFn 4 times with nearly
identical params differing only in date range. The helper creates
a curried function that handles the current/previous split.
* fix(insights): address PR review feedback
- OAuth token: scope preferUserId lookup to org membership (P1)
- OAuth token: add 45-min TTL to cached tokens (P2)
- Error detection: catch 0→N error spikes (P2)
- Prompts: use ?? instead of || for maxInsights (P2)
- Prompts: order dismissed patterns by recency (P2)
- Validate: fix truncation overflow (P2)
- Dashboard: handle clipboard write failures (P2)
* fix(insights): address remaining review comments
- Vitals enrichment: check previous-period sample size too (P2)
- OAuth cache: negative-result caching with 5-min TTL to avoid
repeated DB lookups when no account connected (P2)
---
.agents/skills/databuddy-internal/SKILL.md | 1 +
apps/api/package.json | 2 +-
apps/api/src/routes/agent.ts | 1 +
.../insights/_components/insight-card.tsx | 97 ++-
.../lib/insight-card-view-model.test.ts | 26 +-
.../insights/lib/insight-card-view-model.ts | 18 +-
.../components/integrations-settings.tsx | 109 +++-
apps/dashboard/lib/insight-types.ts | 47 +-
apps/dashboard/package.json | 2 +-
apps/insights/package.json | 2 +-
apps/insights/src/detection.test.ts | 94 ++-
apps/insights/src/detection.ts | 346 ++++++++---
apps/insights/src/enrichment.test.ts | 62 +-
apps/insights/src/enrichment.ts | 215 ++++---
apps/insights/src/generation.ts | 464 ++++-----------
apps/insights/src/prompts.ts | 289 +++++++++
apps/slack/src/slack/respond.test.ts | 179 +++---
apps/slack/src/slack/respond.ts | 552 ++++++++++--------
bun.lock | 22 +-
packages/ai/package.json | 6 +-
packages/ai/src/ai/agents/analytics.ts | 43 +-
packages/ai/src/ai/agents/types.ts | 1 +
packages/ai/src/ai/insights/validate.ts | 67 ++-
packages/ai/src/ai/prompts/analytics.ts | 5 +
.../ai/schemas/smart-insights-output.test.ts | 48 +-
.../src/ai/schemas/smart-insights-output.ts | 46 +-
packages/ai/src/ai/tools/github-tools.ts | 50 +-
.../ai/src/ai/tools/insights-agent-tools.ts | 2 +-
.../ai/src/ai/tools/investigation-tools.ts | 27 +
packages/ai/src/ai/tools/scrape-page.ts | 120 +++-
.../ai/src/ai/tools/search-console.test.ts | 206 +++++++
packages/ai/src/ai/tools/search-console.ts | 122 ++++
packages/ai/src/ai/tools/toolkit.ts | 82 +++
packages/ai/src/ai/tools/utils/index.ts | 1 +
packages/ai/src/ai/tools/utils/oauth-token.ts | 66 +++
.../db/src/clickhouse/sql-validation.test.ts | 42 +-
packages/db/src/clickhouse/sql-validation.ts | 138 ++++-
packages/db/src/drizzle/schema/analytics.ts | 15 +
packages/evals/package.json | 2 +-
packages/evals/ui/index.html | 22 +-
packages/nuxt/tsconfig.json | 4 +-
packages/rpc/src/routers/insights.ts | 23 +
packages/rpc/src/routers/integrations.ts | 76 ++-
43 files changed, 2590 insertions(+), 1152 deletions(-)
create mode 100644 apps/insights/src/prompts.ts
create mode 100644 packages/ai/src/ai/tools/investigation-tools.ts
create mode 100644 packages/ai/src/ai/tools/search-console.test.ts
create mode 100644 packages/ai/src/ai/tools/search-console.ts
create mode 100644 packages/ai/src/ai/tools/toolkit.ts
create mode 100644 packages/ai/src/ai/tools/utils/oauth-token.ts
diff --git a/.agents/skills/databuddy-internal/SKILL.md b/.agents/skills/databuddy-internal/SKILL.md
index 38dc9779f..121a2fec4 100644
--- a/.agents/skills/databuddy-internal/SKILL.md
+++ b/.agents/skills/databuddy-internal/SKILL.md
@@ -21,6 +21,7 @@ Keep additions **minimal**: one bullet, a new `rg` hint, or a routing note—eno
- Never use production/customer data as tests, fixtures, snapshots, examples, or copied output. Tests must use placeholders/mocks only (example.com, example IDs). If production ClickHouse is queried for investigation, summarize anonymized aggregates and do not paste customer domains, client IDs, emails, or other identifiers into code or responses.
- `apps/dashboard`: Next.js app on port `3000` (per-website **agent** chat: `@ai-sdk/react` `useChat` via `contexts/chat-context.tsx` — not the separate `chat-sdk` package; overlapping sends while streaming are queued client-side to mirror a “queue latest” strategy.)
- Dashboard Playwright webServer commands run under CI PATH from setup-bun; avoid `bash -lc` because login shells can drop Bun from PATH. Build dist-only workspace packages such as `@databuddy/sdk` and `@databuddy/devtools` before starting the API/dashboard. Client `NEXT_PUBLIC_*` flags must use direct env access so Next can inline them. `readBooleanEnv` only treats the literal string `"true"` as enabled, so CI E2E booleans must use `"true"`/`"false"`, not `"1"`/`"0"`.
+- Local E2E dashboard smokes that need `/api/test/e2e/*` should start the API/dashboard directly (or through Playwright's webServer command), not via `bun run dev:dashboard`; Turbo runs in strict env mode and drops `DATABUDDY_E2E_MODE`/`DATABUDDY_E2E_TEST_KEY` unless they are added to `turbo.json` `globalEnv`.
- Dashboard Playwright public/demo analytics specs call API `/v1/query` anonymously from the browser; keep `DATABUDDY_E2E_MODE` query behavior isolated from production rate limits so CI retries do not exhaust `anon:unknown`.
- `apps/api`: Elysia API on port `3001`
- `apps/slack`: Slack agent adapter; Slack installs must resolve through org-scoped DB integration records, not a single env bot token/default website. Agent calls must use an encrypted per-integration Databuddy API key secret as a normal bearer token, never a global internal secret.
diff --git a/apps/api/package.json b/apps/api/package.json
index 7a489d0fd..ae2578b9e 100644
--- a/apps/api/package.json
+++ b/apps/api/package.json
@@ -34,7 +34,7 @@
"@orpc/openapi": "^1.14.0",
"@orpc/server": "^1.14.0",
"@orpc/zod": "^1.14.0",
- "ai": "^6.0.154",
+ "ai": "^6.0.188",
"autumn-js": "catalog:",
"bullmq": "^5.66.5",
"dayjs": "^1.11.19",
diff --git a/apps/api/src/routes/agent.ts b/apps/api/src/routes/agent.ts
index 999c03d23..5e6082305 100644
--- a/apps/api/src/routes/agent.ts
+++ b/apps/api/src/routes/agent.ts
@@ -804,6 +804,7 @@ export const agent = new Elysia({ prefix: "/v1/agent" })
const config = createAgentConfig(
{
userId,
+ organizationId: organizationId ?? undefined,
websiteId: body.websiteId,
websiteDomain: domain,
timezone,
diff --git a/apps/dashboard/app/(main)/insights/_components/insight-card.tsx b/apps/dashboard/app/(main)/insights/_components/insight-card.tsx
index 784de8a5d..02d29c194 100644
--- a/apps/dashboard/app/(main)/insights/_components/insight-card.tsx
+++ b/apps/dashboard/app/(main)/insights/_components/insight-card.tsx
@@ -22,7 +22,7 @@ import {
changePercentChipClassName,
formatSignedChangePercent,
} from "@/lib/insight-signal-key";
-import type { Insight, InsightType } from "@/lib/insight-types";
+import type { Insight, InsightAction, InsightType } from "@/lib/insight-types";
import { cn } from "@/lib/utils";
import {
ArrowRightIcon,
@@ -375,6 +375,52 @@ function InsightCardPanel({
);
}
+const ACTION_ICONS: Record
+ Root cause +
++ {view.rootCause} +
++ Evidence +
+{view.nextStep}
+ {view.actions.length > 0 && ( +- Evidence + Metrics
-${escapeHtml(c.response || "No response captured.")}${escapeHtml(c.response || "No response captured.")}${escapeHtml(c.response || "No response captured.")}What counts as an event? A pageview, custom event, error, or Web Vital measurement. Feature flag - evaluations are free and don't count toward your quota. + evaluations and uptime checks are free and don't count toward your + quota.
Agent credits power Databunny, the AI assistant that analyzes your data, answers questions, and surfaces insights automatically.
-Overage is tiered. Lower rates apply as volume increases.
++ Unlimited seats & sites.{" "} + Team members, websites, and API access are unlimited on every plan. + Overage is tiered with lower rates as volume increases. +
); diff --git a/apps/docs/app/(home)/pricing/page.tsx b/apps/docs/app/(home)/pricing/page.tsx index 00e26297f..b50383a1e 100644 --- a/apps/docs/app/(home)/pricing/page.tsx +++ b/apps/docs/app/(home)/pricing/page.tsx @@ -47,9 +47,9 @@ export default function PricingPage() { Every feature, every plan.- Analytics, errors, vitals, and flags included at every tier. Pick a - plan based on volume, not features. Fair tiered overage, and you - only pay for what you use. + Analytics, uptime monitoring, link management, error tracking, web + vitals, feature flags, and more included at every tier. Pick a plan + based on volume, not features.
diff --git a/apps/docs/components/footer.tsx b/apps/docs/components/footer.tsx index b8cf1cf7d..f31c92b1e 100644 --- a/apps/docs/components/footer.tsx +++ b/apps/docs/components/footer.tsx @@ -1,13 +1,12 @@ "use client"; -import { SiDiscord, SiX } from "@icons-pack/react-simple-icons"; import { Button } from "@databuddy/ui"; import { EnvelopeIcon } from "@databuddy/ui/icons"; +import { SiDiscord, SiX } from "@icons-pack/react-simple-icons"; import Image from "next/image"; import Link from "next/link"; import { CCPAIcon } from "./icons/ccpa"; import { GDPRIcon } from "./icons/gdpr"; -import { Wordmark } from "./landing/wordmark"; import { LogoContent } from "./logo"; import { NavLink } from "./nav-link"; import { NewsletterForm } from "./newsletter-form"; @@ -281,7 +280,6 @@ export function Footer() {${escapeHtml(c.response || "No response captured.")}