Skip to content

feat(slack-bot): bill slack-initiated posthog code tasks under AI credits#59040

Merged
VojtechBartos merged 14 commits into
masterfrom
vojtab/slack-bot-ai-credits-billing
May 27, 2026
Merged

feat(slack-bot): bill slack-initiated posthog code tasks under AI credits#59040
VojtechBartos merged 14 commits into
masterfrom
vojtab/slack-bot-ai-credits-billing

Conversation

@VojtechBartos
Copy link
Copy Markdown
Member

@VojtechBartos VojtechBartos commented May 19, 2026

Problem

PostHog Code Slack-bot task processing has no metering or quota attribution today. The agent the bot runs in the sandbox calls the LLM gateway as posthog_code, so slack-initiated traffic rides desktop's plan-tier path instead of being usage-billed.

Wire slack-initiated runs into the existing PHAI billing pipeline: $ai_billable$ai_generation → daily usage_report.py aggregator → QuotaResource.AI_CREDITS. Pairs with PostHog/code#2225, which switches the agent to send slack_app as the gateway product based on the task's origin_product.

Changes

  • Gateway: new billable flag on ProductConfig. slack_app (agent) and slack_app_routing (the haiku classifier, renamed from slack-posthog-code with aliases for back-compat) are marked billable. The PostHog callback tags their $ai_generation events with $ai_billable=true.
  • Django / Temporal: pre-flight is_team_limited check on both create_posthog_code_task_for_repo_activity and forward_posthog_code_followup_activity. Posts a friendly denial in-thread and bails before any side effects.
  • Gateway throttle: new BillableCreditThrottle keyed on ProductConfig.billable. ZSCOREs the team's API token against the same Redis set Django writes to. Returns 429 to over-quota teams. Closes the cross-surface gap — PostHog Code desktop replies to slack-origin tasks bypass the Temporal pre-flight but cannot bypass the gateway. team_api_token is plumbed through AuthenticatedUser to make this possible. Fails open if Redis is unreachable.

How did you test this code?

Automated

  • uv run pytest services/llm-gateway/tests/test_product_config.py services/llm-gateway/tests/callbacks/test_posthog.py services/llm-gateway/tests/test_billable_credits_throttle.py — covers product registration, $ai_billable tagging, alias resolution, and the 6 throttle branches (non-billable, not limited, limit expired, limit active, no Redis, no team token).
  • hogli test products/slack_app/backend/tests/test_followup_forwarding.py — quota-blocked task creation and quota-blocked follow-up.

Manual end-to-end against local dev

  1. Triggered @posthog in Slack and confirmed the gateway emits two $ai_generation events tagged $ai_billable=true — one for slack_app (the agent) and one for slack_app_routing (the classifier). Verified via HogQL on team 2:

    SELECT properties.$ai_product, count()
    FROM events
    WHERE event = '$ai_generation'
      AND properties.$ai_billable = true
      AND timestamp > now() - INTERVAL 5 MINUTE
    GROUP BY properties.$ai_product
  2. Simulated an over-quota team by writing directly to the Redis set the daily aggregator populates:

    redis-cli ZADD '@posthog/quota-limits/ai_credits' $(($(date +%s) + 3600)) '<team_api_token>'

    Confirmed:

    • New @posthog mention short-circuits in create_posthog_code_task_for_repo_activity — no sandbox provisioned.
    • Slack thread reply short-circuits in forward_posthog_code_followup_activity — no message forwarded.
  3. Removed the limit (ZREM) and re-mentioned — back to normal.

Showcase

Screenshot 2026-05-21 at 12 34 25 Screenshot 2026-05-21 at 12 34 41 Screenshot 2026-05-21 at 12 50 08

Deployment order

Deploy this PR first, then PostHog/code#2225. The agent in #2225 starts calling the gateway as slack_app, which doesn't exist as a registered product until this PR lands. Reversed, slack-initiated mentions would 400.

Publish to changelog?

no

Docs update

No docs touched.

🤖 Agent context

Agent-authored with Claude Code, supervised. billable lives on ProductConfig (one place to flip per product) and the gateway throttle keys off it (forward-compat for any future billable product). Pre-flight in Temporal stays in place as belt-and-suspenders — it saves sandbox provisioning when already over quota; the throttle catches anything that gets past it.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 21, 2026

🎭 Playwright didn't run on this PR — your changes touch code that could affect E2E behavior, but Playwright is opt-in via label now to keep CI cost down.

Add the run-playwright label if you want an E2E sweep before merging — CI will pick it up automatically.

Most PRs don't need this. Real regressions still get caught on master and fix-forward.

@VojtechBartos VojtechBartos marked this pull request as ready for review May 21, 2026 12:27
@VojtechBartos VojtechBartos requested a review from a team May 21, 2026 12:27
@assign-reviewers-posthog assign-reviewers-posthog Bot requested a review from a team May 21, 2026 12:27
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 21, 2026

Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
services/llm-gateway/tests/callbacks/test_posthog.py:350-374
The failure-path test only covers `slack_app`, while the success-path equivalent is parametrized over both `slack_app` and `slack_app_routing`. The team rule is to always prefer parametrized tests, and both products have `billable=True`, so both should be exercised here.

```suggestion
    @pytest.mark.asyncio
    @pytest.mark.parametrize("product", ["slack_app", "slack_app_routing"])
    async def test_on_failure_marks_slack_products_billable(
        self,
        callback: PostHogCallback,
        auth_user: AuthenticatedUser,
        mock_posthog_client: tuple,
        product: str,
    ) -> None:
        _, mock_client = mock_posthog_client
        kwargs = {
            "standard_logging_object": {
                "model": "claude-sonnet-4-6",
                "custom_llm_provider": "anthropic",
                "error_str": "boom",
            },
            "litellm_params": {},
        }

        with (
            patch("llm_gateway.callbacks.posthog.get_auth_user", return_value=auth_user),
            patch("llm_gateway.callbacks.posthog.get_product", return_value=product),
        ):
            await callback._on_failure(kwargs, None, 0.0, 1.0, end_user_id=None)

        props = mock_client.capture.call_args.kwargs["properties"]
        assert props["$ai_billable"] is True
```

### Issue 2 of 2
services/llm-gateway/src/llm_gateway/rate_limiting/billable_credits_throttle.py:55-66
`self._now()` is called twice: once for the limit check and again for `retry_after`. If real-world clock time advances between calls, the computed `retry_after` will be slightly shorter than intended, and in a pathological edge case where `score` is within a fraction of a second of `now`, the first call could evaluate `score > now` (block) while the second call makes `score - now` negative (before `max` saves it). Capturing the timestamp once is safer.

```suggestion
        score = await self._redis.zscore(_AI_CREDITS_LIMIT_KEY, context.user.team_api_token)
        now = self._now()
        if score is None or score <= now:
            return ThrottleResult.allow()

        return ThrottleResult.deny(
            detail=(
                "Your team has used its monthly PostHog AI credits. "
                "Top up at https://us.posthog.com/organization/billing to continue."
            ),
            scope=self.scope,
            retry_after=max(int(score - now), 1),
        )
```

Reviews (1): Last reviewed commit: "feat(llm-gateway): gate billable product..." | Re-trigger Greptile

Comment on lines +350 to +374
@pytest.mark.asyncio
async def test_on_failure_marks_slack_app_billable(
self,
callback: PostHogCallback,
auth_user: AuthenticatedUser,
mock_posthog_client: tuple,
) -> None:
_, mock_client = mock_posthog_client
kwargs = {
"standard_logging_object": {
"model": "claude-sonnet-4-6",
"custom_llm_provider": "anthropic",
"error_str": "boom",
},
"litellm_params": {},
}

