@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.
Files changed (100) hide show
  1. package/README.md +1 -1
  2. package/dist/auto-update.d.ts +129 -0
  3. package/dist/auto-update.js +392 -0
  4. package/dist/command-manifest.js +14 -0
  5. package/dist/credentials.d.ts +2 -0
  6. package/dist/credentials.js +6 -3
  7. package/dist/functions-commands.js +1 -1
  8. package/dist/http-client.js +28 -4
  9. package/dist/index.js +583 -145
  10. package/dist/run-wait.d.ts +3 -1
  11. package/dist/run-wait.js +19 -5
  12. package/dist/streamed-file-import.d.ts +58 -0
  13. package/dist/streamed-file-import.js +115 -0
  14. package/dist/update.d.ts +29 -0
  15. package/dist/update.js +62 -16
  16. package/dist/workflow-plan-limit-notices.d.ts +8 -0
  17. package/dist/workflow-plan-limit-notices.js +28 -0
  18. package/node_modules/@oxygen/cli-ugc/dist/commands.js +3 -3
  19. package/node_modules/@oxygen/shared/dist/billing-anchors.d.ts +17 -0
  20. package/node_modules/@oxygen/shared/dist/billing-anchors.js +27 -0
  21. package/node_modules/@oxygen/shared/dist/billing.d.ts +191 -35
  22. package/node_modules/@oxygen/shared/dist/billing.js +333 -42
  23. package/node_modules/@oxygen/shared/dist/capability-discovery.js +55 -5
  24. package/node_modules/@oxygen/shared/dist/copilot-skills.generated.d.ts +2 -2
  25. package/node_modules/@oxygen/shared/dist/copilot-skills.generated.js +2 -2
  26. package/node_modules/@oxygen/shared/dist/cost-estimate-view.d.ts +50 -0
  27. package/node_modules/@oxygen/shared/dist/cost-estimate-view.js +90 -0
  28. package/node_modules/@oxygen/shared/dist/cost-estimate.d.ts +167 -0
  29. package/node_modules/@oxygen/shared/dist/cost-estimate.js +361 -0
  30. package/node_modules/@oxygen/shared/dist/credit-gate.d.ts +26 -0
  31. package/node_modules/@oxygen/shared/dist/credit-gate.js +65 -0
  32. package/node_modules/@oxygen/shared/dist/email-deliverability-policy.d.ts +51 -0
  33. package/node_modules/@oxygen/shared/dist/email-deliverability-policy.js +101 -0
  34. package/node_modules/@oxygen/shared/dist/email-hard-bounce.d.ts +3 -1
  35. package/node_modules/@oxygen/shared/dist/email-hard-bounce.js +3 -3
  36. package/node_modules/@oxygen/shared/dist/error-redaction.d.ts +1 -1
  37. package/node_modules/@oxygen/shared/dist/error-redaction.js +1 -1
  38. package/node_modules/@oxygen/shared/dist/feature-gates.d.ts +4 -0
  39. package/node_modules/@oxygen/shared/dist/feature-gates.js +5 -0
  40. package/node_modules/@oxygen/shared/dist/file-import.d.ts +13 -1
  41. package/node_modules/@oxygen/shared/dist/file-import.js +33 -6
  42. package/node_modules/@oxygen/shared/dist/hosted-ai.d.ts +73 -3
  43. package/node_modules/@oxygen/shared/dist/hosted-ai.js +246 -24
  44. package/node_modules/@oxygen/shared/dist/import-limits.d.ts +25 -1
  45. package/node_modules/@oxygen/shared/dist/import-limits.js +35 -2
  46. package/node_modules/@oxygen/shared/dist/index.d.ts +2 -22
  47. package/node_modules/@oxygen/shared/dist/index.js +2 -42
  48. package/node_modules/@oxygen/shared/dist/object-storage.d.ts +9 -0
  49. package/node_modules/@oxygen/shared/dist/object-storage.js +17 -0
  50. package/node_modules/@oxygen/shared/dist/operational-telemetry.d.ts +41 -0
  51. package/node_modules/@oxygen/shared/dist/operational-telemetry.js +55 -0
  52. package/node_modules/@oxygen/shared/dist/plan-band.d.ts +117 -1
  53. package/node_modules/@oxygen/shared/dist/plan-band.js +175 -10
  54. package/node_modules/@oxygen/shared/dist/plan-capabilities.d.ts +77 -7
  55. package/node_modules/@oxygen/shared/dist/plan-capabilities.js +87 -7
  56. package/node_modules/@oxygen/shared/dist/plan-limits-view.d.ts +219 -0
  57. package/node_modules/@oxygen/shared/dist/plan-limits-view.js +330 -0
  58. package/node_modules/@oxygen/shared/dist/plan-limits.d.ts +204 -6
  59. package/node_modules/@oxygen/shared/dist/plan-limits.js +197 -15
  60. package/node_modules/@oxygen/shared/dist/pricing-sheet.d.ts +80 -36
  61. package/node_modules/@oxygen/shared/dist/pricing-sheet.js +80 -31
  62. package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.d.ts +38 -20
  63. package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.js +47 -34
  64. package/node_modules/@oxygen/shared/dist/provider-http-error.d.ts +10 -0
  65. package/node_modules/@oxygen/shared/dist/provider-http-error.js +27 -0
  66. package/node_modules/@oxygen/shared/dist/repricing.d.ts +127 -0
  67. package/node_modules/@oxygen/shared/dist/repricing.js +407 -6
  68. package/node_modules/@oxygen/shared/dist/semver.d.ts +21 -0
  69. package/node_modules/@oxygen/shared/dist/semver.js +41 -0
  70. package/node_modules/@oxygen/shared/dist/sending-limits.d.ts +5 -7
  71. package/node_modules/@oxygen/shared/dist/sending-limits.js +10 -16
  72. package/node_modules/@oxygen/shared/dist/sending-seats.d.ts +18 -15
  73. package/node_modules/@oxygen/shared/dist/sending-seats.js +22 -17
  74. package/node_modules/@oxygen/shared/dist/spend-safety.d.ts +57 -8
  75. package/node_modules/@oxygen/shared/dist/spend-safety.js +64 -11
  76. package/node_modules/@oxygen/shared/dist/stripe-price-catalog.d.ts +15 -7
  77. package/node_modules/@oxygen/shared/dist/stripe-price-catalog.js +18 -5
  78. package/node_modules/@oxygen/shared/dist/table-capacity.d.ts +34 -5
  79. package/node_modules/@oxygen/shared/dist/table-capacity.js +25 -8
  80. package/node_modules/@oxygen/shared/dist/telemetry-export-observer.d.ts +6 -0
  81. package/node_modules/@oxygen/shared/dist/telemetry-export-observer.js +13 -5
  82. package/node_modules/@oxygen/shared/dist/telemetry-resource.d.ts +40 -0
  83. package/node_modules/@oxygen/shared/dist/telemetry-resource.js +35 -0
  84. package/node_modules/@oxygen/shared/dist/telemetry.js +5 -0
  85. package/node_modules/@oxygen/shared/dist/ugc.d.ts +15 -0
  86. package/node_modules/@oxygen/shared/dist/ugc.js +29 -0
  87. package/node_modules/@oxygen/shared/dist/version.d.ts +1 -3
  88. package/node_modules/@oxygen/shared/dist/version.generated.d.ts +1 -1
  89. package/node_modules/@oxygen/shared/dist/version.generated.js +1 -1
  90. package/node_modules/@oxygen/shared/dist/version.js +14 -27
  91. package/node_modules/@oxygen/shared/dist/workspace-file-storage.d.ts +5 -0
  92. package/node_modules/@oxygen/shared/dist/workspace-file-storage.js +5 -0
  93. package/node_modules/@oxygen/workflows/dist/graph/manifest-schema.d.ts +3 -3
  94. package/node_modules/@oxygen/workflows/dist/graph/types.d.ts +15 -1
  95. package/node_modules/@oxygen/workflows/dist/graph/types.js +15 -1
  96. package/node_modules/@oxygen/workflows/dist/index.d.ts +45 -0
  97. package/node_modules/@oxygen/workflows/dist/index.js +152 -2
  98. package/node_modules/@oxygen/workflows/dist/usage-estimate.d.ts +10 -1
  99. package/node_modules/@oxygen/workflows/dist/usage-estimate.js +33 -29
  100. 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
- // No imports on purpose: `@oxygen/shared` ships inside the published CLI, and
45
- // "use client" components may import the schedule. A client component cannot
46
- // read the server's environment, though, so a page that shows a price in force
47
- // must resolve it on the server and pass it down.
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. Today every band has 3,000,000 rows per Table, 25,000,000 per
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 rise only after the capacity test at 5M and 10M rows, so only
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
+ }