@tangle-network/agent-app 0.44.32 → 0.44.33

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.
@@ -12,6 +12,8 @@ import '../agent-activity-C8ZG0F0M.js';
12
12
  import '../flow-types-CJxEmaRy.js';
13
13
  import '../queue-VTBA5ONX.js';
14
14
  import '../sandbox-terminal-ChNEdHF8.js';
15
+ import '../billing-BibxgALe.js';
16
+ import '../billing/index.js';
15
17
  import '../catalog/index.js';
16
18
  import '../harness/index.js';
17
19
  import '../attachment-validation-CNkH91Gs.js';
@@ -3,7 +3,7 @@ import {
3
3
  ChatMessages,
4
4
  ModelPicker,
5
5
  ProviderLogo
6
- } from "../chunk-GCH3BUAZ.js";
6
+ } from "../chunk-NDVTYHLN.js";
7
7
  import "../chunk-FBVLEGEG.js";
8
8
  import "../chunk-YTMKRL3L.js";
9
9
  import "../chunk-GEYACSFW.js";
@@ -0,0 +1,193 @@
1
+ import { PlatformIdentity, PlatformBillingClient } from './billing/index.js';
2
+
3
+ /**
4
+ * Platform billing HTTP transport + tier state for apps on the shared
5
+ * Tangle balance model (id.tangle.tools). Reads authenticate as the user via
6
+ * their per-user platform key (the platform resolves the caller from the
7
+ * key; service or impersonation headers on read routes are rejected). The
8
+ * deduct write authenticates as the product service (`Bearer <serviceToken>`
9
+ * + `X-Service-Name`) and names the target user in the body. Also provides a
10
+ * fetch-backed implementation of the `/billing` module's
11
+ * `PlatformBillingClient` seam (type-only import — no runtime coupling).
12
+ */
13
+
14
+ /** Define available subscription tiers for the TanglePlan service */
15
+ type TanglePlanTier = 'free' | 'pro' | 'enterprise';
16
+ /** 'pro' | 'enterprise' pass through; anything else (null, unknown) → 'free'. */
17
+ declare function normalizeTanglePlanTier(plan: string | null | undefined): TanglePlanTier;
18
+ /** Represent platform billing HTTP errors with status code and detailed message */
19
+ declare class PlatformBillingHttpError extends Error {
20
+ readonly status: number;
21
+ constructor(status: number, detail: string);
22
+ }
23
+ /** Structural guard (name + numeric status) — robust across module instances. */
24
+ declare function isPlatformBillingHttpError(error: unknown): error is PlatformBillingHttpError;
25
+ /** Define HTTP options for platform billing including base URL, service token, product slug, fetch implementation, and timeout */
26
+ interface PlatformBillingHttpOptions {
27
+ /** Platform root, e.g. https://id.tangle.tools (trailing slashes stripped). */
28
+ baseUrl: string;
29
+ /** Used only by `deduct()`; resolved lazily so reads never require it.
30
+ * Throws at call time when empty. */
31
+ serviceToken: string | (() => string);
32
+ /** Product slug — the `X-Service-Name` header and the deduct `product` field. */
33
+ productSlug: string;
34
+ fetchImpl?: typeof fetch;
35
+ /** Default 10 000. */
36
+ timeoutMs?: number;
37
+ }
38
+ /** Describe subscription tier and status information for a platform user */
39
+ interface PlatformSubscriptionInfo {
40
+ tier: TanglePlanTier;
41
+ status: string | null;
42
+ }
43
+ /** Describe the platform balance and lifetime spending with an optional update timestamp */
44
+ interface PlatformBalanceSnapshot {
45
+ balance: number;
46
+ lifetimeSpent: number;
47
+ updatedAt?: string;
48
+ }
49
+ /** Describe a product's usage and spending metrics on the platform */
50
+ interface PlatformUsageProductRow {
51
+ product: string | null;
52
+ totalSpent: number;
53
+ count: number;
54
+ }
55
+ /** Lifecycle of a per-product seat subscription, mirroring the Stripe states
56
+ * the platform persists. 'none' = the user has never held this seat. */
57
+ type SeatStatus = 'none' | 'active' | 'trialing' | 'past_due' | 'canceled';
58
+ /** Price and included shared-wallet credit for one seat billing period. */
59
+ interface ProductSeatOfferPeriod {
60
+ priceCents: number;
61
+ includedCreditsCents: number;
62
+ }
63
+ /** Commercial terms returned by the platform's product catalog. Products use
64
+ * this exact object for display instead of duplicating prices in UI copy. */
65
+ interface ProductSeatOffer {
66
+ currency: 'usd';
67
+ interval: 'month';
68
+ recurring: ProductSeatOfferPeriod;
69
+ introductory: ProductSeatOfferPeriod | null;
70
+ }
71
+ /**
72
+ * Per-product entitlement snapshot from the platform — the single read that
73
+ * tells a product whether to show its workspace or the seat paywall. Shape
74
+ * matches `GET /v1/billing/product-entitlement?product=<id>`.
75
+ *
76
+ * `hasSeat` and `onFreeTier` are computed platform-side from the raw seat row
77
+ * + cumulative spend so the access rule is identical across products:
78
+ * - `hasSeat` — an active/trialing seat whose period has not lapsed.
79
+ * - `onFreeTier` — no active seat AND cumulative spend below the free cap
80
+ * ($2 / 200¢ lifetime). Keys off lifetime spend, not wallet
81
+ * balance, so a router top-up never re-opens free access.
82
+ */
83
+ interface ProductEntitlement {
84
+ seatStatus: SeatStatus;
85
+ /** ISO timestamp the active seat's paid period runs until; null when none. */
86
+ currentPeriodEnd: string | null;
87
+ /** Cumulative inference spend across the whole suite, in dollars. */
88
+ lifetimeSpentUsd: number;
89
+ hasSeat: boolean;
90
+ onFreeTier: boolean;
91
+ /** Present when the platform exposes catalog-backed commercial terms. */
92
+ offer?: ProductSeatOffer;
93
+ }
94
+ /** Define methods to interact with platform billing endpoints using user or service authentication */
95
+ interface PlatformBillingHttp {
96
+ /** GET /v1/plans/current (user bearer). */
97
+ getSubscription(userApiKey: string): Promise<PlatformSubscriptionInfo>;
98
+ /** GET /v1/billing/balance (user bearer). */
99
+ getBalance(userApiKey: string): Promise<PlatformBalanceSnapshot>;
100
+ /** GET /v1/billing/usage (user bearer). */
101
+ getUsageByProduct(userApiKey: string): Promise<PlatformUsageProductRow[]>;
102
+ /** GET /v1/billing/product-entitlement?product=<id> (user bearer). */
103
+ getProductEntitlement(userApiKey: string, productId: string): Promise<ProductEntitlement>;
104
+ /** POST /v1/billing/deduct (service token). */
105
+ deduct(input: {
106
+ platformUserId: string;
107
+ amountUsd: number;
108
+ type: string;
109
+ description: string;
110
+ referenceId: string;
111
+ }): Promise<void>;
112
+ /** Absolute URL of the platform's billing-management surface. */
113
+ billingUrl(): string;
114
+ /** Absolute URL of the catalog-backed seat checkout for `productId`. */
115
+ seatCheckoutUrl(productId: string): string;
116
+ }
117
+ /** Create a PlatformBillingHttp instance configured with given options and default behaviors */
118
+ declare function createPlatformBillingHttp(opts: PlatformBillingHttpOptions): PlatformBillingHttp;
119
+ /**
120
+ * Platform Stripe checkout URL for a product's catalog-backed seat. The
121
+ * platform resolves the product-specific price and introductory offer from the
122
+ * `product` query param. Mirrors the `billingUrl()` shape — a deterministic
123
+ * platform-rooted URL with no client-side pricing decisions.
124
+ */
125
+ declare function seatCheckoutUrl(baseUrl: string, productId: string): string;
126
+ /** Define policy settings for concurrency and overage allowance in a tangle tier */
127
+ interface TangleTierPolicy {
128
+ concurrency: number;
129
+ overageAllowed: boolean;
130
+ }
131
+ /** Define default concurrency and overage policies for each TanglePlanTier level */
132
+ declare const DEFAULT_TANGLE_TIER_POLICY: Record<TanglePlanTier, TangleTierPolicy>;
133
+ /** Describe the state of a Tangle plan tier including subscription, balance, spending, and concurrency details */
134
+ interface TangleTierState {
135
+ tier: TanglePlanTier;
136
+ subscriptionStatus: string | null;
137
+ remainingBalanceUsd: number;
138
+ lifetimeSpentUsd: number;
139
+ concurrency: number;
140
+ overageAllowed: boolean;
141
+ }
142
+ /**
143
+ * Read subscription + balance and project them onto the tier policy. A
144
+ * null/absent key fails CLOSED (free tier, zero balance) — a billable run is
145
+ * never started against an unknown balance. Platform errors throw; callers
146
+ * on the billable path choose their posture explicitly.
147
+ */
148
+ declare function readTangleTierState(http: PlatformBillingHttp, userApiKey: string | null | undefined, policy?: Record<TanglePlanTier, TangleTierPolicy>): Promise<TangleTierState>;
149
+ /** Lifetime free-tier cap: $2 (200¢) cumulative inference spend, expressed in
150
+ * dollars. Free product access ends once cumulative spend crosses this. */
151
+ declare const FREE_TIER_SPEND_CAP_USD = 2;
152
+ /**
153
+ * Default name of the per-app feature flag gating seat billing. While OFF the
154
+ * entitlement read is skipped and access fails OPEN (entitled) so nothing
155
+ * changes live until a product flips the flag.
156
+ */
157
+ declare const DEFAULT_SEAT_BILLING_ENABLED_ENV_VAR = "SEAT_BILLING_ENABLED";
158
+ /** Define options to configure seat billing flag environment variables and override flag name */
159
+ interface SeatBillingFlagOptions {
160
+ env?: Record<string, string | undefined>;
161
+ /** Override the flag name; default {@link DEFAULT_SEAT_BILLING_ENABLED_ENV_VAR}. */
162
+ flagEnvVar?: string;
163
+ }
164
+ /**
165
+ * Seat billing is OFF unless the flag is explicitly truthy ('true'/'1'/'on'/
166
+ * 'enabled'). Default OFF — pre-rollout, the paywall never engages. Returns
167
+ * false when no env is available (browser bundles) so the client stays
168
+ * fail-open there too.
169
+ */
170
+ declare function isSeatBillingEnabled(opts?: SeatBillingFlagOptions): boolean;
171
+ /**
172
+ * Read a user's entitlement for one product. Fails OPEN: an absent key,
173
+ * disabled flag, or unreachable seat endpoint all return a permissive snapshot
174
+ * (`hasSeat: true`) so consumers never break pre-rollout. The platform owns the
175
+ * `hasSeat`/`onFreeTier` computation; this client only transports + degrades
176
+ * safely.
177
+ *
178
+ * @param flag — pass {@link isSeatBillingEnabled} (or your own boolean) so the
179
+ * product owns when the gate engages. When false, no network call is made.
180
+ */
181
+ declare function getProductEntitlement(http: Pick<PlatformBillingHttp, 'getProductEntitlement'>, userApiKey: string | null | undefined, productId: string, flag?: boolean): Promise<ProductEntitlement>;
182
+ /** Entitled = holds an active seat OR is still inside the free tier. The one
183
+ * predicate every product uses. */
184
+ declare function isProductEntitled(ent: ProductEntitlement): boolean;
185
+ /** Define a contract for resolving platform identities based on user identifiers */
186
+ interface PlatformIdentityStore {
187
+ resolveIdentity(userId: string): Promise<PlatformIdentity | null>;
188
+ }
189
+ /** Concrete fetch-backed `PlatformBillingClient<TanglePlanTier>` for
190
+ * `createPlatformBalanceManager` (from `/billing`). */
191
+ declare function createTanglePlatformBillingClient(http: PlatformBillingHttp, identity: PlatformIdentityStore): PlatformBillingClient<TanglePlanTier>;
192
+
193
+ export { DEFAULT_SEAT_BILLING_ENABLED_ENV_VAR as D, FREE_TIER_SPEND_CAP_USD as F, type PlatformBalanceSnapshot as P, type SeatBillingFlagOptions as S, type TanglePlanTier as T, DEFAULT_TANGLE_TIER_POLICY as a, type PlatformBillingHttp as b, PlatformBillingHttpError as c, type PlatformBillingHttpOptions as d, type PlatformIdentityStore as e, type PlatformSubscriptionInfo as f, type PlatformUsageProductRow as g, type ProductEntitlement as h, type ProductSeatOffer as i, type ProductSeatOfferPeriod as j, type SeatStatus as k, type TangleTierPolicy as l, type TangleTierState as m, createPlatformBillingHttp as n, createTanglePlatformBillingClient as o, getProductEntitlement as p, isPlatformBillingHttpError as q, isProductEntitled as r, isSeatBillingEnabled as s, normalizeTanglePlanTier as t, readTangleTierState as u, seatCheckoutUrl as v };
@@ -2687,7 +2687,15 @@ function AgentActivityPanel({ fetchActivity, renderMissionRef, title = "Agent ac
2687
2687
  }
2688
2688
 
2689
2689
  // src/web-react/seat-paywall.tsx
2690
- import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
2690
+ import { Fragment as Fragment5, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
2691
+ function usd(cents) {
2692
+ return new Intl.NumberFormat("en-US", {
2693
+ style: "currency",
2694
+ currency: "USD",
2695
+ minimumFractionDigits: cents % 100 === 0 ? 0 : 2,
2696
+ maximumFractionDigits: cents % 100 === 0 ? 0 : 2
2697
+ }).format(cents / 100);
2698
+ }
2691
2699
  function CheckGlyph3() {
2692
2700
  return /* @__PURE__ */ jsx11(
2693
2701
  "svg",
@@ -2715,12 +2723,16 @@ function SeatPaywall({
2715
2723
  onCheckout,
2716
2724
  priceUsd = 100,
2717
2725
  includedUsageUsd = 50,
2726
+ offer,
2718
2727
  tagline,
2719
2728
  ctaLabel,
2720
2729
  benefits,
2721
2730
  footnote
2722
2731
  }) {
2723
2732
  const { pending, run } = usePending();
2733
+ const recurringPrice = offer ? usd(offer.recurring.priceCents) : `$${priceUsd}`;
2734
+ const recurringUsage = offer ? usd(offer.recurring.includedCreditsCents) : `$${includedUsageUsd}`;
2735
+ const introductory = offer?.introductory ?? null;
2724
2736
  return /* @__PURE__ */ jsx11("div", { className: "flex min-h-[60vh] w-full items-center justify-center p-6", children: /* @__PURE__ */ jsxs9("div", { className: "w-full max-w-md rounded-2xl border border-border bg-card p-8 shadow-sm", children: [
2725
2737
  /* @__PURE__ */ jsx11("p", { className: "text-xs font-medium uppercase tracking-wide text-muted-foreground", children: product }),
2726
2738
  /* @__PURE__ */ jsxs9("h1", { className: "mt-2 text-2xl font-semibold tracking-tight text-foreground", children: [
@@ -2728,21 +2740,37 @@ function SeatPaywall({
2728
2740
  product
2729
2741
  ] }),
2730
2742
  tagline && /* @__PURE__ */ jsx11("p", { className: "mt-2 text-sm text-muted-foreground", children: tagline }),
2731
- /* @__PURE__ */ jsxs9("div", { className: "mt-6 flex items-baseline gap-1.5", children: [
2732
- /* @__PURE__ */ jsxs9("span", { className: "text-3xl font-semibold text-foreground", children: [
2733
- "$",
2734
- priceUsd
2743
+ introductory ? /* @__PURE__ */ jsxs9(Fragment5, { children: [
2744
+ /* @__PURE__ */ jsxs9("div", { className: "mt-6 flex items-baseline gap-1.5", children: [
2745
+ /* @__PURE__ */ jsx11("span", { className: "text-3xl font-semibold text-foreground", children: usd(introductory.priceCents) }),
2746
+ /* @__PURE__ */ jsx11("span", { className: "text-sm text-muted-foreground", children: "first month" })
2735
2747
  ] }),
2736
- /* @__PURE__ */ jsx11("span", { className: "text-sm text-muted-foreground", children: "/mo" })
2737
- ] }),
2738
- /* @__PURE__ */ jsxs9("p", { className: "mt-1 text-sm text-muted-foreground", children: [
2739
- "Includes $",
2740
- includedUsageUsd,
2741
- "/mo of AI usage"
2748
+ /* @__PURE__ */ jsxs9("p", { className: "mt-1 text-sm text-muted-foreground", children: [
2749
+ "Includes ",
2750
+ usd(introductory.includedCreditsCents),
2751
+ " of AI usage in your first month"
2752
+ ] }),
2753
+ /* @__PURE__ */ jsxs9("p", { className: "mt-1 text-sm text-muted-foreground", children: [
2754
+ "Then ",
2755
+ recurringPrice,
2756
+ "/mo \xB7 includes ",
2757
+ recurringUsage,
2758
+ "/mo of AI usage"
2759
+ ] })
2760
+ ] }) : /* @__PURE__ */ jsxs9(Fragment5, { children: [
2761
+ /* @__PURE__ */ jsxs9("div", { className: "mt-6 flex items-baseline gap-1.5", children: [
2762
+ /* @__PURE__ */ jsx11("span", { className: "text-3xl font-semibold text-foreground", children: recurringPrice }),
2763
+ /* @__PURE__ */ jsx11("span", { className: "text-sm text-muted-foreground", children: "/mo" })
2764
+ ] }),
2765
+ /* @__PURE__ */ jsxs9("p", { className: "mt-1 text-sm text-muted-foreground", children: [
2766
+ "Includes ",
2767
+ recurringUsage,
2768
+ "/mo of AI usage"
2769
+ ] })
2742
2770
  ] }),
2743
2771
  /* @__PURE__ */ jsx11("ul", { className: "mt-6 space-y-2.5", children: (benefits ?? [
2744
2772
  `Full access to ${product}`,
2745
- `$${includedUsageUsd}/mo of AI usage included, every month`
2773
+ `${recurringUsage}/mo of AI usage included`
2746
2774
  ]).map((benefit, i) => /* @__PURE__ */ jsx11(Benefit, { children: benefit }, i)) }),
2747
2775
  /* @__PURE__ */ jsx11(
2748
2776
  "button",
@@ -2913,7 +2941,7 @@ function AgentSessionControls(props) {
2913
2941
  }
2914
2942
 
2915
2943
  // src/web-react/index.tsx
2916
- import { Fragment as Fragment5, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
2944
+ import { Fragment as Fragment6, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
2917
2945
  function formatModelCost(msg, models) {
2918
2946
  if (msg.promptTokens == null && msg.completionTokens == null) return null;
2919
2947
  const pricing = models.find((m) => m.id === msg.modelUsed)?.pricing;
@@ -3143,7 +3171,7 @@ function ProposalCard({
3143
3171
  ] })
3144
3172
  ] }),
3145
3173
  /* @__PURE__ */ jsxs11("div", { className: "flex flex-wrap items-center gap-2 px-4 pb-3.5 pt-3", children: [
3146
- approval ? /* @__PURE__ */ jsxs11(Fragment5, { children: [
3174
+ approval ? /* @__PURE__ */ jsxs11(Fragment6, { children: [
3147
3175
  /* @__PURE__ */ jsx13(
3148
3176
  "button",
3149
3177
  {
@@ -3438,7 +3466,7 @@ function AssistantMessageImpl({
3438
3466
  onToolCallClick,
3439
3467
  toolRenderers
3440
3468
  }
3441
- ) : /* @__PURE__ */ jsxs11(Fragment5, { children: [
3469
+ ) : /* @__PURE__ */ jsxs11(Fragment6, { children: [
3442
3470
  /* @__PURE__ */ jsxs11("div", { className: "text-base leading-[1.75]", children: [
3443
3471
  body,
3444
3472
  streaming && content && !msg.toolCalls?.length && /* @__PURE__ */ jsx13(StreamingCaret, {})
@@ -3551,12 +3579,12 @@ function ChatMessages({
3551
3579
  const lastIsUser = messages[messages.length - 1]?.role === "user";
3552
3580
  if (messages.length === 0 && !loading && !error) {
3553
3581
  const empty = renderEmpty ? renderEmpty() : /* @__PURE__ */ jsx13(ChatEmptyState, { ...emptyState });
3554
- return /* @__PURE__ */ jsxs11(Fragment5, { children: [
3582
+ return /* @__PURE__ */ jsxs11(Fragment6, { children: [
3555
3583
  header,
3556
3584
  empty
3557
3585
  ] });
3558
3586
  }
3559
- return /* @__PURE__ */ jsxs11(Fragment5, { children: [
3587
+ return /* @__PURE__ */ jsxs11(Fragment6, { children: [
3560
3588
  header,
3561
3589
  messages.map(
3562
3590
  (msg) => msg.role === "user" ? /* @__PURE__ */ jsx13("div", { className: "mx-auto w-full max-w-3xl px-6 py-3", children: /* @__PURE__ */ jsxs11("div", { className: "ml-auto w-fit max-w-[85%]", children: [
@@ -3670,4 +3698,4 @@ export {
3670
3698
  useThinkingSeconds,
3671
3699
  ChatMessages
3672
3700
  };
3673
- //# sourceMappingURL=chunk-GCH3BUAZ.js.map
3701
+ //# sourceMappingURL=chunk-NDVTYHLN.js.map