with (
patch("llm_gateway.callbacks.posthog.get_auth_user", return_value=auth_user),
patch("llm_gateway.callbacks.posthog.get_product", return_value="slack_app"),
):
await callback._on_failure(kwargs, None, 0.0, 1.0, end_user_id=None)

props = mock_client.capture.call_args.kwargs["properties"]
assert props["$ai_billable"] is True
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The failure-path test only covers slack_app, while the success-path equivalent is parametrized over both slack_app and slack_app_routing. The team rule is to always prefer parametrized tests, and both products have billable=True, so both should be exercised here.

Suggested change
@pytest.mark.asyncio
async def test_on_failure_marks_slack_app_billable(
self,
callback: PostHogCallback,
auth_user: AuthenticatedUser,
mock_posthog_client: tuple,
) -> None:
_, mock_client = mock_posthog_client
kwargs = {
"standard_logging_object": {
"model": "claude-sonnet-4-6",
"custom_llm_provider": "anthropic",
"error_str": "boom",
},
"litellm_params": {},
}
with (
patch("llm_gateway.callbacks.posthog.get_auth_user", return_value=auth_user),
patch("llm_gateway.callbacks.posthog.get_product", return_value="slack_app"),
):
await callback._on_failure(kwargs, None, 0.0, 1.0, end_user_id=None)
props = mock_client.capture.call_args.kwargs["properties"]
assert props["$ai_billable"] is True
@pytest.mark.asyncio
@pytest.mark.parametrize("product", ["slack_app", "slack_app_routing"])
async def test_on_failure_marks_slack_products_billable(
self,
callback: PostHogCallback,
auth_user: AuthenticatedUser,
mock_posthog_client: tuple,
product: str,
) -> None:
_, mock_client = mock_posthog_client
kwargs = {
"standard_logging_object": {
"model": "claude-sonnet-4-6",
"custom_llm_provider": "anthropic",
"error_str": "boom",
},
"litellm_params": {},
}
with (
patch("llm_gateway.callbacks.posthog.get_auth_user", return_value=auth_user),
patch("llm_gateway.callbacks.posthog.get_product", return_value=product),
):
await callback._on_failure(kwargs, None, 0.0, 1.0, end_user_id=None)
props = mock_client.capture.call_args.kwargs["properties"]
assert props["$ai_billable"] is True
Prompt To Fix With AI
This is a comment left during a code review.
Path: services/llm-gateway/tests/callbacks/test_posthog.py
Line: 350-374

Comment:
The failure-path test only covers `slack_app`, while the success-path equivalent is parametrized over both `slack_app` and `slack_app_routing`. The team rule is to always prefer parametrized tests, and both products have `billable=True`, so both should be exercised here.

```suggestion
    @pytest.mark.asyncio
    @pytest.mark.parametrize("product", ["slack_app", "slack_app_routing"])
    async def test_on_failure_marks_slack_products_billable(
        self,
        callback: PostHogCallback,
        auth_user: AuthenticatedUser,
        mock_posthog_client: tuple,
        product: str,
    ) -> None:
        _, mock_client = mock_posthog_client
        kwargs = {
            "standard_logging_object": {
                "model": "claude-sonnet-4-6",
                "custom_llm_provider": "anthropic",
                "error_str": "boom",
            },
            "litellm_params": {},
        }

        with (
            patch("llm_gateway.callbacks.posthog.get_auth_user", return_value=auth_user),
            patch("llm_gateway.callbacks.posthog.get_product", return_value=product),
        ):
            await callback._on_failure(kwargs, None, 0.0, 1.0, end_user_id=None)

        props = mock_client.capture.call_args.kwargs["properties"]
        assert props["$ai_billable"] is True
```

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread services/llm-gateway/src/llm_gateway/rate_limiting/billable_credits_throttle.py Outdated
@veria-ai
Copy link
Copy Markdown

veria-ai Bot commented May 21, 2026

PR overview

Two security issues remain open, both allowing over-quota callers to bypass AI-credit throttling for billable LLM gateway products such as Slack app routing. The bypasses are reachable through supported authentication paths or authorization failures, so attacker action is confirmed but the impact is limited to quota and billing enforcement rather than broader data access or code execution. One issue has already been addressed, indicating the PR is moving in the right direction but still needs quota enforcement fixes before it is secure.

Open issues (2)

Fixed/addressed: 1 · PR risk: 6/10

Comment thread posthog/temporal/ai/posthog_code_slack_mention.py
Copy link
Copy Markdown
Contributor

@joshsny joshsny left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

django & the gateway don't share a redis instance, so you'll need to go a different route here :)

