@oxygen-agent/cli 1.750.4 → 1.782.1
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/README.md +1 -1
- package/dist/command-manifest.js +11 -1
- package/dist/help.js +8 -0
- package/dist/index.js +492 -96
- package/node_modules/@oxygen/shared/dist/billing.d.ts +88 -46
- package/node_modules/@oxygen/shared/dist/billing.js +134 -74
- package/node_modules/@oxygen/shared/dist/capability-discovery.js +12 -4
- package/node_modules/@oxygen/shared/dist/cli-result.js +1 -0
- package/node_modules/@oxygen/shared/dist/copilot-journeys.d.ts +8 -0
- package/node_modules/@oxygen/shared/dist/copilot-journeys.js +23 -5
- package/node_modules/@oxygen/shared/dist/future-signup-events.d.ts +13 -2
- package/node_modules/@oxygen/shared/dist/future-signup-events.js +17 -2
- package/node_modules/@oxygen/shared/dist/future-signup-lifecycle-projection.d.ts +106 -1
- package/node_modules/@oxygen/shared/dist/future-signup-lifecycle-projection.js +156 -45
- package/node_modules/@oxygen/shared/dist/index.d.ts +5 -0
- package/node_modules/@oxygen/shared/dist/index.js +5 -0
- package/node_modules/@oxygen/shared/dist/object-storage.d.ts +33 -0
- package/node_modules/@oxygen/shared/dist/object-storage.js +69 -4
- package/node_modules/@oxygen/shared/dist/person-name.d.ts +40 -0
- package/node_modules/@oxygen/shared/dist/person-name.js +23 -0
- package/node_modules/@oxygen/shared/dist/plan-capabilities.d.ts +82 -0
- package/node_modules/@oxygen/shared/dist/plan-capabilities.js +130 -0
- package/node_modules/@oxygen/shared/dist/plan-limits.d.ts +2 -2
- package/node_modules/@oxygen/shared/dist/plan-limits.js +18 -2
- package/node_modules/@oxygen/shared/dist/pricing-sheet.d.ts +50 -56
- package/node_modules/@oxygen/shared/dist/pricing-sheet.js +77 -90
- package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.d.ts +62 -0
- package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.js +91 -0
- package/node_modules/@oxygen/shared/dist/provider-funding-errors.d.ts +44 -0
- package/node_modules/@oxygen/shared/dist/provider-funding-errors.js +81 -0
- package/node_modules/@oxygen/shared/dist/publishing-limits.d.ts +24 -0
- package/node_modules/@oxygen/shared/dist/publishing-limits.js +24 -0
- package/node_modules/@oxygen/shared/dist/spend-safety.d.ts +27 -6
- package/node_modules/@oxygen/shared/dist/spend-safety.js +34 -6
- package/node_modules/@oxygen/shared/dist/table-capacity.d.ts +13 -0
- package/node_modules/@oxygen/shared/dist/table-capacity.js +14 -0
- package/node_modules/@oxygen/shared/dist/version.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.js +6 -3
- package/node_modules/@oxygen/shared/package.json +10 -0
- package/node_modules/@oxygen/workflows/dist/graph/diff.d.ts +33 -0
- package/node_modules/@oxygen/workflows/dist/graph/diff.js +75 -0
- package/node_modules/@oxygen/workflows/dist/graph/expression.js +17 -1
- package/node_modules/@oxygen/workflows/dist/graph/index.d.ts +1 -0
- package/node_modules/@oxygen/workflows/dist/graph/index.js +1 -0
- package/node_modules/@oxygen/workflows/dist/graph/lint.js +30 -28
- package/node_modules/@oxygen/workflows/dist/graph/manifest-schema.d.ts +2 -2
- package/node_modules/@oxygen/workflows/dist/graph/manifest-schema.js +1 -1
- package/node_modules/@oxygen/workflows/dist/graph/remap.js +0 -5
- package/node_modules/@oxygen/workflows/dist/graph/topology.d.ts +27 -0
- package/node_modules/@oxygen/workflows/dist/graph/topology.js +95 -0
- package/node_modules/@oxygen/workflows/dist/graph/types.d.ts +27 -8
- package/node_modules/@oxygen/workflows/dist/graph/types.js +2 -4
- package/node_modules/@oxygen/workflows/dist/index.js +0 -1
- package/node_modules/@oxygen/workflows/dist/portable.js +0 -9
- package/package.json +1 -1
|
@@ -111,102 +111,142 @@ export type CreditTopupPackId = CreditTopupPack["id"];
|
|
|
111
111
|
export declare function resolveCreditTopupPack(id: string | null | undefined): CreditTopupPack | null;
|
|
112
112
|
export declare const TRIAL_PERIOD_DAYS = 7;
|
|
113
113
|
export declare const TRIAL_CREDIT_GRANT = 20000;
|
|
114
|
+
/**
|
|
115
|
+
* Free-tier entry (founder-ratified 2026-08-16, grant shape settled 2026-08-17).
|
|
116
|
+
*
|
|
117
|
+
* Two ONE-TIME grants and no monthly trickle. The free tier's *surface* is
|
|
118
|
+
* unmetered — CRM, Tables, Workflows, Knowledge, Agents, Copilot, Sequence
|
|
119
|
+
* authoring cost nothing marginal, so they are not rationed — and credits meter
|
|
120
|
+
* only real per-call spend. That is why `BASE_PRICING_PLANS.free` still carries
|
|
121
|
+
* `monthlyCredits: 0` and `rolloverCap: null`: 0 is the ANSWER (no monthly
|
|
122
|
+
* allowance exists), not a missing value, and null still means "never claw back
|
|
123
|
+
* a leftover balance".
|
|
124
|
+
*
|
|
125
|
+
* The split exists for a specific reason. Granting only on CLI/MCP activation
|
|
126
|
+
* would ship a Workspace Copilot with no funds for exactly the users who chose
|
|
127
|
+
* the in-product path — the default Copilot session budget is 5,000 credits, so
|
|
128
|
+
* the signup grant is precisely one session. The activation half is the pull
|
|
129
|
+
* toward the agent-native surface, and rewards the same action the Company KPI
|
|
130
|
+
* scorecard already counts as activation.
|
|
131
|
+
*
|
|
132
|
+
* Ceiling is therefore 10,000 credits ($10) per billing-owner organization,
|
|
133
|
+
* ever. Neither grant is minted for a churned or downgraded workspace, and no
|
|
134
|
+
* other path re-mints either one — not downgrade, churn, re-subscribe, or
|
|
135
|
+
* billing-owner re-link. Top-ups stay purchasable on free, which is what keeps
|
|
136
|
+
* the metered surface open-ended rather than capped at the grant.
|
|
137
|
+
*/
|
|
138
|
+
export declare const FREE_SIGNUP_GRANT_CREDITS = 5000;
|
|
139
|
+
export declare const FREE_ACTIVATION_GRANT_CREDITS = 5000;
|
|
140
|
+
export declare const FREE_TIER_GRANTS_ENABLED_ENV_VAR = "OXYGEN_FREE_TIER_GRANTS_ENABLED";
|
|
141
|
+
/**
|
|
142
|
+
* Fail-closed rollout switch for the free-tier grants, matching the staged
|
|
143
|
+
* pattern used by CREDIT_COMMITMENTS_ENABLED.
|
|
144
|
+
*
|
|
145
|
+
* UNSET MEANS OFF, which is the opposite of the InboxKit-style kill switches and
|
|
146
|
+
* is deliberate: this flag mints credits. Until the rest of the free tier ships
|
|
147
|
+
* — the capability-scoped entitlement of OXP-10.3 and the card-less signup of
|
|
148
|
+
* OXP-10.4 — an armed grant would fund workspaces that still hit the binary
|
|
149
|
+
* subscription wall, which is money spent on an experience that does not work
|
|
150
|
+
* yet. It also means the grant cannot fire for the existing organization
|
|
151
|
+
* population before the cutover posture is decided.
|
|
152
|
+
*/
|
|
153
|
+
export declare function freeTierGrantsEnabled(env?: Record<string, string | undefined>): boolean;
|
|
114
154
|
export declare const WALKTHROUGH_COMPLETION_BONUS_CREDITS = 1000;
|
|
115
155
|
export declare const WALKTHROUGH_SCRAPE_MAX_CREDITS = 2000;
|
|
116
156
|
export declare const BASE_PRICING_PLANS: {
|
|
117
157
|
readonly free: {
|
|
118
|
-
readonly tier: "free";
|
|
119
|
-
readonly name: "Free";
|
|
120
|
-
readonly monthlyPriceCents: 0;
|
|
121
|
-
readonly monthlyCredits: 0;
|
|
122
|
-
readonly weeklyCreditsLimit: null;
|
|
123
|
-
readonly rolloverCap: null;
|
|
124
158
|
readonly monthlyAutomationActions: null;
|
|
125
159
|
readonly automationOverageCentsPerMillion: null;
|
|
126
160
|
readonly automationOverageEnabledDefault: false;
|
|
127
|
-
readonly
|
|
128
|
-
readonly
|
|
129
|
-
readonly
|
|
130
|
-
readonly
|
|
161
|
+
readonly description: "Your CRM, tables, and workflows — free forever. Credits pay for enrichment and AI; connecting a sender, publishing, managed email infrastructure, and your own provider keys need a plan.";
|
|
162
|
+
readonly ctaLabel: "Upgrade";
|
|
163
|
+
readonly features: readonly ["CRM, Tables, Workflows, and Knowledge — unmetered", "Sequences: author, preview, and enroll", "Agents and Workspace Copilot", "Credits for enrichment and AI, topped up any time"];
|
|
164
|
+
readonly monthlyPriceCents: number | null;
|
|
165
|
+
readonly monthlyCredits: number | null;
|
|
166
|
+
readonly weeklyCreditsLimit: number | null;
|
|
167
|
+
readonly rolloverCap: number | null;
|
|
168
|
+
readonly byokEnabled: boolean;
|
|
169
|
+
readonly tier: "free";
|
|
170
|
+
readonly name: "Free";
|
|
131
171
|
};
|
|
132
172
|
readonly starter: {
|
|
133
|
-
readonly tier: "starter";
|
|
134
|
-
readonly name: "Starter";
|
|
135
|
-
readonly monthlyPriceCents: 9900;
|
|
136
|
-
readonly monthlyCredits: 99000;
|
|
137
|
-
readonly weeklyCreditsLimit: null;
|
|
138
|
-
readonly rolloverCap: 99000;
|
|
139
173
|
readonly monthlyAutomationActions: null;
|
|
140
174
|
readonly automationOverageCentsPerMillion: null;
|
|
141
175
|
readonly automationOverageEnabledDefault: false;
|
|
142
|
-
readonly byokEnabled: true;
|
|
143
176
|
readonly description: "For founders running focused managed enrichment and AI columns.";
|
|
144
177
|
readonly ctaLabel: "Choose Starter";
|
|
145
178
|
readonly features: readonly ["99,000 credits every month", "All integrations", "Workflows", "Bring your own keys", "Standard support"];
|
|
179
|
+
readonly monthlyPriceCents: number | null;
|
|
180
|
+
readonly monthlyCredits: number | null;
|
|
181
|
+
readonly weeklyCreditsLimit: number | null;
|
|
182
|
+
readonly rolloverCap: number | null;
|
|
183
|
+
readonly byokEnabled: boolean;
|
|
184
|
+
readonly tier: "starter";
|
|
185
|
+
readonly name: "Starter";
|
|
146
186
|
};
|
|
147
187
|
readonly pro: {
|
|
148
|
-
readonly tier: "pro";
|
|
149
|
-
readonly name: "Pro";
|
|
150
|
-
readonly monthlyPriceCents: 24900;
|
|
151
|
-
readonly monthlyCredits: 249000;
|
|
152
|
-
readonly weeklyCreditsLimit: null;
|
|
153
|
-
readonly rolloverCap: 249000;
|
|
154
188
|
readonly monthlyAutomationActions: null;
|
|
155
189
|
readonly automationOverageCentsPerMillion: null;
|
|
156
190
|
readonly automationOverageEnabledDefault: false;
|
|
157
|
-
readonly byokEnabled: true;
|
|
158
191
|
readonly description: "For recurring GTM enrichment and AI-column workflows on managed credits.";
|
|
159
192
|
readonly badge: "Most popular";
|
|
160
193
|
readonly highlighted: true;
|
|
161
194
|
readonly ctaLabel: "Choose Pro";
|
|
162
195
|
readonly features: readonly ["249,000 credits every month", "All integrations", "Workflows", "Bring your own keys", "Standard support"];
|
|
196
|
+
readonly monthlyPriceCents: number | null;
|
|
197
|
+
readonly monthlyCredits: number | null;
|
|
198
|
+
readonly weeklyCreditsLimit: number | null;
|
|
199
|
+
readonly rolloverCap: number | null;
|
|
200
|
+
readonly byokEnabled: boolean;
|
|
201
|
+
readonly tier: "pro";
|
|
202
|
+
readonly name: "Pro";
|
|
163
203
|
};
|
|
164
204
|
readonly team: {
|
|
165
|
-
readonly tier: "team";
|
|
166
|
-
readonly name: "Team";
|
|
167
|
-
readonly monthlyPriceCents: 74900;
|
|
168
|
-
readonly monthlyCredits: 749000;
|
|
169
|
-
readonly weeklyCreditsLimit: null;
|
|
170
|
-
readonly rolloverCap: 749000;
|
|
171
205
|
readonly monthlyAutomationActions: null;
|
|
172
206
|
readonly automationOverageCentsPerMillion: null;
|
|
173
207
|
readonly automationOverageEnabledDefault: false;
|
|
174
|
-
readonly byokEnabled: true;
|
|
175
208
|
readonly description: "For small GTM teams and agencies running shared workflows with their own AI keys.";
|
|
176
209
|
readonly ctaLabel: "Choose Team";
|
|
177
210
|
readonly features: readonly ["749,000 credits every month", "All integrations", "Workflows", "Bring your own keys", "Premium support"];
|
|
211
|
+
readonly monthlyPriceCents: number | null;
|
|
212
|
+
readonly monthlyCredits: number | null;
|
|
213
|
+
readonly weeklyCreditsLimit: number | null;
|
|
214
|
+
readonly rolloverCap: number | null;
|
|
215
|
+
readonly byokEnabled: boolean;
|
|
216
|
+
readonly tier: "team";
|
|
217
|
+
readonly name: "Team";
|
|
178
218
|
};
|
|
179
219
|
readonly scale: {
|
|
180
|
-
readonly tier: "scale";
|
|
181
|
-
readonly name: "Scale";
|
|
182
|
-
readonly monthlyPriceCents: null;
|
|
183
|
-
readonly monthlyCredits: 10000000;
|
|
184
|
-
readonly weeklyCreditsLimit: null;
|
|
185
|
-
readonly rolloverCap: 10000000;
|
|
186
220
|
readonly monthlyAutomationActions: null;
|
|
187
221
|
readonly automationOverageCentsPerMillion: null;
|
|
188
222
|
readonly automationOverageEnabledDefault: false;
|
|
189
|
-
readonly byokEnabled: true;
|
|
190
223
|
readonly description: "For high-volume GTM teams that need custom limits and rollout support.";
|
|
191
224
|
readonly ctaLabel: "Contact sales";
|
|
192
225
|
readonly contactHref: "https://cal.com/tim-scheuer-mxbib9/45";
|
|
193
226
|
readonly features: readonly ["10,000,000+ managed credits / month", "Workflows", "Phone number enrichment on managed credits", "Custom provider routing and BYOK policies", "Dedicated onboarding"];
|
|
227
|
+
readonly monthlyPriceCents: number | null;
|
|
228
|
+
readonly monthlyCredits: number | null;
|
|
229
|
+
readonly weeklyCreditsLimit: number | null;
|
|
230
|
+
readonly rolloverCap: number | null;
|
|
231
|
+
readonly byokEnabled: boolean;
|
|
232
|
+
readonly tier: "scale";
|
|
233
|
+
readonly name: "Scale";
|
|
194
234
|
};
|
|
195
235
|
readonly enterprise: {
|
|
196
|
-
readonly tier: "enterprise";
|
|
197
|
-
readonly name: "Enterprise";
|
|
198
|
-
readonly monthlyPriceCents: null;
|
|
199
|
-
readonly monthlyCredits: null;
|
|
200
|
-
readonly weeklyCreditsLimit: null;
|
|
201
|
-
readonly rolloverCap: null;
|
|
202
236
|
readonly monthlyAutomationActions: null;
|
|
203
237
|
readonly automationOverageCentsPerMillion: null;
|
|
204
238
|
readonly automationOverageEnabledDefault: false;
|
|
205
|
-
readonly byokEnabled: true;
|
|
206
239
|
readonly description: "Custom procurement, invoicing, limits, and rollout support.";
|
|
207
240
|
readonly ctaLabel: "Talk to us";
|
|
208
241
|
readonly contactHref: "https://cal.com/tim-scheuer-mxbib9/45";
|
|
209
242
|
readonly features: readonly ["Everything in Team", "Custom usage limits", "Volume pricing", "Custom invoicing and billing", "Dedicated support"];
|
|
243
|
+
readonly monthlyPriceCents: number | null;
|
|
244
|
+
readonly monthlyCredits: number | null;
|
|
245
|
+
readonly weeklyCreditsLimit: number | null;
|
|
246
|
+
readonly rolloverCap: number | null;
|
|
247
|
+
readonly byokEnabled: boolean;
|
|
248
|
+
readonly tier: "enterprise";
|
|
249
|
+
readonly name: "Enterprise";
|
|
210
250
|
};
|
|
211
251
|
};
|
|
212
252
|
export declare const PUBLIC_PLAN_ORDER: readonly PlanTier[];
|
|
@@ -217,7 +257,7 @@ export type ActiveSubscriptionStatus = (typeof ACTIVE_SUBSCRIPTION_STATUSES)[num
|
|
|
217
257
|
export declare const BILLING_VISIBLE_SUBSCRIPTION_STATUSES: readonly ["active", "trialing", "past_due", "unpaid", "paused", "incomplete"];
|
|
218
258
|
export type BillingVisibleSubscriptionStatus = (typeof BILLING_VISIBLE_SUBSCRIPTION_STATUSES)[number];
|
|
219
259
|
export declare const PAST_DUE_GRACE_DAYS = 7;
|
|
220
|
-
export type SubscriptionEntitlementReason = "active" | "trialing" | "past_due_grace" | "past_due_grace_expired" | "missing_past_due_period_start" | "trial_expired" | "missing_trial_end" | "unpaid" | "paused" | "incomplete" | "canceled" | "incomplete_expired" | "missing_subscription";
|
|
260
|
+
export type SubscriptionEntitlementReason = "active" | "trialing" | "past_due_grace" | "past_due_grace_expired" | "missing_past_due_period_start" | "trial_expired" | "missing_trial_end" | "unpaid" | "paused" | "incomplete" | "canceled" | "incomplete_expired" | "missing_subscription" | "free_tier";
|
|
221
261
|
export type SubscriptionEntitlement = {
|
|
222
262
|
entitled: boolean;
|
|
223
263
|
reason: SubscriptionEntitlementReason;
|
|
@@ -229,6 +269,8 @@ export declare function resolveSubscriptionEntitlement(input: {
|
|
|
229
269
|
currentPeriodEnd?: Date | null;
|
|
230
270
|
trialEnd?: Date | null;
|
|
231
271
|
now?: Date;
|
|
272
|
+
/** Overrides the env switch. Injected by tests; production reads the flag. */
|
|
273
|
+
freeTierEntitled?: boolean;
|
|
232
274
|
}): SubscriptionEntitlement;
|
|
233
275
|
export type ResolvedPricingPlan = PricingPlanDefinition;
|
|
234
276
|
export declare function resolveBasePricingPlan(tier: string | null | undefined): ResolvedPricingPlan | null;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { AUTOMATION_ACTION_CREDITS as SNAPSHOT_AUTOMATION_ACTION_CREDITS, CREDITS_PER_USD as SNAPSHOT_CREDITS_PER_USD, PRICING_PLAN_SNAPSHOT, TOPUP_USD_CENTS_PER_1K_CREDITS as SNAPSHOT_TOPUP_USD_CENTS_PER_1K_CREDITS, } from "./pricing-snapshot.generated.js";
|
|
2
|
+
import { freeTierEntitlementEnabled } from "./plan-capabilities.js";
|
|
1
3
|
export const WEEKLY_USAGE_WINDOW_DAYS = 7;
|
|
2
4
|
export const BILLING_CURRENCIES = ["usd"];
|
|
3
5
|
export const DEFAULT_BILLING_CURRENCY = "usd";
|
|
@@ -7,8 +9,13 @@ export const CONTACT_SALES_URL = "https://cal.com/tim-scheuer-mxbib9/45";
|
|
|
7
9
|
// exactly 1,000 credits — while on-demand top-up packs price the same credit
|
|
8
10
|
// at $1.25 per 1,000, making subscription credits exactly 20% cheaper than
|
|
9
11
|
// credits bought on demand.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
//
|
|
13
|
+
// Sourced from the generated snapshot rather than declared here: this pair was
|
|
14
|
+
// previously written down in seven production modules, and if any two ever
|
|
15
|
+
// disagreed every markup-derived price would be silently wrong by the ratio.
|
|
16
|
+
// Change them in packages/control-db/src/pricing/seed.ts.
|
|
17
|
+
export const CREDITS_PER_USD = SNAPSHOT_CREDITS_PER_USD;
|
|
18
|
+
export const TOPUP_USD_CENTS_PER_1K_CREDITS = SNAPSHOT_TOPUP_USD_CENTS_PER_1K_CREDITS;
|
|
12
19
|
// On-demand purchases start at the original fixed packs' $10 floor and run up
|
|
13
20
|
// to a $1,250 ceiling, in any 1,000-credit increment. The ceiling is a
|
|
14
21
|
// single-checkout guard rail, not a spend cap: a workspace that needs more
|
|
@@ -31,7 +38,8 @@ export function creditTopupUsdCents(credits) {
|
|
|
31
38
|
// Workflow/automation actions (steps, tool calls, row writes, retries) draw
|
|
32
39
|
// from the same credit pool at a flat capacity-guard rate — the old separate
|
|
33
40
|
// monthly action allowance + USD overage meter economy is retired (2026-07-14).
|
|
34
|
-
|
|
41
|
+
// Seed key: charge.automation.action.
|
|
42
|
+
export const AUTOMATION_ACTION_CREDITS = SNAPSHOT_AUTOMATION_ACTION_CREDITS;
|
|
35
43
|
// ---------------------------------------------------------------------------
|
|
36
44
|
// FIXED (committed) credit commitments — recurring per-resource monthly charges
|
|
37
45
|
// ---------------------------------------------------------------------------
|
|
@@ -152,6 +160,52 @@ export function resolveCreditTopupPack(id) {
|
|
|
152
160
|
// whichever comes first. Starter remains the signup default.
|
|
153
161
|
export const TRIAL_PERIOD_DAYS = 7;
|
|
154
162
|
export const TRIAL_CREDIT_GRANT = 20_000;
|
|
163
|
+
/**
|
|
164
|
+
* Free-tier entry (founder-ratified 2026-08-16, grant shape settled 2026-08-17).
|
|
165
|
+
*
|
|
166
|
+
* Two ONE-TIME grants and no monthly trickle. The free tier's *surface* is
|
|
167
|
+
* unmetered — CRM, Tables, Workflows, Knowledge, Agents, Copilot, Sequence
|
|
168
|
+
* authoring cost nothing marginal, so they are not rationed — and credits meter
|
|
169
|
+
* only real per-call spend. That is why `BASE_PRICING_PLANS.free` still carries
|
|
170
|
+
* `monthlyCredits: 0` and `rolloverCap: null`: 0 is the ANSWER (no monthly
|
|
171
|
+
* allowance exists), not a missing value, and null still means "never claw back
|
|
172
|
+
* a leftover balance".
|
|
173
|
+
*
|
|
174
|
+
* The split exists for a specific reason. Granting only on CLI/MCP activation
|
|
175
|
+
* would ship a Workspace Copilot with no funds for exactly the users who chose
|
|
176
|
+
* the in-product path — the default Copilot session budget is 5,000 credits, so
|
|
177
|
+
* the signup grant is precisely one session. The activation half is the pull
|
|
178
|
+
* toward the agent-native surface, and rewards the same action the Company KPI
|
|
179
|
+
* scorecard already counts as activation.
|
|
180
|
+
*
|
|
181
|
+
* Ceiling is therefore 10,000 credits ($10) per billing-owner organization,
|
|
182
|
+
* ever. Neither grant is minted for a churned or downgraded workspace, and no
|
|
183
|
+
* other path re-mints either one — not downgrade, churn, re-subscribe, or
|
|
184
|
+
* billing-owner re-link. Top-ups stay purchasable on free, which is what keeps
|
|
185
|
+
* the metered surface open-ended rather than capped at the grant.
|
|
186
|
+
*/
|
|
187
|
+
export const FREE_SIGNUP_GRANT_CREDITS = 5_000;
|
|
188
|
+
export const FREE_ACTIVATION_GRANT_CREDITS = 5_000;
|
|
189
|
+
export const FREE_TIER_GRANTS_ENABLED_ENV_VAR = "OXYGEN_FREE_TIER_GRANTS_ENABLED";
|
|
190
|
+
/**
|
|
191
|
+
* Fail-closed rollout switch for the free-tier grants, matching the staged
|
|
192
|
+
* pattern used by CREDIT_COMMITMENTS_ENABLED.
|
|
193
|
+
*
|
|
194
|
+
* UNSET MEANS OFF, which is the opposite of the InboxKit-style kill switches and
|
|
195
|
+
* is deliberate: this flag mints credits. Until the rest of the free tier ships
|
|
196
|
+
* — the capability-scoped entitlement of OXP-10.3 and the card-less signup of
|
|
197
|
+
* OXP-10.4 — an armed grant would fund workspaces that still hit the binary
|
|
198
|
+
* subscription wall, which is money spent on an experience that does not work
|
|
199
|
+
* yet. It also means the grant cannot fire for the existing organization
|
|
200
|
+
* population before the cutover posture is decided.
|
|
201
|
+
*/
|
|
202
|
+
export function freeTierGrantsEnabled(env = process.env) {
|
|
203
|
+
const raw = env[FREE_TIER_GRANTS_ENABLED_ENV_VAR];
|
|
204
|
+
if (!raw)
|
|
205
|
+
return false;
|
|
206
|
+
const normalized = raw.trim().toLowerCase();
|
|
207
|
+
return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
|
|
208
|
+
}
|
|
155
209
|
// Onboarding walkthrough (2026-07-21): completing the in-product walkthrough
|
|
156
210
|
// grants a one-off bonus (idempotent per billing owner), and the walkthrough's
|
|
157
211
|
// "start scraping engagers" step may spend at most this many credits on the
|
|
@@ -159,41 +213,70 @@ export const TRIAL_CREDIT_GRANT = 20_000;
|
|
|
159
213
|
// is free connected-account reads).
|
|
160
214
|
export const WALKTHROUGH_COMPLETION_BONUS_CREDITS = 1_000;
|
|
161
215
|
export const WALKTHROUGH_SCRAPE_MAX_CREDITS = 2_000;
|
|
216
|
+
/**
|
|
217
|
+
* The five NUMERIC fields of a plan, read from the generated pricing snapshot.
|
|
218
|
+
*
|
|
219
|
+
* Prices live in packages/control-db/src/pricing/seed.ts; the marketing copy
|
|
220
|
+
* (name, description, ctaLabel, features) stays here, because a feature bullet
|
|
221
|
+
* is not a price and does not belong in a pricing table. Split that way, a plan
|
|
222
|
+
* re-price is one edit in the seed and this file does not move at all.
|
|
223
|
+
*
|
|
224
|
+
* Sync on purpose: this catalog is a module-level constant that has to exist
|
|
225
|
+
* before any request does. Async callers that want a staff override applied
|
|
226
|
+
* should read loadPricingBook() instead.
|
|
227
|
+
*/
|
|
228
|
+
function planPricing(planKey) {
|
|
229
|
+
const row = PRICING_PLAN_SNAPSHOT.find((plan) => plan.planKey === planKey);
|
|
230
|
+
if (!row)
|
|
231
|
+
throw new Error(`pricing snapshot has no plan "${planKey}"`);
|
|
232
|
+
return {
|
|
233
|
+
monthlyPriceCents: row.monthlyPriceCents,
|
|
234
|
+
monthlyCredits: row.monthlyCredits,
|
|
235
|
+
weeklyCreditsLimit: row.weeklyCreditsLimit,
|
|
236
|
+
rolloverCap: row.rolloverCap,
|
|
237
|
+
byokEnabled: row.byokEnabled,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
162
240
|
export const BASE_PRICING_PLANS = {
|
|
163
|
-
//
|
|
164
|
-
//
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
//
|
|
241
|
+
// The ENTRY tier since 2026-08-16, superseding the 2026-07-22 retirement that
|
|
242
|
+
// left this as the unentitled fallback for churned / never-subscribed orgs.
|
|
243
|
+
//
|
|
244
|
+
// `free` now serves TWO populations and deliberately does not distinguish
|
|
245
|
+
// them here: a newly signed-up workspace and a voluntarily churned one get the
|
|
246
|
+
// same capability set. Only the grants differ — a newcomer draws
|
|
247
|
+
// FREE_SIGNUP_GRANT_CREDITS (and FREE_ACTIVATION_GRANT_CREDITS on first
|
|
248
|
+
// CLI/MCP activation), a returner draws neither and keeps whatever balance it
|
|
249
|
+
// already had. Anything branching on `plan.tier === "free"` must be read under
|
|
250
|
+
// BOTH meanings: code written when this could only mean "churned org that can
|
|
251
|
+
// spend nothing" now also applies to a funded newcomer.
|
|
252
|
+
//
|
|
253
|
+
// The seeded numbers are unchanged and load-bearing: monthlyCredits 0 is the
|
|
254
|
+
// ANSWER (there is no monthly allowance — ensureCurrentPlanCreditGrant
|
|
255
|
+
// short-circuits, and the grants are one-time), and rolloverCap null rather
|
|
256
|
+
// than 0 is what stops a churned customer's leftover balance being clawed back.
|
|
168
257
|
free: {
|
|
169
258
|
tier: "free",
|
|
170
259
|
name: "Free",
|
|
171
|
-
|
|
172
|
-
monthlyCredits: 0,
|
|
173
|
-
weeklyCreditsLimit: null,
|
|
174
|
-
rolloverCap: null,
|
|
260
|
+
...planPricing("free"),
|
|
175
261
|
monthlyAutomationActions: null,
|
|
176
262
|
automationOverageCentsPerMillion: null,
|
|
177
263
|
automationOverageEnabledDefault: false,
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
ctaLabel: "Start trial",
|
|
264
|
+
description: "Your CRM, tables, and workflows — free forever. Credits pay for enrichment and AI; connecting a sender, publishing, managed email infrastructure, and your own provider keys need a plan.",
|
|
265
|
+
ctaLabel: "Upgrade",
|
|
181
266
|
features: [
|
|
182
|
-
"
|
|
183
|
-
"
|
|
267
|
+
"CRM, Tables, Workflows, and Knowledge — unmetered",
|
|
268
|
+
"Sequences: author, preview, and enroll",
|
|
269
|
+
"Agents and Workspace Copilot",
|
|
270
|
+
"Credits for enrichment and AI, topped up any time",
|
|
184
271
|
],
|
|
185
272
|
},
|
|
186
273
|
starter: {
|
|
187
274
|
tier: "starter",
|
|
188
275
|
name: "Starter",
|
|
189
|
-
|
|
190
|
-
monthlyCredits: 99_000,
|
|
191
|
-
weeklyCreditsLimit: null,
|
|
192
|
-
rolloverCap: 99_000,
|
|
276
|
+
...planPricing("starter"),
|
|
193
277
|
monthlyAutomationActions: null,
|
|
194
278
|
automationOverageCentsPerMillion: null,
|
|
195
279
|
automationOverageEnabledDefault: false,
|
|
196
|
-
byokEnabled: true,
|
|
197
280
|
description: "For founders running focused managed enrichment and AI columns.",
|
|
198
281
|
ctaLabel: "Choose Starter",
|
|
199
282
|
features: [
|
|
@@ -207,14 +290,10 @@ export const BASE_PRICING_PLANS = {
|
|
|
207
290
|
pro: {
|
|
208
291
|
tier: "pro",
|
|
209
292
|
name: "Pro",
|
|
210
|
-
|
|
211
|
-
monthlyCredits: 249_000,
|
|
212
|
-
weeklyCreditsLimit: null,
|
|
213
|
-
rolloverCap: 249_000,
|
|
293
|
+
...planPricing("pro"),
|
|
214
294
|
monthlyAutomationActions: null,
|
|
215
295
|
automationOverageCentsPerMillion: null,
|
|
216
296
|
automationOverageEnabledDefault: false,
|
|
217
|
-
byokEnabled: true,
|
|
218
297
|
description: "For recurring GTM enrichment and AI-column workflows on managed credits.",
|
|
219
298
|
badge: "Most popular",
|
|
220
299
|
highlighted: true,
|
|
@@ -230,14 +309,10 @@ export const BASE_PRICING_PLANS = {
|
|
|
230
309
|
team: {
|
|
231
310
|
tier: "team",
|
|
232
311
|
name: "Team",
|
|
233
|
-
|
|
234
|
-
monthlyCredits: 749_000,
|
|
235
|
-
weeklyCreditsLimit: null,
|
|
236
|
-
rolloverCap: 749_000,
|
|
312
|
+
...planPricing("team"),
|
|
237
313
|
monthlyAutomationActions: null,
|
|
238
314
|
automationOverageCentsPerMillion: null,
|
|
239
315
|
automationOverageEnabledDefault: false,
|
|
240
|
-
byokEnabled: true,
|
|
241
316
|
description: "For small GTM teams and agencies running shared workflows with their own AI keys.",
|
|
242
317
|
ctaLabel: "Choose Team",
|
|
243
318
|
features: [
|
|
@@ -251,14 +326,10 @@ export const BASE_PRICING_PLANS = {
|
|
|
251
326
|
scale: {
|
|
252
327
|
tier: "scale",
|
|
253
328
|
name: "Scale",
|
|
254
|
-
|
|
255
|
-
monthlyCredits: 10_000_000,
|
|
256
|
-
weeklyCreditsLimit: null,
|
|
257
|
-
rolloverCap: 10_000_000,
|
|
329
|
+
...planPricing("scale"),
|
|
258
330
|
monthlyAutomationActions: null,
|
|
259
331
|
automationOverageCentsPerMillion: null,
|
|
260
332
|
automationOverageEnabledDefault: false,
|
|
261
|
-
byokEnabled: true,
|
|
262
333
|
description: "For high-volume GTM teams that need custom limits and rollout support.",
|
|
263
334
|
ctaLabel: "Contact sales",
|
|
264
335
|
contactHref: CONTACT_SALES_URL,
|
|
@@ -273,14 +344,10 @@ export const BASE_PRICING_PLANS = {
|
|
|
273
344
|
enterprise: {
|
|
274
345
|
tier: "enterprise",
|
|
275
346
|
name: "Enterprise",
|
|
276
|
-
|
|
277
|
-
monthlyCredits: null,
|
|
278
|
-
weeklyCreditsLimit: null,
|
|
279
|
-
rolloverCap: null,
|
|
347
|
+
...planPricing("enterprise"),
|
|
280
348
|
monthlyAutomationActions: null,
|
|
281
349
|
automationOverageCentsPerMillion: null,
|
|
282
350
|
automationOverageEnabledDefault: false,
|
|
283
|
-
byokEnabled: true,
|
|
284
351
|
description: "Custom procurement, invoicing, limits, and rollout support.",
|
|
285
352
|
ctaLabel: "Talk to us",
|
|
286
353
|
contactHref: CONTACT_SALES_URL,
|
|
@@ -322,6 +389,23 @@ export const PAST_DUE_GRACE_DAYS = 7;
|
|
|
322
389
|
export function resolveSubscriptionEntitlement(input) {
|
|
323
390
|
const now = input.now ?? new Date();
|
|
324
391
|
if (!input.status) {
|
|
392
|
+
// A workspace that has NEVER subscribed is a free workspace, and free is a
|
|
393
|
+
// real entitled tier once free-tier entry is armed (founder, 2026-08-16).
|
|
394
|
+
// This is the switch that makes `assertSubscriptionExecutionAccess` stop
|
|
395
|
+
// being the interesting gate and hands the decision to the capability
|
|
396
|
+
// resolver instead.
|
|
397
|
+
//
|
|
398
|
+
// Scoped deliberately to "no subscription row at all". A `canceled` status
|
|
399
|
+
// is NOT included, even though OXP-10.1.6 puts a voluntarily churned
|
|
400
|
+
// workspace on this same free tier: Stripe also lands a FAILED-PAYMENT
|
|
401
|
+
// customer in `canceled` after its recovery attempts are exhausted, and the
|
|
402
|
+
// ratified 2026-08-02 lifecycle suspends execution and freezes credits for
|
|
403
|
+
// exactly that case. Status alone cannot tell the two apart, so treating
|
|
404
|
+
// `canceled` as free here would silently reverse the failed-payment freeze.
|
|
405
|
+
// Churned-to-free needs that distinction built first.
|
|
406
|
+
if (input.freeTierEntitled ?? freeTierEntitlementEnabled()) {
|
|
407
|
+
return { entitled: true, reason: "free_tier", graceEndsAt: null };
|
|
408
|
+
}
|
|
325
409
|
return { entitled: false, reason: "missing_subscription", graceEndsAt: null };
|
|
326
410
|
}
|
|
327
411
|
if (input.status === "active") {
|
|
@@ -362,70 +446,46 @@ const LEGACY_PRICING_PLAN_OVERRIDES = {
|
|
|
362
446
|
launch_50: {
|
|
363
447
|
...BASE_PRICING_PLANS.starter,
|
|
364
448
|
name: "Legacy Launch",
|
|
365
|
-
|
|
366
|
-
monthlyCredits: 200_000,
|
|
367
|
-
weeklyCreditsLimit: 200_000,
|
|
368
|
-
rolloverCap: 200_000,
|
|
449
|
+
...planPricing("launch_50"),
|
|
369
450
|
description: "Legacy Launch plan.",
|
|
370
451
|
},
|
|
371
452
|
starter_50: {
|
|
372
453
|
...BASE_PRICING_PLANS.starter,
|
|
373
454
|
name: "Legacy Starter",
|
|
374
|
-
|
|
375
|
-
monthlyCredits: 200_000,
|
|
376
|
-
weeklyCreditsLimit: 200_000,
|
|
377
|
-
rolloverCap: 200_000,
|
|
455
|
+
...planPricing("starter_50"),
|
|
378
456
|
description: "Legacy Starter plan.",
|
|
379
457
|
},
|
|
380
458
|
pro_149: {
|
|
381
459
|
...BASE_PRICING_PLANS.pro,
|
|
382
460
|
name: "Legacy Pro",
|
|
383
|
-
|
|
384
|
-
monthlyCredits: 1_000_000,
|
|
385
|
-
weeklyCreditsLimit: 1_000_000,
|
|
386
|
-
rolloverCap: 1_000_000,
|
|
461
|
+
...planPricing("pro_149"),
|
|
387
462
|
description: "Legacy Pro plan.",
|
|
388
463
|
},
|
|
389
464
|
team_399: {
|
|
390
465
|
...BASE_PRICING_PLANS.team,
|
|
391
466
|
name: "Legacy Team",
|
|
392
|
-
|
|
393
|
-
monthlyCredits: 3_000_000,
|
|
394
|
-
weeklyCreditsLimit: 3_000_000,
|
|
395
|
-
rolloverCap: 3_000_000,
|
|
467
|
+
...planPricing("team_399"),
|
|
396
468
|
description: "Legacy Team plan.",
|
|
397
469
|
},
|
|
398
470
|
growth_250: {
|
|
399
471
|
...BASE_PRICING_PLANS.pro,
|
|
400
|
-
|
|
401
|
-
monthlyCredits: 250_000,
|
|
402
|
-
weeklyCreditsLimit: 100_000,
|
|
403
|
-
rolloverCap: 250_000,
|
|
472
|
+
...planPricing("growth_250"),
|
|
404
473
|
description: "Legacy Growth plan.",
|
|
405
474
|
},
|
|
406
475
|
scale_750: {
|
|
407
476
|
...BASE_PRICING_PLANS.team,
|
|
408
477
|
name: "Legacy Scale",
|
|
409
|
-
|
|
410
|
-
monthlyCredits: 750_000,
|
|
411
|
-
weeklyCreditsLimit: 300_000,
|
|
412
|
-
rolloverCap: 750_000,
|
|
478
|
+
...planPricing("scale_750"),
|
|
413
479
|
description: "Legacy Scale plan.",
|
|
414
480
|
},
|
|
415
481
|
starter_legacy: {
|
|
416
482
|
...BASE_PRICING_PLANS.starter,
|
|
417
|
-
|
|
418
|
-
monthlyCredits: 200_000,
|
|
419
|
-
weeklyCreditsLimit: BASE_PRICING_PLANS.starter.weeklyCreditsLimit,
|
|
420
|
-
rolloverCap: 200_000,
|
|
483
|
+
...planPricing("starter_legacy"),
|
|
421
484
|
description: "Legacy Starter plan.",
|
|
422
485
|
},
|
|
423
486
|
scale_legacy: {
|
|
424
487
|
...BASE_PRICING_PLANS.scale,
|
|
425
|
-
|
|
426
|
-
monthlyCredits: 1_200_000,
|
|
427
|
-
weeklyCreditsLimit: BASE_PRICING_PLANS.scale.weeklyCreditsLimit,
|
|
428
|
-
rolloverCap: 2_400_000,
|
|
488
|
+
...planPricing("scale_legacy"),
|
|
429
489
|
description: "Legacy Scale plan.",
|
|
430
490
|
},
|
|
431
491
|
};
|
|
@@ -39,11 +39,19 @@ export const OXYGEN_CAPABILITY_ROUTES = [
|
|
|
39
39
|
notFor: "Recurring deterministic automation or standing adaptive jobs; use Workflows or Agents.",
|
|
40
40
|
execution: "Start onboarding or a hosted Copilot session; every resulting action still uses its native primitive gate.",
|
|
41
41
|
posture: "mixed",
|
|
42
|
-
gatewayTools: ["oxygen_whoami", "oxygen_context_resolve", "oxygen_copilot_start"],
|
|
43
|
-
gatewayCommands: ["whoami", "context resolve", "copilot start"],
|
|
42
|
+
gatewayTools: ["oxygen_whoami", "oxygen_activation_state", "oxygen_context_resolve", "oxygen_copilot_start"],
|
|
43
|
+
gatewayCommands: ["whoami", "activation state", "context resolve", "copilot start"],
|
|
44
44
|
skills: ["oxygen-quickstart", "oxygen-gtm"],
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
// `activation` carries the prescribed-play step state and the one next step. It
|
|
46
|
+
// belongs here rather than under a primitive because "where am I in setup" is a
|
|
47
|
+
// Control question — and without the section on this card, the Copilot's very
|
|
48
|
+
// first tool call routes past the only read that answers it.
|
|
49
|
+
endpointSections: ["activation", "copilot", "home"],
|
|
50
|
+
// "next step" alone is deliberately absent: it is a phrase every primitive's
|
|
51
|
+
// users say ("next step of this workflow run"), and a 6-point multi-word hit
|
|
52
|
+
// would drag those queries here. The terms kept are the ones only a founder
|
|
53
|
+
// asking where they are in setup actually types.
|
|
54
|
+
intentTerms: ["onboard", "onboarding", "first run", "first day", "setup oxygen", "copilot", "assistant", "guided", "activation", "where am i", "next step in setup", "what do i do next"],
|
|
47
55
|
},
|
|
48
56
|
{
|
|
49
57
|
id: "billing-and-budgets",
|
|
@@ -7,6 +7,14 @@ export type CopilotJourney = {
|
|
|
7
7
|
/** The first user message a journey card seeds into a new session. */
|
|
8
8
|
seedMessage: string;
|
|
9
9
|
};
|
|
10
|
+
/**
|
|
11
|
+
* The one journey a workspace with no play yet is pointed at. Exported as a named
|
|
12
|
+
* constant because every consumer that needs "the prescribed play" must key on the
|
|
13
|
+
* slug: the array is display-ordered, and `COPILOT_JOURNEYS[0]` would silently
|
|
14
|
+
* re-designate a different journey the first time someone reorders the cards.
|
|
15
|
+
* `home/suggestions.tsx` keys its glyphs the same way, for the same reason.
|
|
16
|
+
*/
|
|
17
|
+
export declare const PRESCRIBED_COPILOT_JOURNEY_SLUG = "inbound-led-outbound";
|
|
10
18
|
export declare const COPILOT_JOURNEYS: readonly CopilotJourney[];
|
|
11
19
|
export type CopilotJourneySlug = (typeof COPILOT_JOURNEYS)[number]["slug"];
|
|
12
20
|
export declare function getCopilotJourney(slug: string): CopilotJourney | null;
|
|
@@ -4,17 +4,35 @@
|
|
|
4
4
|
// Each journey is a thin descriptor over EXISTING advisory catalog Recipes:
|
|
5
5
|
// journeys add no runtime behavior, and every guardrail (never enroll/send/buy/
|
|
6
6
|
// change DNS without approval) rides those recipes' own approval gates.
|
|
7
|
+
/**
|
|
8
|
+
* The one journey a workspace with no play yet is pointed at. Exported as a named
|
|
9
|
+
* constant because every consumer that needs "the prescribed play" must key on the
|
|
10
|
+
* slug: the array is display-ordered, and `COPILOT_JOURNEYS[0]` would silently
|
|
11
|
+
* re-designate a different journey the first time someone reorders the cards.
|
|
12
|
+
* `home/suggestions.tsx` keys its glyphs the same way, for the same reason.
|
|
13
|
+
*/
|
|
14
|
+
export const PRESCRIBED_COPILOT_JOURNEY_SLUG = "inbound-led-outbound";
|
|
7
15
|
export const COPILOT_JOURNEYS = [
|
|
8
16
|
{
|
|
9
|
-
slug:
|
|
17
|
+
slug: PRESCRIBED_COPILOT_JOURNEY_SLUG,
|
|
10
18
|
title: "Inbound-led outbound",
|
|
11
|
-
description: "
|
|
19
|
+
description: "Work the LinkedIn network you already have — connections and post engagers — as a capped warm motion.",
|
|
20
|
+
// The network-first play leads: connections and post engagers are the two
|
|
21
|
+
// sources a founder actually has on day 1, and profile viewers (the old lead)
|
|
22
|
+
// are a thin, anonymity-capped sample by comparison. Posting is second because
|
|
23
|
+
// it CREATES the engager stream this play captures, and replies close the loop.
|
|
12
24
|
recipeSlugs: [
|
|
13
|
-
"
|
|
14
|
-
"
|
|
25
|
+
"linkedin-network-first-motion",
|
|
26
|
+
"founder-posting-system",
|
|
15
27
|
"reply-to-meeting",
|
|
16
28
|
],
|
|
17
|
-
|
|
29
|
+
// Deliberately free of steps, commands, table names, and caps: encoding
|
|
30
|
+
// mechanics here would fork a second, unversioned copy of the play that no
|
|
31
|
+
// catalog gate can lint (ADR 0013 §11 — a journey is a thin pointer at
|
|
32
|
+
// advisory recipes, never a hardcoded kit). The activation-state instruction
|
|
33
|
+
// is the one piece of procedure that belongs here rather than in the recipe:
|
|
34
|
+
// it is about where THIS workspace already stands, which the catalog cannot know.
|
|
35
|
+
seedMessage: "I want to turn the LinkedIn network I already have — my connections and the people who engage with my posts — into a small, safe, approval-gated outreach motion. First read my workspace's current activation state and tell me where I already am, then resume from there instead of starting over. Follow the linkedin-network-first-motion recipe for the play itself. Do not connect an account, enroll anyone, send anything, or spend credits without asking me first.",
|
|
18
36
|
},
|
|
19
37
|
{
|
|
20
38
|
slug: "tam-sourcing",
|