@happyvertical/smrt-subscriptions 0.60.14 → 0.60.15

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/AGENTS.md CHANGED
@@ -86,6 +86,51 @@ that reverted to DECIMAL passes every SQLite suite.
86
86
  `retainsPaidPeriod`, derived on save from an entitled pre-cancel row.
87
87
  - **Recipe `subscriptions.plans`** (`src/recipes.ts`, help in `src/plans.recipe.md`): lists plans, subscriptions, plan versions (read-only, written by `plan-versioning.ts`) and charges (read-only, written by the usage service) for the planner and other catalog hosts. Fields it shows carry `@field({ description })` in plain words, money fields saying "in cents"; keep them when editing the models. Reseller models and pricing rules stay out of it.
88
88
 
89
+ ## Hosted AI spend caps (#4760)
90
+
91
+ `src/services/hosted-ai-spend.ts`. Plans cap platform-paid (`source:
92
+ hosted`) inference with ordinary `PlanThreshold`s on two reserved keys, so
93
+ window, amount and enforcement come from the plan (pinned version included):
94
+ `AI_HOSTED_SPEND_METRIC` (`ai.hosted.spend_micros`, the tenant) and
95
+ `AI_HOSTED_USER_SPEND_METRIC` (`ai.hosted.user_spend_micros`, each user).
96
+
97
+ - `limit` is **integer micro-dollars** (1 USD = 1,000,000; the money rule at
98
+ per-call precision). `block` blocks at or over the cap, `warn` warns,
99
+ `observe` never affects a call; `warningRatio` (default 0.8) warns early;
100
+ a 0 cap blocks every hosted call. A malformed hosted cap (fractional or
101
+ negative limit, unknown window/enforcement) throws
102
+ `HostedAiSpendPolicyError` (the gate fails closed); it never reads as "no
103
+ cap". `resolvePlanThresholds()` returns thresholds unfiltered for this
104
+ reason (the models' `getThresholds()` drop invalid entries) and throws on
105
+ non-array JSON.
106
+ - Spend: `createHostedAiSpendReader(db)` sums `_smrt_ai_usage.estimated_cost`
107
+ (USD, rounded to micro-dollars) by `tenant_id` and `created_at` in the
108
+ cap's window for rows whose `tags` hold `"source":"hosted"` (and
109
+ `"userId":"<id>"` for the per-user cap), as exact, case-sensitive JSON
110
+ fragments (`hostedAiTagFragment()`; `strpos` on the PostgreSQL dialect,
111
+ `instr` elsewhere, never `LIKE`, which folds ASCII case on SQLite and
112
+ would merge `Alice` and `alice`); errors only over-count.
113
+ `operation: 'blocked'` rows and external-agent measurements never count;
114
+ uncosted rows add nothing (give hosted models a cost rate). The `userId`
115
+ tag exists only while the hosted gate's ambient-user resolver is
116
+ registered.
117
+ - `resetsAt`: the calendar window's end; for a blocked `rolling` cap (30
118
+ days), `HostedAiSpendReader.liftsAt()`: one window-function query (newest
119
+ first running `SUM(estimated_cost)`, rounded as `summarize()` rounds)
120
+ returns the row whose expiry first brings the total below the cap, plus
121
+ 30 days (null for a 0 cap). A blocked decision names the blocking cap that
122
+ lifts last.
123
+ - `HostedAiSpendEvaluator` (`create({ db })`): plan thresholds via
124
+ `SubscriptionResolver.resolvePlanThresholds()` (entitled terms, no usage
125
+ meters; memoized per tenant for `termsTtlMs`, 30 s, failures evicted,
126
+ `invalidate()`), spend read fresh per call; per-user caps only with a
127
+ user. Errors propagate. smrt-inference's `./gate` consumes it.
128
+ - `resolveEntitlements()` / the tenant usage meter report these keys with
129
+ quantity 0: only the evaluator meters them. The meter's AI summary (and so
130
+ `ai.requests`) skips `operation: 'blocked'` rows. `SpendingPolicy` rows stay the
131
+ billed-charge (cents) policy; hosted AI caps are plan terms because the
132
+ issue's window and amount come from the plan.
133
+
89
134
  ## Reseller billing (#3059)
90
135
 
91
136
  - Billing ownership is consumed from `smrt-tenancy`'s
