Conversation
…paid plans Introduces an extensible feature registry under lib/features/ as the single source of truth for plan-gated capabilities. Adding a new gated feature is a one-entry diff to the FEATURES dictionary. Server enforcement runs at every workflow execution surface (manual, webhook, MCP, internal, scheduler, block, event triggers) and at workflow save time. The executor records a failed workflow_executions row instead of silent-dropping blocked SQS triggers so users see the blocked run in their dashboard. Default-deny when no org context. Client surface keeps gated actions visible in the picker with a diamond icon and grayed style; click opens an upgrade dialog linking to the public pricing page. A toast warns when the user opens an existing workflow that contains gated nodes. Feature snapshot invalidates on org switch and the toast signature includes the plan so plan changes re-evaluate without a reload.
- Gate POST /api/workflows/create against the feature registry - Gate POST /api/workflows/import (JSON import path) - Gate POST /api/workflows/[id]/duplicate (copies gated nodes into new workflow) - Gate POST /api/workflows/current (auto-save of the scratch workflow) - Gate POST /api/workflows/[id]/claim (anonymous workflow into org) Invalidate the client feature snapshot when the auth session user changes via a new FeatureSessionInvalidator mounted in the root layout, so the module-level cache no longer leaks across sign-out/sign-in transitions. Make useFeatures fall open client-side when /api/features is unreachable (fallback to an enterprise-equivalent snapshot) so a network blip does not lock the entire UI behind gated diamonds. Server-side guards remain authoritative; any actual save or execute attempt is still rejected. Reorder declarations in action-config.tsx so isActionLocked is declared before handleCategoryChange, removing the TDZ-only-works-at-runtime fragility flagged in review.
The feature route guard module starts with import "server-only", which explodes when imported in a vitest environment that does not also mock the server-only package. Both x402-call-route.test.ts and mcp-meta-tools.test.ts already mock every other server-only dependency of the MCP call route handler; add a matching mock for enforceWorkflowFeatures so the route can be imported in test context.
…d routes Six integration tests covered routes that now invoke enforceWorkflowFeatures from @/lib/features/route-guard. That module starts with import "server-only", so importing the route handler in a vitest environment without a matching mock pulls in the real server-only package and throws. Add the mock alongside the existing route-level mocks (validateWorkflowIntegrations, getOrgContext, enforceExecutionLimit, etc.) to keep the test surface consistent.
Hold the listener function in a useRef so the same closure registers and unregisters across React 18 strict-mode double mounts. Without the ref, each render created a new listener that the cleanup phase would chase, leaving Set membership consistent but the closure identity unstable.
…l-open Replaces the fail-open snapshot fallback with a 250ms retry loop capped at 60 attempts. UI stays in loading state during a network outage so a disabled or kill-switched feature can never appear unlocked in the picker. Server-side guards remain authoritative regardless. The previous fail-open behavior could expose features with enabled=false in the registry whenever /api/features was unreachable, even though the kill switch is supposed to be authoritative.
Bail out of FeatureSessionInvalidator while useSession reports isPending. The previous logic recorded the loading-state null sentinel as the first observed user id, so the real id arriving a tick later looked like a sign-in transition and triggered an unnecessary snapshot invalidate.
Adds an optional errorMessage to enforceWorkflowFeatures so each route can phrase the upgrade prompt with its own context, then uses that from the claim route to explain the destination-org-plan situation instead of the generic "requires a paid plan" wording. A user moving an anonymous workflow built during a Pro trial into a Free org would otherwise see a 402 with no hint that the gate is on the destination org, not the workflow itself.
Adds BILLING to the ErrorCategory enum and to the workflow_executions errorCategory TypeScript narrowing, then switches the executor to use errorCategory: "billing", errorType: "user" when blocking an SQS trigger because the org's plan does not include a workflow feature. Previously these rows were tagged "configuration", which conflates a malformed workflow with a plan-insufficient one. With a distinct "billing" value, dashboards, oncall, and customer success can filter plan-block events cleanly. No DB migration: the column is text with TypeScript-level narrowing only.
feat: gate Database Query, HTTP Request, Code and Webhook actions to paid plans
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.