class BillableCreditThrottle(Throttle):
"""Gate billable-product LLM calls on the team's AI credits balance.

Reads the same Redis sorted set Django populates via
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are different redis instances, so this approach won't work - instead there should be an endpoint already on the gateway that posthog code uses to check if folks are limited, we should use the same endpoint (we might need to make it more general, should be something like /limits that should expose a users limits based on their OAuth token, or if a personal api key is provided they can get limits based on the user they specify, so long as they have access to that user)

@VojtechBartos VojtechBartos force-pushed the vojtab/slack-bot-ai-credits-billing branch from dc3e14e to bc7ff29 Compare May 25, 2026 08:34
@VojtechBartos
Copy link
Copy Markdown
Member Author

@joshsny updated

• new django endpoint GET /api/projects/<team_id>/quota_limits/
• gateway resolver now passes team_id explicitly and caches per team in its own redis
• moved the slack-bot AI credits gate to 2 places: webhook (before start_workflow, so no temporal exec
for over-quota teams) + first patched activity in workflow run() for defense in depth
• existing in-activity gates left as-is, they catch the race + keep direct-activity unit tests passing
• also fixes the classifier 429 -> wrong repo-picker thread thing i saw while testing

headers={"Authorization": auth_header},
timeout=2.0,
)
if resp.status_code >= 400:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium: Quota bypass on authorization failures

/api/projects/{team_id}/quota_limits/ requires project:read, but LLM gateway authentication only requires llm_gateway:read. An over-quota user can call a billable API-key product such as slack_app_routing with a normal llm_gateway:read key; Django returns 403 here, this branch caches limited=False, and BillableCreditThrottle allows the LLM call. Make the quota endpoint/resolver compatible with the gateway scope, or fail closed for authorization failures instead of treating them as unlimited.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 25, 2026

Size Change: 0 B

Total Size: 80.2 MB

ℹ️ View Unchanged
Filename Size
frontend/dist-report/decompression-worker/src/scenes/session-recordings/player/snapshot-processing/decompressionWorker 2.85 kB
frontend/dist-report/exporter/_chunks/chunk 8.39 MB
frontend/dist-report/exporter/_parent/products/actions/frontend/pages/Action 24.9 kB
frontend/dist-report/exporter/_parent/products/actions/frontend/pages/Actions 1.3 kB
frontend/dist-report/exporter/_parent/products/business_knowledge/frontend/scenes/BusinessKnowledgeScene 19 kB
frontend/dist-report/exporter/_parent/products/conversations/frontend/components/Assignee/CyclotronJobInputAssignee 1.64 kB
frontend/dist-report/exporter/_parent/products/conversations/frontend/components/SlaBusinessHours/CyclotronJobInputBusinessHours 3.02 kB
frontend/dist-report/exporter/_parent/products/conversations/frontend/components/TicketTags/CyclotronJobInputTicketTags 1.02 kB
frontend/dist-report/exporter/_parent/products/conversations/frontend/scenes/settings/SupportSettingsScene 1.78 kB
frontend/dist-report/exporter/_parent/products/conversations/frontend/scenes/ticket/SupportTicketScene 33.9 kB
frontend/dist-report/exporter/_parent/products/conversations/frontend/scenes/tickets/SupportTicketsScene 1.04 kB
frontend/dist-report/exporter/_parent/products/customer_analytics/frontend/CustomerAnalyticsScene 36.9 kB
frontend/dist-report/exporter/_parent/products/customer_analytics/frontend/scenes/CustomerAnalyticsConfigurationScene/CustomerAnalyticsConfigurationScene 2.61 kB
frontend/dist-report/exporter/_parent/products/customer_analytics/frontend/scenes/CustomerJourneyBuilderScene/CustomerJourneyBuilderScene 2.15 kB
frontend/dist-report/exporter/_parent/products/customer_analytics/frontend/scenes/CustomerJourneyTemplatesScene/CustomerJourneyTemplatesScene 7.83 kB
frontend/dist-report/exporter/_parent/products/data_warehouse/DataWarehouseScene 46.8 kB
frontend/dist-report/exporter/_parent/products/data_warehouse/frontend/scenes/NewSourceScene/NewSourceScene 1.08 kB
frontend/dist-report/exporter/_parent/products/data_warehouse/frontend/scenes/SchemaScene/SchemaScene 24 kB
frontend/dist-report/exporter/_parent/products/data_warehouse/frontend/scenes/SourceScene/SourceScene 1.03 kB
frontend/dist-report/exporter/_parent/products/data_warehouse/frontend/scenes/SourcesScene/SourcesScene 6.27 kB
frontend/dist-report/exporter/_parent/products/deployments/frontend/Deployment 4.02 kB
frontend/dist-report/exporter/_parent/products/deployments/frontend/DeploymentProject 5.54 kB
frontend/dist-report/exporter/_parent/products/deployments/frontend/Deployments 9.28 kB
frontend/dist-report/exporter/_parent/products/early_access_features/frontend/EarlyAccessFeature 991 B
frontend/dist-report/exporter/_parent/products/early_access_features/frontend/EarlyAccessFeatures 3.21 kB
frontend/dist-report/exporter/_parent/products/endpoints/frontend/EndpointScene 40.6 kB
frontend/dist-report/exporter/_parent/products/endpoints/frontend/EndpointsScene 24.5 kB
frontend/dist-report/exporter/_parent/products/error_tracking/frontend/scenes/ErrorTrackingFingerprintsScene/ErrorTrackingIssueFingerprintsScene 7.37 kB
frontend/dist-report/exporter/_parent/products/error_tracking/frontend/scenes/ErrorTrackingIssueScene/ErrorTrackingIssueScene 102 kB
frontend/dist-report/exporter/_parent/products/error_tracking/frontend/scenes/ErrorTrackingScene/ErrorTrackingScene 27.1 kB
frontend/dist-report/exporter/_parent/products/feature_flags/frontend/FeatureFlagTemplatesScene 7.35 kB
frontend/dist-report/exporter/_parent/products/games/368Hedgehogs/368Hedgehogs 5.58 kB
frontend/dist-report/exporter/_parent/products/games/FlappyHog/FlappyHog 6.09 kB
frontend/dist-report/exporter/_parent/products/legal_documents/frontend/scenes/LegalDocumentNewScene 59.7 kB
frontend/dist-report/exporter/_parent/products/legal_documents/frontend/scenes/LegalDocumentsScene 5.28 kB
frontend/dist-report/exporter/_parent/products/links/frontend/LinkScene 25.2 kB
frontend/dist-report/exporter/_parent/products/links/frontend/LinksScene 4.51 kB
frontend/dist-report/exporter/_parent/products/live_debugger/frontend/LiveDebugger 19.4 kB
frontend/dist-report/exporter/_parent/products/llm_analytics/frontend/clusters/LLMAnalyticsClusterScene 21.6 kB
frontend/dist-report/exporter/_parent/products/llm_analytics/frontend/clusters/LLMAnalyticsClustersScene 55 kB
frontend/dist-report/exporter/_parent/products/llm_analytics/frontend/datasets/LLMAnalyticsDatasetScene 20.9 kB
frontend/dist-report/exporter/_parent/products/llm_analytics/frontend/datasets/LLMAnalyticsDatasetsScene 3.6 kB
frontend/dist-report/exporter/_parent/products/llm_analytics/frontend/evaluations/EvaluationTemplates 881 B
frontend/dist-report/exporter/_parent/products/llm_analytics/frontend/evaluations/LLMAnalyticsEvaluation 59.8 kB
frontend/dist-report/exporter/_parent/products/llm_analytics/frontend/evaluations/LLMAnalyticsEvaluationsScene 28.1 kB
frontend/dist-report/exporter/_parent/products/llm_analytics/frontend/LLMAnalyticsScene 118 kB
frontend/dist-report/exporter/_parent/products/llm_analytics/frontend/LLMAnalyticsSessionScene 16.7 kB
frontend/dist-report/exporter/_parent/products/llm_analytics/frontend/LLMAnalyticsTraceScene 130 kB
frontend/dist-report/exporter/_parent/products/llm_analytics/frontend/LLMAnalyticsUsers 832 B
frontend/dist-report/exporter/_parent/products/llm_analytics/frontend/LLMASessionFeedbackDisplay 5.15 kB
frontend/dist-report/exporter/_parent/products/llm_analytics/frontend/playground/LLMAnalyticsPlaygroundScene 37.7 kB
frontend/dist-report/exporter/_parent/products/llm_analytics/frontend/prompts/LLMPromptScene 29.1 kB
frontend/dist-report/exporter/_parent/products/llm_analytics/frontend/prompts/LLMPromptsScene 4.79 kB
frontend/dist-report/exporter/_parent/products/llm_analytics/frontend/skills/LLMSkillScene 895 B
frontend/dist-report/exporter/_parent/products/llm_analytics/frontend/skills/LLMSkillsScene 912 B
frontend/dist-report/exporter/_parent/products/llm_analytics/frontend/tags/LLMAnalyticsTag 27.3 kB
frontend/dist-report/exporter/_parent/products/llm_analytics/frontend/tags/LLMAnalyticsTagsScene 7.26 kB
frontend/dist-report/exporter/_parent/products/logs/frontend/LogsScene 17.8 kB
frontend/dist-report/exporter/_parent/products/logs/frontend/scenes/LogsAlertDetailScene/LogsAlertDetailScene 17.3 kB
frontend/dist-report/exporter/_parent/products/logs/frontend/scenes/LogsSamplingDetailScene/LogsSamplingDetailScene 5.27 kB
frontend/dist-report/exporter/_parent/products/logs/frontend/scenes/LogsSamplingNewScene/LogsSamplingNewScene 2.22 kB
frontend/dist-report/exporter/_parent/products/managed_migrations/frontend/ManagedMigration 14.9 kB
frontend/dist-report/exporter/_parent/products/mcp_analytics/frontend/MCPAnalyticsScene 40.2 kB
frontend/dist-report/exporter/_parent/products/mcp_analytics/frontend/MCPAnalyticsToolDetail 18.5 kB
frontend/dist-report/exporter/_parent/products/metrics/frontend/MetricsScene 1.15 kB
frontend/dist-report/exporter/_parent/products/product_analytics/frontend/insights/trends/StickinessBarChart/StickinessBarChart 3.27 kB
frontend/dist-report/exporter/_parent/products/product_analytics/frontend/insights/trends/StickinessLineChart/StickinessLineChart 3.11 kB
frontend/dist-report/exporter/_parent/products/product_analytics/frontend/insights/trends/TrendsBarChart/TrendsBarChart 7.12 kB
frontend/dist-report/exporter/_parent/products/product_analytics/frontend/insights/trends/TrendsLifecycleChart/TrendsLifecycleChart 4.06 kB
frontend/dist-report/exporter/_parent/products/product_analytics/frontend/insights/trends/TrendsLineChart/TrendsLineChart 4.57 kB
frontend/dist-report/exporter/_parent/products/product_analytics/frontend/insights/trends/TrendsPieChart/TrendsPieChart 4.31 kB
frontend/dist-report/exporter/_parent/products/replay_vision/frontend/replay_scanners/ReplayScanner 20.5 kB
frontend/dist-report/exporter/_parent/products/replay_vision/frontend/replay_scanners/ReplayScannersScene 12.4 kB
frontend/dist-report/exporter/_parent/products/revenue_analytics/frontend/RevenueAnalyticsScene 26.5 kB
frontend/dist-report/exporter/_parent/products/session_summaries/frontend/SessionGroupSummariesTable 5.02 kB
frontend/dist-report/exporter/_parent/products/session_summaries/frontend/SessionGroupSummaryScene 19.2 kB
frontend/dist-report/exporter/_parent/products/tasks/frontend/TaskDetailScene 23.5 kB
frontend/dist-report/exporter/_parent/products/tasks/frontend/TaskTracker 14.6 kB
frontend/dist-report/exporter/_parent/products/tracing/frontend/TracingScene 54.1 kB
frontend/dist-report/exporter/_parent/products/user_interviews/frontend/UserInterview 9.28 kB
frontend/dist-report/exporter/_parent/products/user_interviews/frontend/UserInterviewResponse 5.64 kB
frontend/dist-report/exporter/_parent/products/user_interviews/frontend/UserInterviews 6.04 kB
frontend/dist-report/exporter/_parent/products/visual_review/frontend/scenes/VisualReviewIndexScene 2.52 kB
frontend/dist-report/exporter/_parent/products/visual_review/frontend/scenes/VisualReviewRunScene 44.6 kB
frontend/dist-report/exporter/_parent/products/visual_review/frontend/scenes/VisualReviewRunsScene 7.29 kB
frontend/dist-report/exporter/_parent/products/visual_review/frontend/scenes/VisualReviewSettingsScene 11.1 kB
frontend/dist-report/exporter/_parent/products/visual_review/frontend/scenes/VisualReviewSnapshotHistoryScene 13.9 kB
frontend/dist-report/exporter/_parent/products/visual_review/frontend/scenes/VisualReviewSnapshotOverviewScene 19.5 kB
frontend/dist-report/exporter/_parent/products/workflows/frontend/TemplateLibrary/MessageTemplate 16.6 kB
frontend/dist-report/exporter/_parent/products/workflows/frontend/Workflows/WorkflowScene 111 kB
frontend/dist-report/exporter/_parent/products/workflows/frontend/WorkflowsScene 60.1 kB
frontend/dist-report/exporter/src/exporter/exporter 19.1 kB
frontend/dist-report/exporter/src/exporter/scenes/ExporterDashboardScene 1.99 kB
frontend/dist-report/exporter/src/exporter/scenes/ExporterHeatmapScene 19.6 kB
frontend/dist-report/exporter/src/exporter/scenes/ExporterInsightScene 2.98 kB
frontend/dist-report/exporter/src/exporter/scenes/ExporterInterviewScene 310 kB
frontend/dist-report/exporter/src/exporter/scenes/ExporterNotebookScene 2.71 MB
frontend/dist-report/exporter/src/exporter/scenes/ExporterRecordingScene 1.1 kB
frontend/dist-report/exporter/src/exporterSharedChunkAnchors 1.19 kB
frontend/dist-report/exporter/src/lib/components/Cards/TextCard/TextCardMarkdownEditor 11.3 kB
frontend/dist-report/exporter/src/lib/components/MonacoDiffEditor 471 B
frontend/dist-report/exporter/src/lib/lemon-ui/LemonMarkdown/MermaidDiagram 2.22 kB
frontend/dist-report/exporter/src/lib/lemon-ui/LemonTextArea/LemonTextAreaMarkdown 808 B
frontend/dist-report/exporter/src/lib/lemon-ui/Link/Link 359 B
frontend/dist-report/exporter/src/lib/monaco/CodeEditorInline 798 B
frontend/dist-report/exporter/src/lib/monaco/vimMode 211 kB
frontend/dist-report/exporter/src/lib/ui/Button/ButtonPrimitives 422 B
frontend/dist-report/exporter/src/queries/nodes/WebVitals/WebVitals 7.48 kB
frontend/dist-report/exporter/src/queries/nodes/WebVitals/WebVitalsPathBreakdown 4.05 kB
frontend/dist-report/exporter/src/queries/schema 732 kB
frontend/dist-report/exporter/src/scenes/approvals/changeRequestsLogic 850 B
frontend/dist-report/exporter/src/scenes/authentication/passkeyLogic 790 B
frontend/dist-report/exporter/src/scenes/data-pipelines/event-filtering/EventFilterScene 22.2 kB
frontend/dist-report/exporter/src/scenes/data-pipelines/TransformationsScene 6.51 kB
frontend/dist-report/exporter/src/scenes/insights/views/BoxPlot/BoxPlot 5.35 kB
frontend/dist-report/exporter/src/scenes/insights/views/CalendarHeatMap/CalendarHeatMap 8.81 kB
frontend/dist-report/exporter/src/scenes/insights/views/RegionMap/RegionMap 29.7 kB
frontend/dist-report/exporter/src/scenes/insights/views/WorldMap/WorldMap 1.04 MB
frontend/dist-report/exporter/src/scenes/models/ModelsScene 19 kB
frontend/dist-report/exporter/src/scenes/models/NodeDetailScene 17 kB
frontend/dist-report/monaco-editor-worker/src/lib/monaco/workers/monacoEditorWorker 288 kB
frontend/dist-report/monaco-json-worker/src/lib/monaco/workers/monacoJsonWorker 419 kB
frontend/dist-report/monaco-typescript-worker/src/lib/monaco/workers/monacoTsWorker 7.02 MB
frontend/dist-report/posthog-app/_chunks/chunk 8.58 MB
frontend/dist-report/posthog-app/_parent/products/actions/frontend/pages/Action 25.1 kB
frontend/dist-report/posthog-app/_parent/products/actions/frontend/pages/Actions 1.36 kB
frontend/dist-report/posthog-app/_parent/products/business_knowledge/frontend/scenes/BusinessKnowledgeScene 19 kB
frontend/dist-report/posthog-app/_parent/products/conversations/frontend/components/Assignee/CyclotronJobInputAssignee 1.67 kB
frontend/dist-report/posthog-app/_parent/products/conversations/frontend/components/SlaBusinessHours/CyclotronJobInputBusinessHours 3.06 kB
frontend/dist-report/posthog-app/_parent/products/conversations/frontend/components/TicketTags/CyclotronJobInputTicketTags 1.06 kB
frontend/dist-report/posthog-app/_parent/products/conversations/frontend/scenes/settings/SupportSettingsScene 1.82 kB
frontend/dist-report/posthog-app/_parent/products/conversations/frontend/scenes/ticket/SupportTicketScene 26.6 kB
frontend/dist-report/posthog-app/_parent/products/conversations/frontend/scenes/tickets/SupportTicketsScene 1.07 kB
frontend/dist-report/posthog-app/_parent/products/customer_analytics/frontend/CustomerAnalyticsScene 35.7 kB
frontend/dist-report/posthog-app/_parent/products/customer_analytics/frontend/scenes/CustomerAnalyticsConfigurationScene/CustomerAnalyticsConfigurationScene 2.65 kB
frontend/dist-report/posthog-app/_parent/products/customer_analytics/frontend/scenes/CustomerJourneyBuilderScene/CustomerJourneyBuilderScene 2.18 kB
frontend/dist-report/posthog-app/_parent/products/customer_analytics/frontend/scenes/CustomerJourneyTemplatesScene/CustomerJourneyTemplatesScene 7.86 kB
frontend/dist-report/posthog-app/_parent/products/data_warehouse/DataWarehouseScene 1.78 kB
frontend/dist-report/posthog-app/_parent/products/data_warehouse/frontend/scenes/NewSourceScene/NewSourceScene 1.15 kB
frontend/dist-report/posthog-app/_parent/products/data_warehouse/frontend/scenes/SchemaScene/SchemaScene 24.1 kB
frontend/dist-report/posthog-app/_parent/products/data_warehouse/frontend/scenes/SourceScene/SourceScene 1.06 kB
frontend/dist-report/posthog-app/_parent/products/data_warehouse/frontend/scenes/SourcesScene/SourcesScene 6.31 kB
frontend/dist-report/posthog-app/_parent/products/deployments/frontend/Deployment 4.05 kB
frontend/dist-report/posthog-app/_parent/products/deployments/frontend/DeploymentProject 5.58 kB
frontend/dist-report/posthog-app/_parent/products/deployments/frontend/Deployments 9.31 kB
frontend/dist-report/posthog-app/_parent/products/early_access_features/frontend/EarlyAccessFeature 1.16 kB
frontend/dist-report/posthog-app/_parent/products/early_access_features/frontend/EarlyAccessFeatures 3.24 kB
frontend/dist-report/posthog-app/_parent/products/endpoints/frontend/EndpointScene 40.7 kB
frontend/dist-report/posthog-app/_parent/products/endpoints/frontend/EndpointsScene 22.4 kB
frontend/dist-report/posthog-app/_parent/products/error_tracking/frontend/scenes/ErrorTrackingFingerprintsScene/ErrorTrackingIssueFingerprintsScene 7.44 kB
frontend/dist-report/posthog-app/_parent/products/error_tracking/frontend/scenes/ErrorTrackingIssueScene/ErrorTrackingIssueScene 101 kB
frontend/dist-report/posthog-app/_parent/products/error_tracking/frontend/scenes/ErrorTrackingScene/ErrorTrackingScene 27.2 kB
frontend/dist-report/posthog-app/_parent/products/feature_flags/frontend/FeatureFlagTemplatesScene 7.38 kB
frontend/dist-report/posthog-app/_parent/products/games/368Hedgehogs/368Hedgehogs 5.61 kB
frontend/dist-report/posthog-app/_parent/products/games/FlappyHog/FlappyHog 6.12 kB
frontend/dist-report/posthog-app/_parent/products/legal_documents/frontend/scenes/LegalDocumentNewScene 59.7 kB
frontend/dist-report/posthog-app/_parent/products/legal_documents/frontend/scenes/LegalDocumentsScene 5.32 kB
frontend/dist-report/posthog-app/_parent/products/links/frontend/LinkScene 25.2 kB
frontend/dist-report/posthog-app/_parent/products/links/frontend/LinksScene 4.55 kB
frontend/dist-report/posthog-app/_parent/products/live_debugger/frontend/LiveDebugger 19.5 kB
frontend/dist-report/posthog-app/_parent/products/llm_analytics/frontend/clusters/LLMAnalyticsClusterScene 21.7 kB
frontend/dist-report/posthog-app/_parent/products/llm_analytics/frontend/clusters/LLMAnalyticsClustersScene 55 kB
frontend/dist-report/posthog-app/_parent/products/llm_analytics/frontend/datasets/LLMAnalyticsDatasetScene 21 kB
frontend/dist-report/posthog-app/_parent/products/llm_analytics/frontend/datasets/LLMAnalyticsDatasetsScene 3.63 kB
frontend/dist-report/posthog-app/_parent/products/llm_analytics/frontend/evaluations/EvaluationTemplates 915 B
frontend/dist-report/posthog-app/_parent/products/llm_analytics/frontend/evaluations/LLMAnalyticsEvaluation 59.8 kB
frontend/dist-report/posthog-app/_parent/products/llm_analytics/frontend/evaluations/LLMAnalyticsEvaluationsScene 28.1 kB
frontend/dist-report/posthog-app/_parent/products/llm_analytics/frontend/LLMAnalyticsScene 119 kB
frontend/dist-report/posthog-app/_parent/products/llm_analytics/frontend/LLMAnalyticsSessionScene 16.8 kB
frontend/dist-report/posthog-app/_parent/products/llm_analytics/frontend/LLMAnalyticsTraceScene 130 kB
frontend/dist-report/posthog-app/_parent/products/llm_analytics/frontend/LLMAnalyticsUsers 866 B
frontend/dist-report/posthog-app/_parent/products/llm_analytics/frontend/LLMASessionFeedbackDisplay 5.19 kB
frontend/dist-report/posthog-app/_parent/products/llm_analytics/frontend/playground/LLMAnalyticsPlaygroundScene 37.7 kB
frontend/dist-report/posthog-app/_parent/products/llm_analytics/frontend/prompts/LLMPromptScene 29.2 kB
frontend/dist-report/posthog-app/_parent/products/llm_analytics/frontend/prompts/LLMPromptsScene 4.82 kB
frontend/dist-report/posthog-app/_parent/products/llm_analytics/frontend/skills/LLMSkillScene 929 B
frontend/dist-report/posthog-app/_parent/products/llm_analytics/frontend/skills/LLMSkillsScene 946 B
frontend/dist-report/posthog-app/_parent/products/llm_analytics/frontend/tags/LLMAnalyticsTag 27.3 kB
frontend/dist-report/posthog-app/_parent/products/llm_analytics/frontend/tags/LLMAnalyticsTagsScene 7.3 kB
frontend/dist-report/posthog-app/_parent/products/logs/frontend/LogsScene 17.8 kB
frontend/dist-report/posthog-app/_parent/products/logs/frontend/scenes/LogsAlertDetailScene/LogsAlertDetailScene 17.3 kB
frontend/dist-report/posthog-app/_parent/products/logs/frontend/scenes/LogsSamplingDetailScene/LogsSamplingDetailScene 5.31 kB
frontend/dist-report/posthog-app/_parent/products/logs/frontend/scenes/LogsSamplingNewScene/LogsSamplingNewScene 2.26 kB
frontend/dist-report/posthog-app/_parent/products/managed_migrations/frontend/ManagedMigration 14.9 kB
frontend/dist-report/posthog-app/_parent/products/mcp_analytics/frontend/MCPAnalyticsScene 40.2 kB
frontend/dist-report/posthog-app/_parent/products/mcp_analytics/frontend/MCPAnalyticsToolDetail 18.5 kB
frontend/dist-report/posthog-app/_parent/products/metrics/frontend/MetricsScene 1.18 kB
frontend/dist-report/posthog-app/_parent/products/product_analytics/frontend/insights/trends/StickinessBarChart/StickinessBarChart 3.31 kB
frontend/dist-report/posthog-app/_parent/products/product_analytics/frontend/insights/trends/StickinessLineChart/StickinessLineChart 3.14 kB
frontend/dist-report/posthog-app/_parent/products/product_analytics/frontend/insights/trends/TrendsBarChart/TrendsBarChart 7.15 kB
frontend/dist-report/posthog-app/_parent/products/product_analytics/frontend/insights/trends/TrendsLifecycleChart/TrendsLifecycleChart 4.1 kB
frontend/dist-report/posthog-app/_parent/products/product_analytics/frontend/insights/trends/TrendsLineChart/TrendsLineChart 4.6 kB
frontend/dist-report/posthog-app/_parent/products/product_analytics/frontend/insights/trends/TrendsPieChart/TrendsPieChart 4.35 kB
frontend/dist-report/posthog-app/_parent/products/replay_vision/frontend/replay_scanners/ReplayScanner 20.6 kB
frontend/dist-report/posthog-app/_parent/products/replay_vision/frontend/replay_scanners/ReplayScannersScene 12.5 kB
frontend/dist-report/posthog-app/_parent/products/revenue_analytics/frontend/RevenueAnalyticsScene 26.6 kB
frontend/dist-report/posthog-app/_parent/products/session_summaries/frontend/SessionGroupSummariesTable 5.05 kB
frontend/dist-report/posthog-app/_parent/products/session_summaries/frontend/SessionGroupSummaryScene 19.2 kB
frontend/dist-report/posthog-app/_parent/products/tasks/frontend/TaskDetailScene 23.6 kB
frontend/dist-report/posthog-app/_parent/products/tasks/frontend/TaskTracker 14.6 kB
frontend/dist-report/posthog-app/_parent/products/tracing/frontend/TracingScene 54.1 kB
frontend/dist-report/posthog-app/_parent/products/user_interviews/frontend/UserInterview 9.32 kB
frontend/dist-report/posthog-app/_parent/products/user_interviews/frontend/UserInterviewResponse 5.68 kB
frontend/dist-report/posthog-app/_parent/products/user_interviews/frontend/UserInterviews 6.08 kB
frontend/dist-report/posthog-app/_parent/products/visual_review/frontend/scenes/VisualReviewIndexScene 2.56 kB
frontend/dist-report/posthog-app/_parent/products/visual_review/frontend/scenes/VisualReviewRunScene 44.7 kB
frontend/dist-report/posthog-app/_parent/products/visual_review/frontend/scenes/VisualReviewRunsScene 7.32 kB
frontend/dist-report/posthog-app/_parent/products/visual_review/frontend/scenes/VisualReviewSettingsScene 11.1 kB
frontend/dist-report/posthog-app/_parent/products/visual_review/frontend/scenes/VisualReviewSnapshotHistoryScene 13.9 kB
frontend/dist-report/posthog-app/_parent/products/visual_review/frontend/scenes/VisualReviewSnapshotOverviewScene 19.6 kB
frontend/dist-report/posthog-app/_parent/products/workflows/frontend/TemplateLibrary/MessageTemplate 16.6 kB
frontend/dist-report/posthog-app/_parent/products/workflows/frontend/Workflows/WorkflowScene 104 kB
frontend/dist-report/posthog-app/_parent/products/workflows/frontend/WorkflowsScene 60.2 kB
frontend/dist-report/posthog-app/src/index 61 kB
frontend/dist-report/posthog-app/src/layout/panel-layout/ai-first/tabs/NavTabChat 7.16 kB
frontend/dist-report/posthog-app/src/lib/components/Cards/TextCard/TextCardMarkdownEditor 11.3 kB
frontend/dist-report/posthog-app/src/lib/components/MonacoDiffEditor 471 B
frontend/dist-report/posthog-app/src/lib/lemon-ui/LemonMarkdown/MermaidDiagram 2.25 kB
frontend/dist-report/posthog-app/src/lib/lemon-ui/LemonTextArea/LemonTextAreaMarkdown 842 B
frontend/dist-report/posthog-app/src/lib/lemon-ui/Link/Link 359 B
frontend/dist-report/posthog-app/src/lib/monaco/CodeEditorInline 832 B
frontend/dist-report/posthog-app/src/lib/monaco/vimMode 211 kB
frontend/dist-report/posthog-app/src/lib/ui/Button/ButtonPrimitives 426 B
frontend/dist-report/posthog-app/src/queries/nodes/WebVitals/WebVitals 7.52 kB
frontend/dist-report/posthog-app/src/queries/nodes/WebVitals/WebVitalsPathBreakdown 4.09 kB
frontend/dist-report/posthog-app/src/queries/schema 732 kB
frontend/dist-report/posthog-app/src/scenes/activity/explore/EventsScene 3.28 kB
frontend/dist-report/posthog-app/src/scenes/activity/explore/SessionsScene 4.69 kB
frontend/dist-report/posthog-app/src/scenes/activity/live/LiveEventsTable 5.58 kB
frontend/dist-report/posthog-app/src/scenes/agentic/AgenticAuthorize 5.84 kB
frontend/dist-report/posthog-app/src/scenes/approvals/ApprovalDetail 16.6 kB
frontend/dist-report/posthog-app/src/scenes/approvals/changeRequestsLogic 884 B
frontend/dist-report/posthog-app/src/scenes/audit-logs/AdvancedActivityLogsScene 40 kB
frontend/dist-report/posthog-app/src/scenes/AuthenticatedShell 171 kB
frontend/dist-report/posthog-app/src/scenes/authentication/AccountConnected 3.33 kB
frontend/dist-report/posthog-app/src/scenes/authentication/AgenticAccountMismatch 2.73 kB
frontend/dist-report/posthog-app/src/scenes/authentication/CLIAuthorize 11.7 kB
frontend/dist-report/posthog-app/src/scenes/authentication/CLILive 4.37 kB
frontend/dist-report/posthog-app/src/scenes/authentication/credential-review/CredentialReview 3.95 kB
frontend/dist-report/posthog-app/src/scenes/authentication/EmailMFAVerify 3.37 kB
frontend/dist-report/posthog-app/src/scenes/authentication/InviteSignup 15.4 kB
frontend/dist-report/posthog-app/src/scenes/authentication/Login 10.2 kB
frontend/dist-report/posthog-app/src/scenes/authentication/Login2FA 4.6 kB
frontend/dist-report/posthog-app/src/scenes/authentication/passkeyLogic 824 B
frontend/dist-report/posthog-app/src/scenes/authentication/PasswordReset 4.71 kB
frontend/dist-report/posthog-app/src/scenes/authentication/PasswordResetComplete 3.34 kB
frontend/dist-report/posthog-app/src/scenes/authentication/signup/SignupContainer 28.5 kB
frontend/dist-report/posthog-app/src/scenes/authentication/signup/verify-email/VerifyEmail 5.13 kB
frontend/dist-report/posthog-app/src/scenes/authentication/TwoFactorReset 4.37 kB
frontend/dist-report/posthog-app/src/scenes/authentication/VercelConnect 5.33 kB
frontend/dist-report/posthog-app/src/scenes/authentication/VercelLinkError 2.61 kB
frontend/dist-report/posthog-app/src/scenes/billing/AuthorizationStatus 1.07 kB
frontend/dist-report/posthog-app/src/scenes/billing/Billing 833 B
frontend/dist-report/posthog-app/src/scenes/billing/BillingSection 21.1 kB
frontend/dist-report/posthog-app/src/scenes/cohorts/Cohort 28.4 kB
frontend/dist-report/posthog-app/src/scenes/cohorts/CohortCalculationHistory 6.58 kB
frontend/dist-report/posthog-app/src/scenes/cohorts/Cohorts 9.78 kB
frontend/dist-report/posthog-app/src/scenes/coupons/Coupons 1.06 kB
frontend/dist-report/posthog-app/src/scenes/dashboard/Dashboard 1.65 kB
frontend/dist-report/posthog-app/src/scenes/dashboard/dashboards/Dashboards 19.8 kB
frontend/dist-report/posthog-app/src/scenes/dashboard/dashboards/templates/DashboardTemplateCopyScene 6.06 kB
frontend/dist-report/posthog-app/src/scenes/data-management/DataManagementScene 986 B
frontend/dist-report/posthog-app/src/scenes/data-management/definition/DefinitionEdit 17.2 kB
frontend/dist-report/posthog-app/src/scenes/data-management/definition/DefinitionView 24.4 kB
frontend/dist-report/posthog-app/src/scenes/data-management/MaterializedColumns/MaterializedColumns 12 kB
frontend/dist-report/posthog-app/src/scenes/data-management/variables/SqlVariableEditScene 7.6 kB
frontend/dist-report/posthog-app/src/scenes/data-pipelines/batch-exports/BatchExportScene 61 kB
frontend/dist-report/posthog-app/src/scenes/data-pipelines/DataPipelinesNewScene 2.66 kB
frontend/dist-report/posthog-app/src/scenes/data-pipelines/DestinationsScene 3.03 kB
frontend/dist-report/posthog-app/src/scenes/data-pipelines/event-filtering/EventFilterScene 22.2 kB
frontend/dist-report/posthog-app/src/scenes/data-pipelines/legacy-plugins/LegacyPluginScene 21 kB
frontend/dist-report/posthog-app/src/scenes/data-pipelines/TransformationsScene 2.27 kB
frontend/dist-report/posthog-app/src/scenes/data-pipelines/WebScriptsScene 2.89 kB
frontend/dist-report/posthog-app/src/scenes/data-warehouse/DataWarehouseScene 1.72 kB
frontend/dist-report/posthog-app/src/scenes/data-warehouse/editor/EditorScene 1.48 kB
frontend/dist-report/posthog-app/src/scenes/debug/DebugScene 20.3 kB
frontend/dist-report/posthog-app/src/scenes/debug/hog/HogRepl 7.72 kB
frontend/dist-report/posthog-app/src/scenes/experiments/Experiment 207 kB
frontend/dist-report/posthog-app/src/scenes/experiments/Experiments 20.8 kB
frontend/dist-report/posthog-app/src/scenes/experiments/SharedMetrics/SharedMetric 6.41 kB
frontend/dist-report/posthog-app/src/scenes/experiments/SharedMetrics/SharedMetrics 889 B
frontend/dist-report/posthog-app/src/scenes/exports/ExportsScene 4.33 kB
frontend/dist-report/posthog-app/src/scenes/feature-flags/FeatureFlag 146 kB
frontend/dist-report/posthog-app/src/scenes/feature-flags/FeatureFlags 1.08 kB
frontend/dist-report/posthog-app/src/scenes/groups/Group 15.5 kB
frontend/dist-report/posthog-app/src/scenes/groups/Groups 4.26 kB
frontend/dist-report/posthog-app/src/scenes/groups/GroupsNew 7.7 kB
frontend/dist-report/posthog-app/src/scenes/health/categoryDetail/HealthCategoryDetailScene 7.59 kB
frontend/dist-report/posthog-app/src/scenes/health/HealthScene 12.5 kB
frontend/dist-report/posthog-app/src/scenes/health/pipelineStatus/PipelineStatusScene 9.45 kB
frontend/dist-report/posthog-app/src/scenes/heatmaps/scenes/heatmap/HeatmapNewScene 5.38 kB
frontend/dist-report/posthog-app/src/scenes/heatmaps/scenes/heatmap/HeatmapRecordingScene 4.27 kB
frontend/dist-report/posthog-app/src/scenes/heatmaps/scenes/heatmap/HeatmapScene 6.91 kB
frontend/dist-report/posthog-app/src/scenes/heatmaps/scenes/heatmaps/HeatmapsScene 4.23 kB
frontend/dist-report/posthog-app/src/scenes/hog-functions/HogFunctionScene 59.6 kB
frontend/dist-report/posthog-app/src/scenes/inbox/InboxScene 63.3 kB
frontend/dist-report/posthog-app/src/scenes/insights/InsightQuickStart/InsightQuickStart 5.77 kB
frontend/dist-report/posthog-app/src/scenes/insights/InsightScene 34.8 kB
frontend/dist-report/posthog-app/src/scenes/insights/views/BoxPlot/BoxPlot 5.39 kB
frontend/dist-report/posthog-app/src/scenes/insights/views/CalendarHeatMap/CalendarHeatMap 4.84 kB
frontend/dist-report/posthog-app/src/scenes/insights/views/RegionMap/RegionMap 29.8 kB
frontend/dist-report/posthog-app/src/scenes/insights/views/WorldMap/WorldMap 5.13 kB
frontend/dist-report/posthog-app/src/scenes/instance/AsyncMigrations/AsyncMigrations 13.5 kB
frontend/dist-report/posthog-app/src/scenes/instance/DeadLetterQueue/DeadLetterQueue 5.74 kB
frontend/dist-report/posthog-app/src/scenes/instance/QueryPerformance/QueryPerformance 8.97 kB
frontend/dist-report/posthog-app/src/scenes/instance/SystemStatus/SystemStatus 17.4 kB
frontend/dist-report/posthog-app/src/scenes/IntegrationsRedirect/IntegrationsRedirect 1.08 kB
frontend/dist-report/posthog-app/src/scenes/marketing-analytics/MarketingAnalyticsScene 40.5 kB
frontend/dist-report/posthog-app/src/scenes/max/Max 1.02 kB
frontend/dist-report/posthog-app/src/scenes/models/ModelsScene 19 kB
frontend/dist-report/posthog-app/src/scenes/models/NodeDetailScene 17.1 kB
frontend/dist-report/posthog-app/src/scenes/moveToPostHogCloud/MoveToPostHogCloud 4.81 kB
frontend/dist-report/posthog-app/src/scenes/new-tab/NewTabScene 1.82 kB
frontend/dist-report/posthog-app/src/scenes/notebooks/NotebookCanvasScene 3.89 kB
frontend/dist-report/posthog-app/src/scenes/notebooks/NotebookPanel/NotebookPanel 5.94 kB
frontend/dist-report/posthog-app/src/scenes/notebooks/NotebookScene 9.13 kB
frontend/dist-report/posthog-app/src/scenes/notebooks/NotebooksScene 7.95 kB
frontend/dist-report/posthog-app/src/scenes/oauth/OAuthAuthorize 980 B
frontend/dist-report/posthog-app/src/scenes/onboarding/coupon/OnboardingCouponRedemption 1.55 kB
frontend/dist-report/posthog-app/src/scenes/onboarding/Onboarding 791 kB
frontend/dist-report/posthog-app/src/scenes/onboarding/sdks/SdkDoctorScene 9.77 kB
frontend/dist-report/posthog-app/src/scenes/organization/ConfirmOrganization/ConfirmOrganization 4.88 kB
frontend/dist-report/posthog-app/src/scenes/organization/Create/Create 1 kB
frontend/dist-report/posthog-app/src/scenes/organization/Deactivated 1.48 kB
frontend/dist-report/posthog-app/src/scenes/organization/PendingDeletion 2.45 kB
frontend/dist-report/posthog-app/src/scenes/persons/PersonScene 19 kB
frontend/dist-report/posthog-app/src/scenes/persons/PersonsScene 6.09 kB
frontend/dist-report/posthog-app/src/scenes/PreflightCheck/PreflightCheck 5.91 kB
frontend/dist-report/posthog-app/src/scenes/product-tours/ProductTour 275 kB
frontend/dist-report/posthog-app/src/scenes/product-tours/ProductTours 5.03 kB
frontend/dist-report/posthog-app/src/scenes/project-homepage/ProjectHomepage 18.4 kB
frontend/dist-report/posthog-app/src/scenes/project/Create/Create 1.18 kB
frontend/dist-report/posthog-app/src/scenes/resource-transfer/ResourceTransfer 9.53 kB
frontend/dist-report/posthog-app/src/scenes/saved-insights/SavedInsights 1 kB
frontend/dist-report/posthog-app/src/scenes/session-recordings/detail/SessionRecordingDetail 2.1 kB
frontend/dist-report/posthog-app/src/scenes/session-recordings/file-playback/SessionRecordingFilePlaybackScene 4.82 kB
frontend/dist-report/posthog-app/src/scenes/session-recordings/kiosk/SessionRecordingsKiosk 10.3 kB
frontend/dist-report/posthog-app/src/scenes/session-recordings/player/snapshot-processing/DecompressionWorkerManager 329 B
frontend/dist-report/posthog-app/src/scenes/session-recordings/playlist/SessionRecordingsPlaylistScene 5.45 kB
frontend/dist-report/posthog-app/src/scenes/session-recordings/SessionRecordings 1.12 kB
frontend/dist-report/posthog-app/src/scenes/session-recordings/settings/SessionRecordingsSettingsScene 2.31 kB
frontend/dist-report/posthog-app/src/scenes/sessions/SessionProfileScene 15.4 kB
frontend/dist-report/posthog-app/src/scenes/settings/SettingsScene 3.9 kB
frontend/dist-report/posthog-app/src/scenes/sites/Site 1.53 kB
frontend/dist-report/posthog-app/src/scenes/startups/StartupProgram 21.5 kB
frontend/dist-report/posthog-app/src/scenes/StripeConfirmInstall/StripeConfirmInstall 3.88 kB
frontend/dist-report/posthog-app/src/scenes/subscriptions/SubscriptionScene 14.7 kB
frontend/dist-report/posthog-app/src/scenes/subscriptions/SubscriptionsScene 5.53 kB
frontend/dist-report/posthog-app/src/scenes/surveys/forms/SurveyFormBuilder 1.89 kB
frontend/dist-report/posthog-app/src/scenes/surveys/Survey 1.36 kB
frontend/dist-report/posthog-app/src/scenes/surveys/Surveys 26.7 kB
frontend/dist-report/posthog-app/src/scenes/surveys/wizard/SurveyWizard 72.7 kB
frontend/dist-report/posthog-app/src/scenes/themes/CustomCssScene 3.91 kB
frontend/dist-report/posthog-app/src/scenes/toolbar-launch/ToolbarLaunch 2.82 kB
frontend/dist-report/posthog-app/src/scenes/Unsubscribe/Unsubscribe 2 kB
frontend/dist-report/posthog-app/src/scenes/web-analytics/SessionAttributionExplorer/SessionAttributionExplorerScene 6.97 kB
frontend/dist-report/posthog-app/src/scenes/web-analytics/WebAnalyticsScene 10.6 kB
frontend/dist-report/posthog-app/src/scenes/wizard/Wizard 4.8 kB
frontend/dist-report/posthog-app/src/sharedChunkAnchors 1.19 kB
frontend/dist-report/render-query/src/render-query/render-query 27.2 MB
frontend/dist-report/toolbar/src/toolbar/toolbar 15.7 MB

compressed-size-action

Copy link
Copy Markdown
Contributor

@joshsny joshsny left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, some things to fix but then we can get this in 🚢

Comment thread ee/api/quota_limits.py
def list(self, request: Request, *args: Any, **kwargs: Any) -> Response:
limited = {
QuotaResource.AI_CREDITS.value: {
"limited": is_team_limited(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does this function call? is it a clickhouse query, or hitting redis? (I think redis in which case great)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup, redis 🚀

Comment thread ee/api/quota_limits.py
),
responses={200: QuotaLimitsResponseSerializer},
)
def list(self, request: Request, *args: Any, **kwargs: Any) -> Response:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be general given the endpoints registration, lets show them quota limiting status for all products?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is technically only data that should be available to admins since it's directed off of billing data, but given we're just exposing is limited or not this seems fine to be available to anyone with project:read imo

cc @PostHog/team-billing in case one of them disagrees, but you can assume not

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.



@activity.defn
def enforce_posthog_code_billing_quota_activity(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should pull activities into their own file together with their input / output types as a general rule of thumb

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, don't like it either, but it's going to be a bigger change, planning to do it as follow up and move all activities to own file(s)

Comment thread products/slack_app/backend/api.py Outdated
return slack_user_id


def _refuse_mention_if_team_over_quota(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this a job of the workflow or the API, it should be a job of one of those things, but not both (probably the workflow)?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't feel super strong about this one, so happy to do one of those, but why not both?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

# Cache window for the fail-open path (4xx from Django, e.g. expired token).
# Short enough that a fixed-up token recovers quickly; long enough to keep a
# misconfigured client off Django's neck during an auth-failure storm.
_FAIL_OPEN_CACHE_TTL_SECONDS = 5
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can probs fail open for longer, say 60 seconds

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


posthog_api_base_url: str = "https://us.posthog.com"
plan_cache_ttl: int = 900 # 15 minutes
# AI credits quota state is fast-moving — Django itself caches the underlying
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should check this with billing, but we used to quota limit on an hourly schedule, not sure if that's still the case, in any case we can bump the ttl here to 5 mins, we are okay if teams go slightly over their limit

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

end_user_id = await _extract_end_user_id_from_body(request)

plan_info = await resolve_plan_info(request, user.user_id, product)
# Plan + quota are independent Django roundtrips on cache miss — overlap them.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this logic is in two places, can we pull into a function?

return cached

try:
status, ttl = await self._fetch(resource_key, team_id, auth_header)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should do exponential backoff here up to the fail open ttl and then fail closed

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@VojtechBartos VojtechBartos force-pushed the vojtab/slack-bot-ai-credits-billing branch 2 times, most recently from eb4f9d5 to 96084d4 Compare May 27, 2026 10:58
"""Resolve the team's AI credits quota state, falling open on errors."""
if team_id is None:
return QuotaResourceStatus(limited=False)
auth_header = request.headers.get("Authorization", "")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium: AI-credit quota bypass with X-API-Key auth

