Skip to content

feat(cloud): Enforce per-plan AI token allowance #9

Description

@nfebe

The cloud plugin should enforce a per-plan AI token allowance by implementing the usage half of the Entitlements seam. Core already gives the plugin what it needs: it gates each turn with remaining($owner, 'ai_tokens') and meters with consume($owner, 'ai_tokens', $n), both keyed on the polymorphic owner. The plugin supplies the counter storage, resolves the owner's allowance, and binds its implementation over the default AllowAllEntitlements.

Two things aren't in place yet: the owner's active plan is never persisted (plans exist only as marketing config), and core meters the agent route only, so a user can spend tokens on other routes without touching their allowance.

Checklist

  • Persist the owner's active plan in the plugin so remaining() can resolve an allowance (link an existing plan/subscription issue if one already covers this).
  • Add a token_allowance and a monthly reset period to each plan.
  • Create ai_usage_counters in the plugin, keyed polymorphically: morphs('owner'), period_start, tokens_used, unique(owner_type, owner_id, period_start).
  • Implement remaining('ai_tokens') as allowance minus current-period usage and consume('ai_tokens', n) as an increment. Bind over the default.
  • (core) Meter every token-spending path. The general and SmartQL replies and the per-turn classify and title calls currently call neither consume() nor record usage; only the agent route does.
  • (core) Fire consume() at most once per assistant message, keyed on the message id core already owns, so a job retry doesn't double-count.

Acceptance

  • A user over their allowance gets a quota-exceeded response from POST /api/v1/ai/chat.
  • Usage rolls over monthly.
  • A deployment with the cloud plugin disabled stays unlimited.
  • A core test confirms the counter matches the tokens core recorded for a turn.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions