@oxygen-agent/cli 1.1010.721 → 1.1010.905
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/auto-update.d.ts +129 -0
- package/dist/auto-update.js +392 -0
- package/dist/command-manifest.js +14 -0
- package/dist/credentials.d.ts +2 -0
- package/dist/credentials.js +6 -3
- package/dist/functions-commands.js +1 -1
- package/dist/http-client.js +28 -4
- package/dist/index.js +583 -145
- package/dist/run-wait.d.ts +3 -1
- package/dist/run-wait.js +19 -5
- package/dist/streamed-file-import.d.ts +58 -0
- package/dist/streamed-file-import.js +115 -0
- package/dist/update.d.ts +29 -0
- package/dist/update.js +62 -16
- package/dist/workflow-plan-limit-notices.d.ts +8 -0
- package/dist/workflow-plan-limit-notices.js +28 -0
- package/node_modules/@oxygen/cli-ugc/dist/commands.js +3 -3
- package/node_modules/@oxygen/shared/dist/billing-anchors.d.ts +17 -0
- package/node_modules/@oxygen/shared/dist/billing-anchors.js +27 -0
- package/node_modules/@oxygen/shared/dist/billing.d.ts +191 -35
- package/node_modules/@oxygen/shared/dist/billing.js +333 -42
- package/node_modules/@oxygen/shared/dist/capability-discovery.js +55 -5
- package/node_modules/@oxygen/shared/dist/copilot-skills.generated.d.ts +2 -2
- package/node_modules/@oxygen/shared/dist/copilot-skills.generated.js +2 -2
- package/node_modules/@oxygen/shared/dist/cost-estimate-view.d.ts +50 -0
- package/node_modules/@oxygen/shared/dist/cost-estimate-view.js +90 -0
- package/node_modules/@oxygen/shared/dist/cost-estimate.d.ts +167 -0
- package/node_modules/@oxygen/shared/dist/cost-estimate.js +361 -0
- package/node_modules/@oxygen/shared/dist/credit-gate.d.ts +26 -0
- package/node_modules/@oxygen/shared/dist/credit-gate.js +65 -0
- package/node_modules/@oxygen/shared/dist/email-deliverability-policy.d.ts +51 -0
- package/node_modules/@oxygen/shared/dist/email-deliverability-policy.js +101 -0
- package/node_modules/@oxygen/shared/dist/email-hard-bounce.d.ts +3 -1
- package/node_modules/@oxygen/shared/dist/email-hard-bounce.js +3 -3
- package/node_modules/@oxygen/shared/dist/error-redaction.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/error-redaction.js +1 -1
- package/node_modules/@oxygen/shared/dist/feature-gates.d.ts +4 -0
- package/node_modules/@oxygen/shared/dist/feature-gates.js +5 -0
- package/node_modules/@oxygen/shared/dist/file-import.d.ts +13 -1
- package/node_modules/@oxygen/shared/dist/file-import.js +33 -6
- package/node_modules/@oxygen/shared/dist/hosted-ai.d.ts +73 -3
- package/node_modules/@oxygen/shared/dist/hosted-ai.js +246 -24
- package/node_modules/@oxygen/shared/dist/import-limits.d.ts +25 -1
- package/node_modules/@oxygen/shared/dist/import-limits.js +35 -2
- package/node_modules/@oxygen/shared/dist/index.d.ts +2 -22
- package/node_modules/@oxygen/shared/dist/index.js +2 -42
- package/node_modules/@oxygen/shared/dist/object-storage.d.ts +9 -0
- package/node_modules/@oxygen/shared/dist/object-storage.js +17 -0
- package/node_modules/@oxygen/shared/dist/operational-telemetry.d.ts +41 -0
- package/node_modules/@oxygen/shared/dist/operational-telemetry.js +55 -0
- package/node_modules/@oxygen/shared/dist/plan-band.d.ts +117 -1
- package/node_modules/@oxygen/shared/dist/plan-band.js +175 -10
- package/node_modules/@oxygen/shared/dist/plan-capabilities.d.ts +77 -7
- package/node_modules/@oxygen/shared/dist/plan-capabilities.js +87 -7
- package/node_modules/@oxygen/shared/dist/plan-limits-view.d.ts +219 -0
- package/node_modules/@oxygen/shared/dist/plan-limits-view.js +330 -0
- package/node_modules/@oxygen/shared/dist/plan-limits.d.ts +204 -6
- package/node_modules/@oxygen/shared/dist/plan-limits.js +197 -15
- package/node_modules/@oxygen/shared/dist/pricing-sheet.d.ts +80 -36
- package/node_modules/@oxygen/shared/dist/pricing-sheet.js +80 -31
- package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.d.ts +38 -20
- package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.js +47 -34
- package/node_modules/@oxygen/shared/dist/provider-http-error.d.ts +10 -0
- package/node_modules/@oxygen/shared/dist/provider-http-error.js +27 -0
- package/node_modules/@oxygen/shared/dist/repricing.d.ts +127 -0
- package/node_modules/@oxygen/shared/dist/repricing.js +407 -6
- package/node_modules/@oxygen/shared/dist/semver.d.ts +21 -0
- package/node_modules/@oxygen/shared/dist/semver.js +41 -0
- package/node_modules/@oxygen/shared/dist/sending-limits.d.ts +5 -7
- package/node_modules/@oxygen/shared/dist/sending-limits.js +10 -16
- package/node_modules/@oxygen/shared/dist/sending-seats.d.ts +18 -15
- package/node_modules/@oxygen/shared/dist/sending-seats.js +22 -17
- package/node_modules/@oxygen/shared/dist/spend-safety.d.ts +57 -8
- package/node_modules/@oxygen/shared/dist/spend-safety.js +64 -11
- package/node_modules/@oxygen/shared/dist/stripe-price-catalog.d.ts +15 -7
- package/node_modules/@oxygen/shared/dist/stripe-price-catalog.js +18 -5
- package/node_modules/@oxygen/shared/dist/table-capacity.d.ts +34 -5
- package/node_modules/@oxygen/shared/dist/table-capacity.js +25 -8
- package/node_modules/@oxygen/shared/dist/telemetry-export-observer.d.ts +6 -0
- package/node_modules/@oxygen/shared/dist/telemetry-export-observer.js +13 -5
- package/node_modules/@oxygen/shared/dist/telemetry-resource.d.ts +40 -0
- package/node_modules/@oxygen/shared/dist/telemetry-resource.js +35 -0
- package/node_modules/@oxygen/shared/dist/telemetry.js +5 -0
- package/node_modules/@oxygen/shared/dist/ugc.d.ts +15 -0
- package/node_modules/@oxygen/shared/dist/ugc.js +29 -0
- package/node_modules/@oxygen/shared/dist/version.d.ts +1 -3
- package/node_modules/@oxygen/shared/dist/version.generated.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.generated.js +1 -1
- package/node_modules/@oxygen/shared/dist/version.js +14 -27
- package/node_modules/@oxygen/shared/dist/workspace-file-storage.d.ts +5 -0
- package/node_modules/@oxygen/shared/dist/workspace-file-storage.js +5 -0
- package/node_modules/@oxygen/workflows/dist/graph/manifest-schema.d.ts +3 -3
- package/node_modules/@oxygen/workflows/dist/graph/types.d.ts +15 -1
- package/node_modules/@oxygen/workflows/dist/graph/types.js +15 -1
- package/node_modules/@oxygen/workflows/dist/index.d.ts +45 -0
- package/node_modules/@oxygen/workflows/dist/index.js +152 -2
- package/node_modules/@oxygen/workflows/dist/usage-estimate.d.ts +10 -1
- package/node_modules/@oxygen/workflows/dist/usage-estimate.js +33 -29
- package/package.json +1 -1
|
@@ -33,6 +33,21 @@ export declare function parseRepricingEffectiveAt(raw: string | undefined): Date
|
|
|
33
33
|
export declare function readRepricingSwitch(options?: RepricingOptions): RepricingSwitchState;
|
|
34
34
|
/** True once the switch names an instant that has passed. */
|
|
35
35
|
export declare function isRepricingInForce(options?: RepricingOptions): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* The value an entry moves to and when, only while the switch names an instant
|
|
38
|
+
* still ahead (`scheduled`). `null` when unset, invalid or already in force, so a
|
|
39
|
+
* marketing surface that quotes "$1.25, $1.30 from <date>" announces nothing
|
|
40
|
+
* until an operator sets the date and stops announcing once the new value is
|
|
41
|
+
* the one in force.
|
|
42
|
+
*/
|
|
43
|
+
export declare function scheduledRepricedValue<T extends RepricedScalar>(value: {
|
|
44
|
+
readonly after: T;
|
|
45
|
+
}, options?: RepricingOptions): {
|
|
46
|
+
value: Widen<T>;
|
|
47
|
+
effectiveAt: Date;
|
|
48
|
+
} | null;
|
|
49
|
+
/** "October 27, 2026": how marketing copy names the switch's instant, in UTC. */
|
|
50
|
+
export declare function formatRepricingDate(effectiveAt: Date): string;
|
|
36
51
|
/**
|
|
37
52
|
* A price or limit value. `null` means "no bound", and only a limit's `before`
|
|
38
53
|
* may hold it: a limit that did not exist and now does is a tightening that
|
|
@@ -70,6 +85,14 @@ export declare const EMAIL_MAILBOX_DAILY_CAP_MAXIMUM: RepricedValue<number | nul
|
|
|
70
85
|
* setting the cap again records it as the customer's own, and it is then kept.
|
|
71
86
|
*/
|
|
72
87
|
export declare const EMAIL_MAILBOX_OLD_DEFAULT_DAILY_CAP: RepricedValue<25 | 40>;
|
|
88
|
+
/**
|
|
89
|
+
* S10 (decision 1.5): 100 on-demand top-up credits cost $1.30, up from $1.25.
|
|
90
|
+
* The seed and the generated snapshot carry the new rate; the rate sold until
|
|
91
|
+
* now stays in force until the switch's instant. `creditTopupUsdCentsPer100()`
|
|
92
|
+
* in billing.ts is the one runtime reader, and the packs on offer move with it
|
|
93
|
+
* (`creditTopupPacks()`, proposal P-7).
|
|
94
|
+
*/
|
|
95
|
+
export declare const TOPUP_RATE_REPRICING: RepricedValue<125 | 130>;
|
|
73
96
|
/**
|
|
74
97
|
* S20: the monthly credit reservation of every connected account (decisions
|
|
75
98
|
* 2.1–2.4). Each key is the pricing-seed charge key, so the seed row carries the
|
|
@@ -80,6 +103,110 @@ export declare const EMAIL_MAILBOX_OLD_DEFAULT_DAILY_CAP: RepricedValue<25 | 40>
|
|
|
80
103
|
* rather than in credits. Such a kind reserves nothing until the instant.
|
|
81
104
|
*/
|
|
82
105
|
export declare const S20_RESERVATION_PRICE_RISES: readonly RepricedValue<number>[];
|
|
106
|
+
/**
|
|
107
|
+
* S40 (decision 6.1): a workflow step costs 0.05 credits, up from 0.001 per
|
|
108
|
+
* action. The seed and the generated snapshot carry the new price; the price
|
|
109
|
+
* charged until now stays in force until the switch's instant. Row writes inside
|
|
110
|
+
* a run are not steps and bill nothing (decision 6.2, live on release because it
|
|
111
|
+
* is a decrease), so the rise applies to steps only.
|
|
112
|
+
*/
|
|
113
|
+
export declare const AUTOMATION_STEP_CREDITS_REPRICING: RepricedValue<0.001 | 0.05>;
|
|
114
|
+
/**
|
|
115
|
+
* S40 (proposal P-45): once a step costs real money, the steps a live run
|
|
116
|
+
* executes count toward that run's approved `max_credits` and a trigger's
|
|
117
|
+
* per-delivery ceiling, next to its paid tool calls. Counting them tightens an
|
|
118
|
+
* approved ceiling, so it starts with the price and not before.
|
|
119
|
+
*/
|
|
120
|
+
export declare const AUTOMATION_STEPS_COUNT_TOWARD_RUN_CAP_REPRICING: RepricedValue<boolean>;
|
|
121
|
+
/**
|
|
122
|
+
* S30 (decision 3.5): EmailGuard monitoring per subscribed inbox, including the
|
|
123
|
+
* cycle's placement-test allowance, 100 -> 135 credits a month. Existing
|
|
124
|
+
* subscriptions pay the price in force at each renewal.
|
|
125
|
+
*/
|
|
126
|
+
export declare const DELIVERABILITY_SEAT_REPRICING: RepricedValue<100 | 135>;
|
|
127
|
+
/**
|
|
128
|
+
* S30 (decision 3.6; flat literal, proposal P-29): one managed placement test
|
|
129
|
+
* past an inbox's included allowance, or on an inbox with no monitoring
|
|
130
|
+
* subscription, 16.6 -> 66 credits. Tests on your own EmailGuard key stay 0.
|
|
131
|
+
*/
|
|
132
|
+
export declare const PLACEMENT_TEST_REPRICING: RepricedValue<16.6 | 66>;
|
|
133
|
+
/** EmailGuard monitoring per subscribed inbox-month, in credits, at the price in force. */
|
|
134
|
+
export declare function deliverabilitySeatCredits(options?: RepricingOptions): number;
|
|
135
|
+
/** One managed placement test beyond the included allowance, in credits, at the price in force. */
|
|
136
|
+
export declare function placementTestCredits(options?: RepricingOptions): number;
|
|
137
|
+
/** The flat monthly mailbox line of a managed Google inbox (the pricing seed's `commitment.managed_mailbox`). */
|
|
138
|
+
export declare const MANAGED_MAILBOX_REPRICING: RepricedValue<300 | 400>;
|
|
139
|
+
/** What a managed Google inbox costs a month, all-in: mailbox, connection and warm-up. */
|
|
140
|
+
export declare const MANAGED_INBOX_ALL_IN_REPRICING: RepricedValue<number>;
|
|
141
|
+
/** A managed Google inbox's monthly credits, all-in, at the switch state (marketing and docs). */
|
|
142
|
+
export declare function managedInboxAllInCredits(options?: RepricingOptions): number;
|
|
143
|
+
/**
|
|
144
|
+
* S41 (decision 6.3): a provider call made with the customer's own API key
|
|
145
|
+
* (BYOK) pays a 0.05-credit platform fee, up from nothing. The vendor still
|
|
146
|
+
* bills the call itself. The seed and the generated snapshot carry the fee; it
|
|
147
|
+
* is 0 until the switch's instant. Standing permissions approved while BYOK was
|
|
148
|
+
* free (armed auto-runs, workflow triggers) pay it from then on inside the
|
|
149
|
+
* per-delivery ceiling they already carry (proposal P-67).
|
|
150
|
+
*/
|
|
151
|
+
export declare const BYOK_PLATFORM_FEE_REPRICING: RepricedValue<0 | 0.05>;
|
|
152
|
+
/**
|
|
153
|
+
* L5.1 (decision L5.1; the free value and the rounding are PROPOSED P-54): the
|
|
154
|
+
* default credit ceiling of one unattended delivery is 10% of the plan's
|
|
155
|
+
* monthly credits, rounded half up. Integer arithmetic, because
|
|
156
|
+
* `229_885 * 0.1` is 22988.4999... in floating point and rounds the wrong way.
|
|
157
|
+
*/
|
|
158
|
+
export declare function tenthOfMonthlyCreditsRoundedHalfUp(monthlyCredits: number): number;
|
|
159
|
+
export declare const S56_DELIVERY_CREDIT_CEILING_CUTS: readonly RepricedValue<number>[];
|
|
160
|
+
/**
|
|
161
|
+
* L5.2 (decision L5.2): the free plan gains a default daily runaway guard. Free
|
|
162
|
+
* had none, because the guard scales from a monthly grant free does not have,
|
|
163
|
+
* so the guard itself is the tightening: no bound before the instant.
|
|
164
|
+
*/
|
|
165
|
+
export declare const FREE_ORG_DAILY_GUARD_WARN_CREDITS: RepricedValue<number | null>;
|
|
166
|
+
export declare const FREE_ORG_DAILY_GUARD_BLOCK_CREDITS: RepricedValue<number | null>;
|
|
167
|
+
/**
|
|
168
|
+
* L5.4 (decision L5.4; the inference half is PROPOSED P-56): the default credit
|
|
169
|
+
* ceilings of one Agent run on the team and scale limits rungs, which the $999
|
|
170
|
+
* and $1,999 plans, grandfathered Team plans and legacy scale plans read. A
|
|
171
|
+
* customer can still approve a higher ceiling for a single run.
|
|
172
|
+
*/
|
|
173
|
+
export declare const S56_AGENT_RUN_LIMIT_CUTS: readonly RepricedValue<number>[];
|
|
174
|
+
/**
|
|
175
|
+
* S53 (decision L4.2, ratified 2026-09-26): a free workspace may schedule a
|
|
176
|
+
* workflow or Agent cron at most every 15 minutes, down from every minute. Paid
|
|
177
|
+
* plans keep every minute. A new schedule that fires closer together is refused
|
|
178
|
+
* with `cron_interval_below_plan_minimum`; one already armed keeps running at the
|
|
179
|
+
* 15-minute interval instead and is never deleted (proposal P-47).
|
|
180
|
+
* `resolveCronMinIntervalMinutes` in plan-limits.ts resolves it, and a test pins
|
|
181
|
+
* `after` to `PLAN_LIMITS.free.workflows.cronMinIntervalMinutes`.
|
|
182
|
+
*/
|
|
183
|
+
export declare const FREE_CRON_MIN_INTERVAL_REPRICING: RepricedValue<1 | 15>;
|
|
184
|
+
/** A Lusha person enrichment that reveals a phone number, per contact. */
|
|
185
|
+
export declare const LUSHA_PHONE_REVEAL_CREDITS_REPRICING: RepricedValue<43.5 | 217.5>;
|
|
186
|
+
/** A Lusha person enrichment that reveals an email and a phone number, per contact. */
|
|
187
|
+
export declare const LUSHA_EMAIL_AND_PHONE_REVEAL_CREDITS_REPRICING: RepricedValue<43.5 | 261>;
|
|
188
|
+
/**
|
|
189
|
+
* Before the instant a Lusha or Apollo bulk person enrichment never costs more
|
|
190
|
+
* than its old flat price per request (43.5 and 100); from it on, it costs its
|
|
191
|
+
* per-record price times the contacts or records it holds.
|
|
192
|
+
*/
|
|
193
|
+
export declare const PERSON_REVEAL_PRICED_PER_RECORD_REPRICING: RepricedValue<boolean>;
|
|
194
|
+
export declare const S42_REVEAL_PRICE_RISES: readonly RepricedValue[];
|
|
195
|
+
/** Whether the LinkedIn-URL waterfall bills a flat price per URL found. */
|
|
196
|
+
export declare const LINKEDIN_URL_FLAT_PRICE_REPRICING: RepricedValue<boolean>;
|
|
197
|
+
/** True once a LinkedIn URL is priced per URL found (S43). */
|
|
198
|
+
export declare function linkedinUrlFlatPriceInForce(options?: RepricingOptions): boolean;
|
|
199
|
+
/** The flat price of one LinkedIn URL found, in credits (the pricing seed's `enrichment.linkedin_url_found`). */
|
|
200
|
+
export declare function linkedinUrlFoundCredits(): number;
|
|
201
|
+
/**
|
|
202
|
+
* S22 (decision 2.7): bringing your own provider keys (BYOK) needs the $99 plan
|
|
203
|
+
* or above. The pricing seed and snapshot carry the new value (`oxygen_49`
|
|
204
|
+
* `byokEnabled: false`); `resolveBasePricingPlan` holds the $49 plan at the old
|
|
205
|
+
* value until the switch's instant. From the instant on, a key the workspace
|
|
206
|
+
* connected before it keeps working until the plan changes (P-20, proposed):
|
|
207
|
+
* `byokConnectionGrandfathered` in plan-capabilities.ts.
|
|
208
|
+
*/
|
|
209
|
+
export declare const OXYGEN_49_BYOK_REPRICING: RepricedValue<boolean>;
|
|
83
210
|
/**
|
|
84
211
|
* Every value the 2026-09 switch moves. Each dark slice appends its entries
|
|
85
212
|
* here, so `oxygen limits show` can list them once a date is set and one test
|
|
@@ -41,10 +41,12 @@
|
|
|
41
41
|
// tests drive both states, and an operator's change must not need a restart to
|
|
42
42
|
// be seen consistently. Parsing one short string per call is negligible.
|
|
43
43
|
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
//
|
|
44
|
+
// Its only import is the generated pricing snapshot, which is literals only:
|
|
45
|
+
// `@oxygen/shared` ships inside the published CLI, and "use client" components
|
|
46
|
+
// may import the schedule. A client component cannot read the server's
|
|
47
|
+
// environment, though, so a page that shows a price in force must resolve it on
|
|
48
|
+
// the server and pass it down.
|
|
49
|
+
import { AUTOMATION_ACTION_CREDITS as SNAPSHOT_AUTOMATION_STEP_CREDITS, DELIVERABILITY_SEAT_CREDITS_PER_MONTH as SNAPSHOT_DELIVERABILITY_SEAT_CREDITS, PLACEMENT_TEST_OVERAGE_CREDITS as SNAPSHOT_PLACEMENT_TEST_CREDITS, BYOK_PLATFORM_FEE_CREDITS as SNAPSHOT_BYOK_PLATFORM_FEE_CREDITS, LINKEDIN_URL_FOUND_CREDITS as SNAPSHOT_LINKEDIN_URL_FOUND_CREDITS, MANAGED_INBOX_CREDITS_PER_MONTH as SNAPSHOT_MANAGED_MAILBOX_CREDITS, PRICING_PLAN_SNAPSHOT, SENDING_MAILBOX_CREDITS_PER_MONTH as SNAPSHOT_SENDING_MAILBOX_CREDITS, TOPUP_USD_CENTS_PER_100_CREDITS as SNAPSHOT_TOPUP_USD_CENTS_PER_100_CREDITS, } from "./pricing-snapshot.generated.js";
|
|
48
50
|
export const REPRICING_2026_09_EFFECTIVE_AT_ENV_VAR = "OXYGEN_REPRICING_2026_09_EFFECTIVE_AT";
|
|
49
51
|
/** Stable machine name of this price change, as reported on customer surfaces. */
|
|
50
52
|
export const REPRICING_2026_09_NAME = "repricing_2026_09";
|
|
@@ -91,6 +93,23 @@ export function readRepricingSwitch(options = {}) {
|
|
|
91
93
|
export function isRepricingInForce(options = {}) {
|
|
92
94
|
return readRepricingSwitch(options).status === "in_force";
|
|
93
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* The value an entry moves to and when, only while the switch names an instant
|
|
98
|
+
* still ahead (`scheduled`). `null` when unset, invalid or already in force, so a
|
|
99
|
+
* marketing surface that quotes "$1.25, $1.30 from <date>" announces nothing
|
|
100
|
+
* until an operator sets the date and stops announcing once the new value is
|
|
101
|
+
* the one in force.
|
|
102
|
+
*/
|
|
103
|
+
export function scheduledRepricedValue(value, options = {}) {
|
|
104
|
+
const state = readRepricingSwitch(options);
|
|
105
|
+
if (state.status !== "scheduled")
|
|
106
|
+
return null;
|
|
107
|
+
return { value: value.after, effectiveAt: state.effectiveAt };
|
|
108
|
+
}
|
|
109
|
+
/** "October 27, 2026": how marketing copy names the switch's instant, in UTC. */
|
|
110
|
+
export function formatRepricingDate(effectiveAt) {
|
|
111
|
+
return effectiveAt.toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric", timeZone: "UTC" });
|
|
112
|
+
}
|
|
94
113
|
/** The value in force: `before` until the switch's instant, `after` from it on. */
|
|
95
114
|
export function repricedValue(value, options = {}) {
|
|
96
115
|
return (isRepricingInForce(options) ? value.after : value.before);
|
|
@@ -100,9 +119,10 @@ export function defineRepricedValue(definition) {
|
|
|
100
119
|
}
|
|
101
120
|
const GIB = 1024 ** 3;
|
|
102
121
|
// S50 (decision L1, ratified 2026-09-26): the storage limits that TIGHTEN, per
|
|
103
|
-
// plan band.
|
|
122
|
+
// plan band. Before it every band had 3,000,000 rows per Table, 25,000,000 per
|
|
104
123
|
// workspace and 30 GiB (warning at 20 GiB). $199 and $499 keep those values, and
|
|
105
|
-
// $999 / $1,999
|
|
124
|
+
// $999 / $1,999 only rise (workspace totals at once, decision F.1; rows per Table
|
|
125
|
+
// after the capacity test at 5M and 10M rows), so only
|
|
106
126
|
// free, $49 and $99 appear here. The database warning is PROPOSED P-48: two
|
|
107
127
|
// thirds of the hard limit, rounded down to a whole byte.
|
|
108
128
|
// `packages/shared/src/table-capacity.ts` resolves these; a test pins each
|
|
@@ -165,6 +185,23 @@ export const EMAIL_MAILBOX_OLD_DEFAULT_DAILY_CAP = defineRepricedValue({
|
|
|
165
185
|
before: 40,
|
|
166
186
|
after: 25,
|
|
167
187
|
});
|
|
188
|
+
/**
|
|
189
|
+
* S10 (decision 1.5): 100 on-demand top-up credits cost $1.30, up from $1.25.
|
|
190
|
+
* The seed and the generated snapshot carry the new rate; the rate sold until
|
|
191
|
+
* now stays in force until the switch's instant. `creditTopupUsdCentsPer100()`
|
|
192
|
+
* in billing.ts is the one runtime reader, and the packs on offer move with it
|
|
193
|
+
* (`creditTopupPacks()`, proposal P-7).
|
|
194
|
+
*/
|
|
195
|
+
export const TOPUP_RATE_REPRICING = defineRepricedValue({
|
|
196
|
+
key: "topup.usd_cents_per_100_credits",
|
|
197
|
+
kind: "price",
|
|
198
|
+
slice: "S10",
|
|
199
|
+
decision: "1.5",
|
|
200
|
+
unit: "usd_cents_per_100_credits",
|
|
201
|
+
description: "Price of 100 on-demand top-up credits, in US cents. Purchased credits never expire.",
|
|
202
|
+
before: 125,
|
|
203
|
+
after: SNAPSHOT_TOPUP_USD_CENTS_PER_100_CREDITS,
|
|
204
|
+
});
|
|
168
205
|
/**
|
|
169
206
|
* S20: the monthly credit reservation of every connected account (decisions
|
|
170
207
|
* 2.1–2.4). Each key is the pricing-seed charge key, so the seed row carries the
|
|
@@ -216,6 +253,354 @@ export const S20_RESERVATION_PRICE_RISES = [
|
|
|
216
253
|
after: 1_000,
|
|
217
254
|
}),
|
|
218
255
|
];
|
|
256
|
+
/**
|
|
257
|
+
* S40 (decision 6.1): a workflow step costs 0.05 credits, up from 0.001 per
|
|
258
|
+
* action. The seed and the generated snapshot carry the new price; the price
|
|
259
|
+
* charged until now stays in force until the switch's instant. Row writes inside
|
|
260
|
+
* a run are not steps and bill nothing (decision 6.2, live on release because it
|
|
261
|
+
* is a decrease), so the rise applies to steps only.
|
|
262
|
+
*/
|
|
263
|
+
export const AUTOMATION_STEP_CREDITS_REPRICING = defineRepricedValue({
|
|
264
|
+
key: "workflows.automation_step_credits",
|
|
265
|
+
kind: "price",
|
|
266
|
+
slice: "S40",
|
|
267
|
+
decision: "6.1",
|
|
268
|
+
unit: "credits per workflow step",
|
|
269
|
+
description: "Each step a live workflow run executes. Row writes inside a run are free.",
|
|
270
|
+
before: 0.001,
|
|
271
|
+
after: SNAPSHOT_AUTOMATION_STEP_CREDITS,
|
|
272
|
+
});
|
|
273
|
+
/**
|
|
274
|
+
* S40 (proposal P-45): once a step costs real money, the steps a live run
|
|
275
|
+
* executes count toward that run's approved `max_credits` and a trigger's
|
|
276
|
+
* per-delivery ceiling, next to its paid tool calls. Counting them tightens an
|
|
277
|
+
* approved ceiling, so it starts with the price and not before.
|
|
278
|
+
*/
|
|
279
|
+
export const AUTOMATION_STEPS_COUNT_TOWARD_RUN_CAP_REPRICING = defineRepricedValue({
|
|
280
|
+
key: "workflows.steps_count_toward_max_credits",
|
|
281
|
+
kind: "limit",
|
|
282
|
+
slice: "S40",
|
|
283
|
+
decision: "6.1",
|
|
284
|
+
unit: "boolean",
|
|
285
|
+
description: "Workflow step credits count toward a run's max_credits and a trigger's per-delivery ceiling.",
|
|
286
|
+
before: false,
|
|
287
|
+
after: true,
|
|
288
|
+
});
|
|
289
|
+
// ---- S30 (decisions 3.5 and 3.6): EmailGuard back on the 5x markup. -------
|
|
290
|
+
// Both keys are the pricing-seed charge keys, so the seed row and the snapshot
|
|
291
|
+
// carry the new price, `after` reads it from the snapshot (one definition), and
|
|
292
|
+
// `repricedChargeCredits` / `repricedValue` hold the old price until the instant.
|
|
293
|
+
/**
|
|
294
|
+
* S30 (decision 3.5): EmailGuard monitoring per subscribed inbox, including the
|
|
295
|
+
* cycle's placement-test allowance, 100 -> 135 credits a month. Existing
|
|
296
|
+
* subscriptions pay the price in force at each renewal.
|
|
297
|
+
*/
|
|
298
|
+
export const DELIVERABILITY_SEAT_REPRICING = defineRepricedValue({
|
|
299
|
+
key: "commitment.deliverability_unit",
|
|
300
|
+
kind: "price",
|
|
301
|
+
slice: "S30",
|
|
302
|
+
decision: "3.5",
|
|
303
|
+
unit: "credits_per_inbox_per_month",
|
|
304
|
+
description: "EmailGuard monitoring per subscribed inbox, including its included placement tests each cycle.",
|
|
305
|
+
before: 100,
|
|
306
|
+
after: SNAPSHOT_DELIVERABILITY_SEAT_CREDITS,
|
|
307
|
+
});
|
|
308
|
+
/**
|
|
309
|
+
* S30 (decision 3.6; flat literal, proposal P-29): one managed placement test
|
|
310
|
+
* past an inbox's included allowance, or on an inbox with no monitoring
|
|
311
|
+
* subscription, 16.6 -> 66 credits. Tests on your own EmailGuard key stay 0.
|
|
312
|
+
*/
|
|
313
|
+
export const PLACEMENT_TEST_REPRICING = defineRepricedValue({
|
|
314
|
+
key: "overage.placement_test",
|
|
315
|
+
kind: "price",
|
|
316
|
+
slice: "S30",
|
|
317
|
+
decision: "3.6",
|
|
318
|
+
unit: "credits_per_test",
|
|
319
|
+
description: "One managed inbox-placement test beyond an inbox's included tests. Tests on your own EmailGuard key cost 0.",
|
|
320
|
+
before: 16.6,
|
|
321
|
+
after: SNAPSHOT_PLACEMENT_TEST_CREDITS,
|
|
322
|
+
});
|
|
323
|
+
/** EmailGuard monitoring per subscribed inbox-month, in credits, at the price in force. */
|
|
324
|
+
export function deliverabilitySeatCredits(options = {}) {
|
|
325
|
+
return repricedValue(DELIVERABILITY_SEAT_REPRICING, options);
|
|
326
|
+
}
|
|
327
|
+
/** One managed placement test beyond the included allowance, in credits, at the price in force. */
|
|
328
|
+
export function placementTestCredits(options = {}) {
|
|
329
|
+
return repricedValue(PLACEMENT_TEST_REPRICING, options);
|
|
330
|
+
}
|
|
331
|
+
// ---- S32 (decision 3.1): a managed Google inbox at 500 credits all-in. ----
|
|
332
|
+
// Until the instant, a managed mailbox is priced at the inbox vendor's cost x 1.2
|
|
333
|
+
// (300 credits for Google at $2.50) and its warm-up add-on bills 100 on top. From
|
|
334
|
+
// it, a Google mailbox is a flat 400 plus the 100-credit mailbox connection every
|
|
335
|
+
// sending mailbox holds, 500 in total, with warm-up included in the connection.
|
|
336
|
+
// Microsoft and Azure keep their cost-derived mailbox or tenant price (proposals
|
|
337
|
+
// P-25, P-26) and gain the same included warm-up and connection.
|
|
338
|
+
//
|
|
339
|
+
// The switch alone does not move it. `isManagedInboxAllInPriceActive`
|
|
340
|
+
// (@oxygen/integrations) also requires credit commitments to bill
|
|
341
|
+
// `sending_mailbox` and `managed_mailbox` (the S26 arming): otherwise the order
|
|
342
|
+
// would drop to 400 while nobody collects the connection it includes.
|
|
343
|
+
/** The flat monthly mailbox line of a managed Google inbox (the pricing seed's `commitment.managed_mailbox`). */
|
|
344
|
+
export const MANAGED_MAILBOX_REPRICING = defineRepricedValue({
|
|
345
|
+
key: "commitment.managed_mailbox",
|
|
346
|
+
kind: "price",
|
|
347
|
+
slice: "S32",
|
|
348
|
+
decision: "3.1",
|
|
349
|
+
unit: "credits_per_inbox_per_month",
|
|
350
|
+
description: "The mailbox line of a managed Google inbox, a flat price. Its 100-credit mailbox connection, with warm-up included, is billed beside it.",
|
|
351
|
+
before: 300,
|
|
352
|
+
after: SNAPSHOT_MANAGED_MAILBOX_CREDITS,
|
|
353
|
+
});
|
|
354
|
+
/** What a managed Google inbox costs a month, all-in: mailbox, connection and warm-up. */
|
|
355
|
+
export const MANAGED_INBOX_ALL_IN_REPRICING = defineRepricedValue({
|
|
356
|
+
key: "managed_inbox.google_all_in_credits",
|
|
357
|
+
kind: "price",
|
|
358
|
+
slice: "S32",
|
|
359
|
+
decision: "3.1",
|
|
360
|
+
unit: "credits_per_inbox_per_month",
|
|
361
|
+
description: "A managed Google inbox, all-in: the mailbox plus its mailbox connection, warm-up included. Inbox placement is an optional add-on.",
|
|
362
|
+
before: 400,
|
|
363
|
+
after: SNAPSHOT_MANAGED_MAILBOX_CREDITS + SNAPSHOT_SENDING_MAILBOX_CREDITS,
|
|
364
|
+
});
|
|
365
|
+
/** A managed Google inbox's monthly credits, all-in, at the switch state (marketing and docs). */
|
|
366
|
+
export function managedInboxAllInCredits(options = {}) {
|
|
367
|
+
return repricedValue(MANAGED_INBOX_ALL_IN_REPRICING, options);
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* S41 (decision 6.3): a provider call made with the customer's own API key
|
|
371
|
+
* (BYOK) pays a 0.05-credit platform fee, up from nothing. The vendor still
|
|
372
|
+
* bills the call itself. The seed and the generated snapshot carry the fee; it
|
|
373
|
+
* is 0 until the switch's instant. Standing permissions approved while BYOK was
|
|
374
|
+
* free (armed auto-runs, workflow triggers) pay it from then on inside the
|
|
375
|
+
* per-delivery ceiling they already carry (proposal P-67).
|
|
376
|
+
*/
|
|
377
|
+
export const BYOK_PLATFORM_FEE_REPRICING = defineRepricedValue({
|
|
378
|
+
key: "byok.platform_fee_credits",
|
|
379
|
+
kind: "price",
|
|
380
|
+
slice: "S41",
|
|
381
|
+
decision: "6.3",
|
|
382
|
+
unit: "credits per BYOK call",
|
|
383
|
+
description: "Platform fee on each provider call made with your own API key. The provider bills the call itself.",
|
|
384
|
+
before: 0,
|
|
385
|
+
after: SNAPSHOT_BYOK_PLATFORM_FEE_CREDITS,
|
|
386
|
+
});
|
|
387
|
+
// ---- S56 (L5.1, L5.2, L5.4): spend-safety defaults that TIGHTEN. ----------
|
|
388
|
+
// Only the cuts are scheduled. Every other L5 change is a larger default or a
|
|
389
|
+
// larger allowance and ships live on release (spec § 6.1): the L5.1 ceilings of
|
|
390
|
+
// the $99-$999 plans and of grandfathered plans, and every L5.3 Copilot
|
|
391
|
+
// allowance.
|
|
392
|
+
/**
|
|
393
|
+
* L5.1 (decision L5.1; the free value and the rounding are PROPOSED P-54): the
|
|
394
|
+
* default credit ceiling of one unattended delivery is 10% of the plan's
|
|
395
|
+
* monthly credits, rounded half up. Integer arithmetic, because
|
|
396
|
+
* `229_885 * 0.1` is 22988.4999... in floating point and rounds the wrong way.
|
|
397
|
+
*/
|
|
398
|
+
export function tenthOfMonthlyCreditsRoundedHalfUp(monthlyCredits) {
|
|
399
|
+
return Math.floor((Math.round(monthlyCredits) + 5) / 10);
|
|
400
|
+
}
|
|
401
|
+
function snapshotMonthlyCredits(planKey) {
|
|
402
|
+
const credits = PRICING_PLAN_SNAPSHOT.find((plan) => plan.planKey === planKey)?.monthlyCredits;
|
|
403
|
+
if (typeof credits !== "number" || !(credits > 0)) {
|
|
404
|
+
throw new Error(`repricing: the pricing snapshot has no monthly credits for ${planKey}`);
|
|
405
|
+
}
|
|
406
|
+
return credits;
|
|
407
|
+
}
|
|
408
|
+
// The two plan sizes whose 10% default falls below the ceiling they read today
|
|
409
|
+
// (the starter rung's 500 and the scale rung's 25,000). The new value follows
|
|
410
|
+
// the plan's monthly credits in the pricing snapshot, so the volume bonus (S11)
|
|
411
|
+
// moves it with the grant. `plan-band.test.ts` pins this list to exactly the
|
|
412
|
+
// sizes whose target is below today's value.
|
|
413
|
+
const DELIVERY_CEILING_CUTS = [
|
|
414
|
+
{ band: "49", before: 500, plans: "the $49 plan" },
|
|
415
|
+
{ band: "1999", before: 25_000, plans: "the $1,999 plan and legacy scale plans" },
|
|
416
|
+
];
|
|
417
|
+
export const S56_DELIVERY_CREDIT_CEILING_CUTS = DELIVERY_CEILING_CUTS.map(({ band, before, plans }) => defineRepricedValue({
|
|
418
|
+
key: `spend.${band}.default_delivery_credit_ceiling`,
|
|
419
|
+
kind: "limit",
|
|
420
|
+
slice: "S56",
|
|
421
|
+
decision: "L5.1",
|
|
422
|
+
unit: "credits_per_delivery",
|
|
423
|
+
description: `Default credit ceiling of one unattended workflow trigger delivery or table auto-run batch armed without max_credits, on ${plans}. An explicit max_credits always wins.`,
|
|
424
|
+
before,
|
|
425
|
+
after: tenthOfMonthlyCreditsRoundedHalfUp(snapshotMonthlyCredits(`oxygen_${band}`)),
|
|
426
|
+
}));
|
|
427
|
+
/**
|
|
428
|
+
* L5.2 (decision L5.2): the free plan gains a default daily runaway guard. Free
|
|
429
|
+
* had none, because the guard scales from a monthly grant free does not have,
|
|
430
|
+
* so the guard itself is the tightening: no bound before the instant.
|
|
431
|
+
*/
|
|
432
|
+
export const FREE_ORG_DAILY_GUARD_WARN_CREDITS = defineRepricedValue({
|
|
433
|
+
key: "spend.free.org_daily_guard_warn_credits",
|
|
434
|
+
kind: "limit",
|
|
435
|
+
slice: "S56",
|
|
436
|
+
decision: "L5.2",
|
|
437
|
+
unit: "credits_per_utc_day",
|
|
438
|
+
description: "Default daily spend guard on the free plan: managed spend past this in one UTC day raises a warning.",
|
|
439
|
+
before: null,
|
|
440
|
+
after: 1_000,
|
|
441
|
+
});
|
|
442
|
+
export const FREE_ORG_DAILY_GUARD_BLOCK_CREDITS = defineRepricedValue({
|
|
443
|
+
key: "spend.free.org_daily_guard_block_credits",
|
|
444
|
+
kind: "limit",
|
|
445
|
+
slice: "S56",
|
|
446
|
+
decision: "L5.2",
|
|
447
|
+
unit: "credits_per_utc_day",
|
|
448
|
+
description: "Default daily spend guard on the free plan: managed spend past this in one UTC day is refused, unless you set your own daily budget.",
|
|
449
|
+
before: null,
|
|
450
|
+
after: 5_000,
|
|
451
|
+
});
|
|
452
|
+
/**
|
|
453
|
+
* L5.4 (decision L5.4; the inference half is PROPOSED P-56): the default credit
|
|
454
|
+
* ceilings of one Agent run on the team and scale limits rungs, which the $999
|
|
455
|
+
* and $1,999 plans, grandfathered Team plans and legacy scale plans read. A
|
|
456
|
+
* customer can still approve a higher ceiling for a single run.
|
|
457
|
+
*/
|
|
458
|
+
export const S56_AGENT_RUN_LIMIT_CUTS = [
|
|
459
|
+
{ tier: "team", plans: "the $999 plan and grandfathered Team plans", total: 5_000, inference: 2_500 },
|
|
460
|
+
{ tier: "scale", plans: "the $1,999 plan and legacy scale plans", total: 10_000, inference: 5_000 },
|
|
461
|
+
].flatMap(({ tier, plans, total, inference }) => [
|
|
462
|
+
defineRepricedValue({
|
|
463
|
+
key: `agents.${tier}.max_total_credits`,
|
|
464
|
+
kind: "limit",
|
|
465
|
+
slice: "S56",
|
|
466
|
+
decision: "L5.4",
|
|
467
|
+
unit: "credits_per_agent_run",
|
|
468
|
+
description: `Most credits one Agent run may use in total on ${plans}, unless you approve a higher ceiling for that run.`,
|
|
469
|
+
before: total,
|
|
470
|
+
after: 2_500,
|
|
471
|
+
}),
|
|
472
|
+
defineRepricedValue({
|
|
473
|
+
key: `agents.${tier}.max_inference_credits`,
|
|
474
|
+
kind: "limit",
|
|
475
|
+
slice: "S56",
|
|
476
|
+
decision: "L5.4 (P-56)",
|
|
477
|
+
unit: "credits_per_agent_run",
|
|
478
|
+
description: `Most credits one Agent run may spend on its own model calls on ${plans}.`,
|
|
479
|
+
before: inference,
|
|
480
|
+
after: 1_250,
|
|
481
|
+
}),
|
|
482
|
+
]);
|
|
483
|
+
/**
|
|
484
|
+
* S53 (decision L4.2, ratified 2026-09-26): a free workspace may schedule a
|
|
485
|
+
* workflow or Agent cron at most every 15 minutes, down from every minute. Paid
|
|
486
|
+
* plans keep every minute. A new schedule that fires closer together is refused
|
|
487
|
+
* with `cron_interval_below_plan_minimum`; one already armed keeps running at the
|
|
488
|
+
* 15-minute interval instead and is never deleted (proposal P-47).
|
|
489
|
+
* `resolveCronMinIntervalMinutes` in plan-limits.ts resolves it, and a test pins
|
|
490
|
+
* `after` to `PLAN_LIMITS.free.workflows.cronMinIntervalMinutes`.
|
|
491
|
+
*/
|
|
492
|
+
export const FREE_CRON_MIN_INTERVAL_REPRICING = defineRepricedValue({
|
|
493
|
+
key: "workflows.free.cron_min_interval_minutes",
|
|
494
|
+
kind: "limit",
|
|
495
|
+
slice: "S53",
|
|
496
|
+
decision: "L4.2",
|
|
497
|
+
unit: "minutes",
|
|
498
|
+
description: "Fastest cron schedule on the free plan. A faster new schedule is refused; one already armed runs at this interval instead.",
|
|
499
|
+
before: 1,
|
|
500
|
+
after: 15,
|
|
501
|
+
});
|
|
502
|
+
// ---- S42 (decision 4.4, bugs B2 and B3): person-reveal pricing. ----------
|
|
503
|
+
// Apollo and Lusha person enrichment are priced by what the request reveals,
|
|
504
|
+
// per record. The decreases ship live (Apollo email-only 100 → 12.5 and a
|
|
505
|
+
// small Apollo bulk request); what rises waits for the switch: a Lusha phone
|
|
506
|
+
// reveal, and pricing per contact or record where a request used to cost one
|
|
507
|
+
// flat price whatever it held. `pricing-resolver.ts` (@oxygen/integrations)
|
|
508
|
+
// holds a request at its old per-request price until the instant, and a test
|
|
509
|
+
// there pins each `after` to the pricing seed in `cogs-rates.ts`.
|
|
510
|
+
/** A Lusha person enrichment that reveals a phone number, per contact. */
|
|
511
|
+
export const LUSHA_PHONE_REVEAL_CREDITS_REPRICING = defineRepricedValue({
|
|
512
|
+
key: "enrichment.lusha.person_enrich_phone_credits",
|
|
513
|
+
kind: "price",
|
|
514
|
+
slice: "S42",
|
|
515
|
+
decision: "4.4",
|
|
516
|
+
unit: "credits_per_contact",
|
|
517
|
+
description: "Lusha person enrichment that reveals a phone number (5 Lusha credits), per contact.",
|
|
518
|
+
before: 43.5,
|
|
519
|
+
after: 217.5,
|
|
520
|
+
});
|
|
521
|
+
/** A Lusha person enrichment that reveals an email and a phone number, per contact. */
|
|
522
|
+
export const LUSHA_EMAIL_AND_PHONE_REVEAL_CREDITS_REPRICING = defineRepricedValue({
|
|
523
|
+
key: "enrichment.lusha.person_enrich_email_and_phone_credits",
|
|
524
|
+
kind: "price",
|
|
525
|
+
slice: "S42",
|
|
526
|
+
decision: "4.4",
|
|
527
|
+
unit: "credits_per_contact",
|
|
528
|
+
description: "Lusha person enrichment that reveals an email and a phone number (6 Lusha credits), per contact.",
|
|
529
|
+
before: 43.5,
|
|
530
|
+
after: 261,
|
|
531
|
+
});
|
|
532
|
+
/**
|
|
533
|
+
* Before the instant a Lusha or Apollo bulk person enrichment never costs more
|
|
534
|
+
* than its old flat price per request (43.5 and 100); from it on, it costs its
|
|
535
|
+
* per-record price times the contacts or records it holds.
|
|
536
|
+
*/
|
|
537
|
+
export const PERSON_REVEAL_PRICED_PER_RECORD_REPRICING = defineRepricedValue({
|
|
538
|
+
key: "enrichment.person_reveal_priced_per_record",
|
|
539
|
+
kind: "price",
|
|
540
|
+
slice: "S42",
|
|
541
|
+
decision: "4.4",
|
|
542
|
+
unit: "boolean",
|
|
543
|
+
description: "Lusha person enrichment and Apollo bulk person enrichment are priced per contact or record, not per request.",
|
|
544
|
+
before: false,
|
|
545
|
+
after: true,
|
|
546
|
+
});
|
|
547
|
+
export const S42_REVEAL_PRICE_RISES = [
|
|
548
|
+
LUSHA_PHONE_REVEAL_CREDITS_REPRICING,
|
|
549
|
+
LUSHA_EMAIL_AND_PHONE_REVEAL_CREDITS_REPRICING,
|
|
550
|
+
PERSON_REVEAL_PRICED_PER_RECORD_REPRICING,
|
|
551
|
+
];
|
|
552
|
+
// ---- S43 (decision 4.2): a LinkedIn URL at a flat price per URL found. ----
|
|
553
|
+
// Until the instant, each lane of the LinkedIn-URL waterfall bills its own
|
|
554
|
+
// price on every attempt, found or not (the managed rail lookup about 2, Icypeas
|
|
555
|
+
// about 2.5). From it, the default chain bills a flat 5 credits only when it
|
|
556
|
+
// finds a URL that passes the identity match (P-35), and a miss costs 0. The
|
|
557
|
+
// price per hit rises, so the whole change waits for the switch. Lanes named
|
|
558
|
+
// outside the default chain keep their own price, and a direct tool call keeps
|
|
559
|
+
// its tool price (P-34). `pricing-resolver.ts` (@oxygen/integrations) applies
|
|
560
|
+
// it; a test there pins the flat price to the pricing seed.
|
|
561
|
+
/** Whether the LinkedIn-URL waterfall bills a flat price per URL found. */
|
|
562
|
+
export const LINKEDIN_URL_FLAT_PRICE_REPRICING = defineRepricedValue({
|
|
563
|
+
key: "enrichment.linkedin_url_priced_per_found_url",
|
|
564
|
+
kind: "price",
|
|
565
|
+
slice: "S43",
|
|
566
|
+
decision: "4.2",
|
|
567
|
+
unit: "boolean",
|
|
568
|
+
description: `A LinkedIn URL costs a flat ${SNAPSHOT_LINKEDIN_URL_FOUND_CREDITS} credits when one is found that matches the person, and nothing when none is found, instead of each provider's price on every attempt.`,
|
|
569
|
+
before: false,
|
|
570
|
+
after: true,
|
|
571
|
+
});
|
|
572
|
+
/** True once a LinkedIn URL is priced per URL found (S43). */
|
|
573
|
+
export function linkedinUrlFlatPriceInForce(options = {}) {
|
|
574
|
+
return repricedValue(LINKEDIN_URL_FLAT_PRICE_REPRICING, options);
|
|
575
|
+
}
|
|
576
|
+
/** The flat price of one LinkedIn URL found, in credits (the pricing seed's `enrichment.linkedin_url_found`). */
|
|
577
|
+
export function linkedinUrlFoundCredits() {
|
|
578
|
+
return SNAPSHOT_LINKEDIN_URL_FOUND_CREDITS;
|
|
579
|
+
}
|
|
580
|
+
function snapshotByokEnabled(planKey) {
|
|
581
|
+
const plan = PRICING_PLAN_SNAPSHOT.find((row) => row.planKey === planKey);
|
|
582
|
+
if (!plan)
|
|
583
|
+
throw new Error(`repricing: the pricing snapshot has no plan ${planKey}`);
|
|
584
|
+
return plan.byokEnabled;
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* S22 (decision 2.7): bringing your own provider keys (BYOK) needs the $99 plan
|
|
588
|
+
* or above. The pricing seed and snapshot carry the new value (`oxygen_49`
|
|
589
|
+
* `byokEnabled: false`); `resolveBasePricingPlan` holds the $49 plan at the old
|
|
590
|
+
* value until the switch's instant. From the instant on, a key the workspace
|
|
591
|
+
* connected before it keeps working until the plan changes (P-20, proposed):
|
|
592
|
+
* `byokConnectionGrandfathered` in plan-capabilities.ts.
|
|
593
|
+
*/
|
|
594
|
+
export const OXYGEN_49_BYOK_REPRICING = defineRepricedValue({
|
|
595
|
+
key: "plans.oxygen_49.byok_enabled",
|
|
596
|
+
kind: "limit",
|
|
597
|
+
slice: "S22",
|
|
598
|
+
decision: "2.7",
|
|
599
|
+
unit: "boolean",
|
|
600
|
+
description: "Whether the $49 plan may connect your own provider API keys. From this date bringing your own keys needs the $99 plan or above; keys connected before it keep working until the plan changes.",
|
|
601
|
+
before: true,
|
|
602
|
+
after: snapshotByokEnabled("oxygen_49"),
|
|
603
|
+
});
|
|
219
604
|
/**
|
|
220
605
|
* Every value the 2026-09 switch moves. Each dark slice appends its entries
|
|
221
606
|
* here, so `oxygen limits show` can list them once a date is set and one test
|
|
@@ -223,10 +608,26 @@ export const S20_RESERVATION_PRICE_RISES = [
|
|
|
223
608
|
* after it.
|
|
224
609
|
*/
|
|
225
610
|
export const REPRICING_2026_09_SCHEDULE = [
|
|
611
|
+
TOPUP_RATE_REPRICING,
|
|
226
612
|
...storageCutEntries(),
|
|
227
613
|
...S20_RESERVATION_PRICE_RISES,
|
|
614
|
+
DELIVERABILITY_SEAT_REPRICING,
|
|
615
|
+
PLACEMENT_TEST_REPRICING,
|
|
616
|
+
MANAGED_MAILBOX_REPRICING,
|
|
617
|
+
MANAGED_INBOX_ALL_IN_REPRICING,
|
|
228
618
|
EMAIL_MAILBOX_DAILY_CAP_MAXIMUM,
|
|
229
619
|
EMAIL_MAILBOX_OLD_DEFAULT_DAILY_CAP,
|
|
620
|
+
AUTOMATION_STEP_CREDITS_REPRICING,
|
|
621
|
+
AUTOMATION_STEPS_COUNT_TOWARD_RUN_CAP_REPRICING,
|
|
622
|
+
BYOK_PLATFORM_FEE_REPRICING,
|
|
623
|
+
FREE_CRON_MIN_INTERVAL_REPRICING,
|
|
624
|
+
...S56_DELIVERY_CREDIT_CEILING_CUTS,
|
|
625
|
+
FREE_ORG_DAILY_GUARD_WARN_CREDITS,
|
|
626
|
+
FREE_ORG_DAILY_GUARD_BLOCK_CREDITS,
|
|
627
|
+
...S56_AGENT_RUN_LIMIT_CUTS,
|
|
628
|
+
...S42_REVEAL_PRICE_RISES,
|
|
629
|
+
LINKEDIN_URL_FLAT_PRICE_REPRICING,
|
|
630
|
+
OXYGEN_49_BYOK_REPRICING,
|
|
230
631
|
];
|
|
231
632
|
/** The schedule entry with this key, or undefined. */
|
|
232
633
|
export function findRepricedValue(key, schedule = REPRICING_2026_09_SCHEDULE) {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type SemanticVersion = {
|
|
2
|
+
major: number;
|
|
3
|
+
minor: number;
|
|
4
|
+
patch: number;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Parse a three-segment semantic version (e.g. `1.142.17`). Pre-release and
|
|
8
|
+
* build metadata suffixes (`-rc.1`, `+build`) are tolerated but ignored.
|
|
9
|
+
* Returns `null` when the input is not a parseable `major.minor.patch` string.
|
|
10
|
+
*/
|
|
11
|
+
export declare function parseSemver(version: string): SemanticVersion | null;
|
|
12
|
+
/**
|
|
13
|
+
* Compare two semantic versions. Returns -1 when `a < b`, 1 when `a > b`, and
|
|
14
|
+
* 0 when they are equal. Unparseable inputs compare as equal (0) so callers
|
|
15
|
+
* fail open rather than misordering garbage.
|
|
16
|
+
*/
|
|
17
|
+
export declare function compareSemver(a: string, b: string): -1 | 0 | 1;
|
|
18
|
+
/** True when `a` is a strictly greater semantic version than `b`. */
|
|
19
|
+
export declare function isVersionGreater(a: string, b: string): boolean;
|
|
20
|
+
/** True when `a` is a strictly lesser semantic version than `b`. */
|
|
21
|
+
export declare function isVersionLess(a: string, b: string): boolean;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a three-segment semantic version (e.g. `1.142.17`). Pre-release and
|
|
3
|
+
* build metadata suffixes (`-rc.1`, `+build`) are tolerated but ignored.
|
|
4
|
+
* Returns `null` when the input is not a parseable `major.minor.patch` string.
|
|
5
|
+
*/
|
|
6
|
+
export function parseSemver(version) {
|
|
7
|
+
const match = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(version);
|
|
8
|
+
if (!match)
|
|
9
|
+
return null;
|
|
10
|
+
return {
|
|
11
|
+
major: Number(match[1]),
|
|
12
|
+
minor: Number(match[2]),
|
|
13
|
+
patch: Number(match[3]),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Compare two semantic versions. Returns -1 when `a < b`, 1 when `a > b`, and
|
|
18
|
+
* 0 when they are equal. Unparseable inputs compare as equal (0) so callers
|
|
19
|
+
* fail open rather than misordering garbage.
|
|
20
|
+
*/
|
|
21
|
+
export function compareSemver(a, b) {
|
|
22
|
+
const left = parseSemver(a);
|
|
23
|
+
const right = parseSemver(b);
|
|
24
|
+
if (!left || !right)
|
|
25
|
+
return 0;
|
|
26
|
+
for (const key of ["major", "minor", "patch"]) {
|
|
27
|
+
if (left[key] > right[key])
|
|
28
|
+
return 1;
|
|
29
|
+
if (left[key] < right[key])
|
|
30
|
+
return -1;
|
|
31
|
+
}
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
/** True when `a` is a strictly greater semantic version than `b`. */
|
|
35
|
+
export function isVersionGreater(a, b) {
|
|
36
|
+
return compareSemver(a, b) > 0;
|
|
37
|
+
}
|
|
38
|
+
/** True when `a` is a strictly lesser semantic version than `b`. */
|
|
39
|
+
export function isVersionLess(a, b) {
|
|
40
|
+
return compareSemver(a, b) < 0;
|
|
41
|
+
}
|