AuthService accepts API keys from X-API-Key, but this resolver treats requests without Authorization as unlimited. An over-quota caller can use the supported X-API-Key header against a billable API-key product such as slack_app_routing and avoid the AI-credit throttle.

Suggested change
auth_header = request.headers.get("Authorization", "")
auth_header = request.headers.get("Authorization", "")
if not auth_header and (api_key := request.headers.get("x-api-key")):
auth_header = f"Bearer {api_key}"

VojtechBartos and others added 11 commits May 27, 2026 14:38
…edge

Adds BillableCreditThrottle that, for any product with ProductConfig.billable=True,
ZSCOREs the team's API token against @posthog/quota-limits/ai_credits — the same
Redis set Django's daily aggregator writes to. Over-quota teams get a 429 with a
friendly denial before the request reaches the upstream LLM provider.

Closes the cross-surface gap for slack-origin tasks: PostHog Code desktop replies
bypass the Temporal pre-flight, but cannot bypass the gateway.

Plumbs team_api_token through AuthenticatedUser (LEFT JOIN posthog_team in both
authenticators) so the throttle has the key it needs to look up Redis.

Fails open when Redis or team_api_token is missing — matches the rest of the
throttle chain. A startup warning surfaces when Redis isn't configured so we
notice silent disablement in production.
The existing gate fires deep inside `create_posthog_code_task_for_repo_activity`, after the
classifier has already been called against the llm-gateway. For over-quota teams the
classifier 429s and the bot's catch-all error handler steers it into the repo-picker,
posting in the wrong thread.