@@ -129,3 +174,32 @@ that reverted to DECIMAL passes every SQLite suite.
129
174
  current subscription and plan.
130
175
  - Subscription UI components should stay provider-neutral and receive actions
131
176
  from the host app.
177
+ - **Feature grants as flags (#4761).** `SubscriptionFeatureSource` implements
178
+ `smrt-features`' `FeatureGrantSource` structurally (no dependency either
179
+ way). Build one per request (`SubscriptionFeatureSource.create(options)` or
180
+ `new SubscriptionFeatureSource({ resolver })`; it memoizes grants per
181
+ tenant) and pass it as `grantSource`, or register an app-wide one built with
182
+ `memoize: false` through `registerSubscriptionFeatureSource()`, which writes
183
+ the features slot key `FEATURE_GRANT_SOURCE_SLOT_KEY`. It reads
184
+ `SubscriptionResolver.resolveFeatureGrants()`, which shares the
185
+ version-pinning and entitlement rules of `resolveEntitlements()` but never
186
+ evaluates thresholds, so a flag check reads no usage meters. Missing
187
+ subscription tables (`isMissingSubscriptionTableError`) and no entitled
188
+ subscription report no grants silently; every other error propagates.
189
+ `findGrantingPlan()` names the first active plan by `sortOrder` visible in
190
+ the current context.
191
+ - **Entitlement change hook (#4759).** `src/services/entitlement-changes.ts`.
192
+ The host wraps its billing-webhook write in
193
+ `trackEntitlementChange({ tenantId, resolver }, apply)`: it snapshots
194
+ `resolveFeatureGrants()` (keys plus grant `metadata`, no usage reads)
195
+ before and after, diffs `gained` / `lost` / `changed` (metadata, e.g. a
196
+ plan budget) and calls listeners registered with
197
+ `registerEntitlementChangeListener()` (a `Symbol.for` slot) or passed in.
198
+ Listeners run after `apply`; their failures are returned in `failures`,
199
+ never thrown, and never undo the write, so listeners must be idempotent.
200
+ An `apply` error propagates and notifies nobody. A redelivered webhook
201
+ diffs to no change, so a lost reaction is repaired by the consumer's
202
+ reconcile entry point (hosted inference: `createHostedInferenceReconciler()`
203
+ with `snapshotEntitlements()`), run on redelivery or a schedule. Listeners satisfy the
204
+ structural type (smrt-inference's hosted listener), so this package
205
+ depends on none of them.
package/README.md CHANGED
@@ -39,6 +39,17 @@ Create one resolver per request or application unit of work and reuse the
39
39
  loaded `EntitlementResolutionContext` when several checks need the same
40
40
  subscription and plan.
41
41
 
42
+ ### Plan grants as feature flags
43
+
44
+ `SubscriptionFeatureSource` feeds a tenant's plan grants into
45
+ `@happyvertical/smrt-features`, so `FeatureResolver` and its `requireFeature()`
46
+ gate answer from the plan. Build one per request with
47
+ `SubscriptionFeatureSource.create(dbOptions)` and pass it as `grantSource`, or
48
+ register an app-wide one (`memoize: false`) with
49
+ `registerSubscriptionFeatureSource()`. With no subscription tables or no
50
+ entitled subscription it reports no grants. A global feature override still
51
+ outranks a grant (an operator stop), and a tenant override outranks both.
52
+
42
53
  ## Core model
43
54
 
44
55
  - `SubscriptionPlan` is tenant-scoped by `(tenantId, planKey)` and stores
@@ -1 +1 @@
1
- {"version":3,"file":"TenantUsageMetricCollection.d.ts","sourceRoot":"","sources":["../../src/collections/TenantUsageMetricCollection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,KAAK,EACV,cAAc,EACd,kBAAkB,EAClB,uBAAuB,EACvB,0BAA0B,EAC1B,qBAAqB,EACrB,YAAY,EACb,MAAM,aAAa,CAAC;AAQrB,qBAAa,2BAA4B,SAAQ,cAAc,CAAC,iBAAiB,CAAC;IAChF,MAAM,CAAC,QAAQ,CAAC,UAAU,2BAAqB;IAEzC,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAmD1E;;;OAGG;IACG,qBAAqB,CACzB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAOzB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,YAAY,CAAC;IAsCrE,mBAAmB,CACvB,OAAO,EAAE,0BAA0B,GAClC,OAAO,CAAC,YAAY,EAAE,CAAC;IAqE1B;;;;;;;;;;;OAWG;IACG,sBAAsB,CAC1B,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,cAAc,CAAC;CA8B3B"}
1
+ {"version":3,"file":"TenantUsageMetricCollection.d.ts","sourceRoot":"","sources":["../../src/collections/TenantUsageMetricCollection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,KAAK,EACV,cAAc,EACd,kBAAkB,EAClB,uBAAuB,EACvB,0BAA0B,EAC1B,qBAAqB,EACrB,YAAY,EACb,MAAM,aAAa,CAAC;AAQrB,qBAAa,2BAA4B,SAAQ,cAAc,CAAC,iBAAiB,CAAC;IAChF,MAAM,CAAC,QAAQ,CAAC,UAAU,2BAAqB;IAEzC,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAmD1E;;;OAGG;IACG,qBAAqB,CACzB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAOzB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,YAAY,CAAC;IAsCrE,mBAAmB,CACvB,OAAO,EAAE,0BAA0B,GAClC,OAAO,CAAC,YAAY,EAAE,CAAC;IAqE1B;;;;;;;;;;;OAWG;IACG,sBAAsB,CAC1B,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,cAAc,CAAC;CA+B3B"}
package/dist/index.d.ts CHANGED
@@ -16,7 +16,7 @@ export { enabledFeatureKeys, extractPlanTerms, isEntitlementUpgrade, PLAN_TERM_F
16
16
  export type { PlanVersionReplacement } from './plan-versioning.js';
17
17
  export { SubscriptionPlansRecipe } from './recipes.js';
18
18
  export type { AssignPriceBooksInput, AutoTopUpGrant, AutoTopUpHook, AutoTopUpRequest, BillingRelationshipReader, CommercialBillingStorage, CommercialUsageServiceOptions, CustomPricingContext, CustomPricingStrategy, DefinePriceBookPriceInput, DelegatedSpendingPolicyInput, GrantChildCreditInput, GrantCreditInput, PriceBookAssignmentView, PriceBookLeg, PriceUsageOptions, RateUsageOptions, ResellerBillingAction, ResellerBillingErrorCode, ResellerBillingServiceOptions, SpendingDecision, SpendingEvaluationInput, SpendingPolicyEvaluatorExtensions, SpendingPolicyEvaluatorOptions, UsageRating, } from './services/index.js';
19
- export { assertCommercialBillingStorageSupported, CommercialBillingStorageConfigurationError, CommercialUsageService, evaluateThreshold, evaluateThresholds, ResellerBillingError, ResellerBillingService, SpendingPolicyEvaluator, type SubscriptionPlanReader, type SubscriptionPlanVersionReader, SubscriptionResolver, type SubscriptionResolverReaders, type TenantSubscriptionReader, TenantUsageMeter, UnsupportedCommercialBillingStorageError, type UsageSummaryReader, } from './services/index.js';
19
+ export { AI_HOSTED_SPEND_METRIC, AI_HOSTED_USER_SPEND_METRIC, assertCommercialBillingStorageSupported, CommercialBillingStorageConfigurationError, CommercialUsageService, createHostedAiSpendReader, decideHostedAiSpend, diffEntitlements, ENTITLEMENT_CHANGE_LISTENERS_SLOT_KEY, type EntitlementChange, type EntitlementChangeFailure, type EntitlementChangeListener, type EntitlementChangeOutcome, type EntitlementSnapshot, emptyEntitlementSnapshot, evaluateHostedAiSpendThreshold, evaluateThreshold, evaluateThresholds, FEATURE_GRANT_SOURCE_SLOT_KEY, type GrantingPlanReader, getEntitlementChangeListeners, type HostedAiSpendDecision, type HostedAiSpendEvaluation, HostedAiSpendEvaluator, type HostedAiSpendEvaluatorOptions, type HostedAiSpendPolicy, HostedAiSpendPolicyError, type HostedAiSpendReader, type HostedAiSpendScope, type HostedAiSpendSummary, hasEntitlementChange, hostedAiSpendThresholds, hostedAiTagFragment, isMissingSubscriptionTableError, MICROS_PER_USD, notifyEntitlementChange, ResellerBillingError, ResellerBillingService, registerEntitlementChangeListener, registerSubscriptionFeatureSource, SpendingPolicyEvaluator, type SubscriptionFeatureGrants, SubscriptionFeatureSource, type SubscriptionFeatureSourceOptions, type SubscriptionGrantingPlan, type SubscriptionPlanReader, type SubscriptionPlanVersionReader, SubscriptionResolver, type SubscriptionResolverReaders, snapshotEntitlements, type TenantSubscriptionReader, TenantUsageMeter, type TrackEntitlementChangeOptions, trackEntitlementChange, UnsupportedCommercialBillingStorageError, type UsageSummaryReader, } from './services/index.js';
20
20
  export type { AiUsageSummary, BillingInterval, EntitlementResolution, EntitlementResolutionContext, JsonObject, PlanFeatureGrant, PlanTerms, PlanThreshold, PlanVersionPinReason, PricingMode, RecordUsageOptions, Subscriber, SubscriberKind, SubscriptionPlanStatus, SubscriptionPlanVersionOptions, SubscriptionResolverOptions, SubscriptionStatus, SummarizeAiUsageOptions, SummarizeUsageBatchOptions, SummarizeUsageOptions, ThresholdEnforcement, ThresholdEvaluation, ThresholdWindow, UsageMeterOptions, UsageMetricRecord, UsageSummary, UsageWindow, } from './types.js';
21
21
  export { assertSubscriberInvariant, getWindowForThreshold, getWindowKey, isValidThreshold, normalizeFeatureGrants, normalizeSubscriber, subscriberToColumns, } from './utils.js';
22
22
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,wBAAwB,CAAC;AAEhC,OAAO,EACL,0BAA0B,EAC1B,iCAAiC,EACjC,4BAA4B,EAC5B,2BAA2B,GAC5B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,qCAAqC,EACrC,qCAAqC,EACrC,2BAA2B,EAC3B,wCAAwC,GACzC,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EACL,+BAA+B,EAC/B,KAAK,uCAAuC,GAC7C,MAAM,8BAA8B,CAAC;AACtC,YAAY,EACV,aAAa,EACb,eAAe,EACf,aAAa,EACb,cAAc,EACd,sBAAsB,GACvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,iBAAiB,EACjB,2BAA2B,EAC3B,YAAY,EACZ,sBAAsB,EACtB,WAAW,EACX,qBAAqB,EACrB,cAAc,EACd,eAAe,EACf,SAAS,EACT,mBAAmB,EACnB,mBAAmB,EACnB,WAAW,EACX,qBAAqB,EACrB,YAAY,EACZ,sBAAsB,EACtB,cAAc,EACd,wBAAwB,EACxB,gBAAgB,EAChB,uBAAuB,EACvB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,aAAa,EACb,eAAe,GAChB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAEnE,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,YAAY,EACV,qBAAqB,EACrB,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,yBAAyB,EACzB,wBAAwB,EACxB,6BAA6B,EAC7B,oBAAoB,EACpB,qBAAqB,EACrB,yBAAyB,EACzB,4BAA4B,EAC5B,qBAAqB,EACrB,gBAAgB,EAChB,uBAAuB,EACvB,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACrB,wBAAwB,EACxB,6BAA6B,EAC7B,gBAAgB,EAChB,uBAAuB,EACvB,iCAAiC,EACjC,8BAA8B,EAC9B,WAAW,GACZ,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,uCAAuC,EACvC,0CAA0C,EAC1C,sBAAsB,EACtB,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,EACpB,sBAAsB,EACtB,uBAAuB,EACvB,KAAK,sBAAsB,EAC3B,KAAK,6BAA6B,EAClC,oBAAoB,EACpB,KAAK,2BAA2B,EAChC,KAAK,wBAAwB,EAC7B,gBAAgB,EAChB,wCAAwC,EACxC,KAAK,kBAAkB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,cAAc,EACd,eAAe,EACf,qBAAqB,EACrB,4BAA4B,EAC5B,UAAU,EACV,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,oBAAoB,EACpB,WAAW,EACX,kBAAkB,EAClB,UAAU,EACV,cAAc,EACd,sBAAsB,EACtB,8BAA8B,EAC9B,2BAA2B,EAC3B,kBAAkB,EAClB,uBAAuB,EACvB,0BAA0B,EAC1B,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,yBAAyB,EACzB,qBAAqB,EACrB,YAAY,EACZ,gBAAgB,EAChB,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}