Two new entry points:
- Webhook: refuse `app_mention` before `start_workflow` so no Temporal execution is
  created. Saves Slack roundtrips and a billable classifier call.
- Workflow: new `enforce_posthog_code_billing_quota_activity`, first call in
  `PostHogCodeSlackMentionWorkflow.run()`, guarded by `workflow.patched(
  "posthog-code-slack-billing-gate")` so in-flight workflows stay deterministic.

Existing in-activity gates stay as defense in depth (and to keep the direct-activity
unit tests passing).
@VojtechBartos VojtechBartos force-pushed the vojtab/slack-bot-ai-credits-billing branch from 1cc02bf to 51f0a58 Compare May 27, 2026 12:39
@VojtechBartos VojtechBartos force-pushed the vojtab/slack-bot-ai-credits-billing branch from 51f0a58 to 9d08641 Compare May 27, 2026 13:10
@VojtechBartos VojtechBartos merged commit d8f44ec into master May 27, 2026
220 checks passed
@VojtechBartos VojtechBartos deleted the vojtab/slack-bot-ai-credits-billing branch May 27, 2026 13:32
@deployment-status-posthog
Copy link
Copy Markdown

deployment-status-posthog Bot commented May 27, 2026

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-05-27 13:55 UTC Run
prod-us ✅ Deployed 2026-05-27 14:09 UTC Run
prod-eu ✅ Deployed 2026-05-27 14:13 UTC Run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants