@oxygen-agent/cli 1.987.20 → 1.1010.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.
Files changed (176) hide show
  1. package/README.md +1 -1
  2. package/dist/admin-primary-providers-render.d.ts +0 -2
  3. package/dist/admin-primary-providers-render.js +1 -1
  4. package/dist/browser-login.js +1 -4
  5. package/dist/column-decision-options.d.ts +20 -0
  6. package/dist/column-decision-options.js +54 -0
  7. package/dist/command-manifest.d.ts +3 -2
  8. package/dist/command-manifest.js +25 -2
  9. package/dist/credentials.d.ts +1 -1
  10. package/dist/functions-commands.js +13 -9
  11. package/dist/help.d.ts +8 -0
  12. package/dist/help.js +46 -0
  13. package/dist/index.js +2751 -242
  14. package/dist/knowledge-mirror.d.ts +2 -2
  15. package/dist/runtime.d.ts +0 -15
  16. package/dist/runtime.js +1 -1
  17. package/dist/search-ai-filter-notice.d.ts +17 -0
  18. package/dist/search-ai-filter-notice.js +38 -0
  19. package/dist/session.d.ts +4 -3
  20. package/dist/skills.d.ts +8 -7
  21. package/dist/skills.js +58 -20
  22. package/dist/transcript.d.ts +2 -1
  23. package/dist/util.d.ts +10 -1
  24. package/dist/util.js +14 -2
  25. package/node_modules/@oxygen/cli-ugc/dist/commands.js +296 -140
  26. package/node_modules/@oxygen/cli-ugc/dist/field-parser.d.ts +9 -0
  27. package/node_modules/@oxygen/cli-ugc/dist/field-parser.js +34 -0
  28. package/node_modules/@oxygen/formula/dist/coerce.d.ts +10 -0
  29. package/node_modules/@oxygen/formula/dist/coerce.js +10 -0
  30. package/node_modules/@oxygen/formula/dist/formula-functions.js +65 -0
  31. package/node_modules/@oxygen/formula/dist/hash.d.ts +19 -0
  32. package/node_modules/@oxygen/formula/dist/hash.js +199 -0
  33. package/node_modules/@oxygen/formula/dist/value-cleaners.d.ts +6 -1
  34. package/node_modules/@oxygen/formula/dist/value-cleaners.js +10 -26
  35. package/node_modules/@oxygen/recipe-sdk/dist/index.d.ts +13 -0
  36. package/node_modules/@oxygen/shared/dist/array-utils.d.ts +5 -0
  37. package/node_modules/@oxygen/shared/dist/array-utils.js +11 -0
  38. package/node_modules/@oxygen/shared/dist/billing.d.ts +99 -22
  39. package/node_modules/@oxygen/shared/dist/billing.js +195 -40
  40. package/node_modules/@oxygen/shared/dist/byok-connect.js +5 -0
  41. package/node_modules/@oxygen/shared/dist/capability-discovery.d.ts +27 -0
  42. package/node_modules/@oxygen/shared/dist/capability-discovery.js +311 -28
  43. package/node_modules/@oxygen/shared/dist/cli-http-error.d.ts +8 -0
  44. package/node_modules/@oxygen/shared/dist/cli-http-error.js +8 -0
  45. package/node_modules/@oxygen/shared/dist/cli-result.js +1 -0
  46. package/node_modules/@oxygen/shared/dist/column-autofill.d.ts +52 -0
  47. package/node_modules/@oxygen/shared/dist/column-autofill.js +62 -0
  48. package/node_modules/@oxygen/shared/dist/column-decision.d.ts +50 -0
  49. package/node_modules/@oxygen/shared/dist/column-decision.js +228 -0
  50. package/node_modules/@oxygen/shared/dist/column-output-fields.js +2 -6
  51. package/node_modules/@oxygen/shared/dist/company-enrichment-fields.d.ts +113 -0
  52. package/node_modules/@oxygen/shared/dist/company-enrichment-fields.js +548 -0
  53. package/node_modules/@oxygen/shared/dist/copilot-skills.generated.d.ts +6 -6
  54. package/node_modules/@oxygen/shared/dist/copilot-skills.generated.js +6 -6
  55. package/node_modules/@oxygen/shared/dist/cutover-freeze.d.ts +26 -0
  56. package/node_modules/@oxygen/shared/dist/cutover-freeze.js +52 -0
  57. package/node_modules/@oxygen/shared/dist/data-suppliers.d.ts +57 -0
  58. package/node_modules/@oxygen/shared/dist/data-suppliers.js +59 -0
  59. package/node_modules/@oxygen/shared/dist/deploy-env.d.ts +74 -0
  60. package/node_modules/@oxygen/shared/dist/deploy-env.js +82 -0
  61. package/node_modules/@oxygen/shared/dist/dnc-rules.d.ts +130 -0
  62. package/node_modules/@oxygen/shared/dist/dnc-rules.js +221 -0
  63. package/node_modules/@oxygen/shared/dist/enrichment-intents.d.ts +107 -0
  64. package/node_modules/@oxygen/shared/dist/enrichment-intents.js +809 -0
  65. package/node_modules/@oxygen/shared/dist/error-message.d.ts +1 -0
  66. package/node_modules/@oxygen/shared/dist/error-message.js +3 -0
  67. package/node_modules/@oxygen/shared/dist/error-redaction.js +1 -3
  68. package/node_modules/@oxygen/shared/dist/external-write-policy.d.ts +33 -0
  69. package/node_modules/@oxygen/shared/dist/external-write-policy.js +68 -0
  70. package/node_modules/@oxygen/shared/dist/format-percent.d.ts +8 -0
  71. package/node_modules/@oxygen/shared/dist/format-percent.js +13 -0
  72. package/node_modules/@oxygen/shared/dist/freemail-domains.d.ts +81 -0
  73. package/node_modules/@oxygen/shared/dist/freemail-domains.js +157 -0
  74. package/node_modules/@oxygen/shared/dist/future-signup-lifecycle-projection.d.ts +1 -0
  75. package/node_modules/@oxygen/shared/dist/future-signup-lifecycle-projection.js +1 -1
  76. package/node_modules/@oxygen/shared/dist/hosted-ai.d.ts +60 -4
  77. package/node_modules/@oxygen/shared/dist/hosted-ai.js +125 -10
  78. package/node_modules/@oxygen/shared/dist/index.d.ts +15 -0
  79. package/node_modules/@oxygen/shared/dist/index.js +15 -0
  80. package/node_modules/@oxygen/shared/dist/json-path.js +1 -3
  81. package/node_modules/@oxygen/shared/dist/knowledge-bases.js +1 -3
  82. package/node_modules/@oxygen/shared/dist/knowledge-bootstrap.d.ts +2 -2
  83. package/node_modules/@oxygen/shared/dist/knowledge-bootstrap.js +2 -2
  84. package/node_modules/@oxygen/shared/dist/langfuse.d.ts +44 -1
  85. package/node_modules/@oxygen/shared/dist/langfuse.js +416 -14
  86. package/node_modules/@oxygen/shared/dist/linkedin-countries.d.ts +33 -0
  87. package/node_modules/@oxygen/shared/dist/linkedin-countries.js +361 -0
  88. package/node_modules/@oxygen/shared/dist/linkedin-country-timezones.d.ts +24 -0
  89. package/node_modules/@oxygen/shared/dist/linkedin-country-timezones.js +276 -0
  90. package/node_modules/@oxygen/shared/dist/linkedin-message-deletion.d.ts +2 -0
  91. package/node_modules/@oxygen/shared/dist/linkedin-message-deletion.js +5 -0
  92. package/node_modules/@oxygen/shared/dist/linkedin-post-keywords.d.ts +44 -0
  93. package/node_modules/@oxygen/shared/dist/linkedin-post-keywords.js +116 -0
  94. package/node_modules/@oxygen/shared/dist/linkedin-sequences.d.ts +96 -0
  95. package/node_modules/@oxygen/shared/dist/linkedin-sequences.js +123 -0
  96. package/node_modules/@oxygen/shared/dist/llm-durable-capture.d.ts +24 -0
  97. package/node_modules/@oxygen/shared/dist/llm-durable-capture.js +89 -0
  98. package/node_modules/@oxygen/shared/dist/llm-prompts.d.ts +75 -0
  99. package/node_modules/@oxygen/shared/dist/llm-prompts.js +161 -0
  100. package/node_modules/@oxygen/shared/dist/log-sink-selector.d.ts +39 -0
  101. package/node_modules/@oxygen/shared/dist/log-sink-selector.js +56 -0
  102. package/node_modules/@oxygen/shared/dist/log.d.ts +1 -0
  103. package/node_modules/@oxygen/shared/dist/log.js +6 -1
  104. package/node_modules/@oxygen/shared/dist/mailbox-egress-ownership.d.ts +90 -0
  105. package/node_modules/@oxygen/shared/dist/mailbox-egress-ownership.js +130 -0
  106. package/node_modules/@oxygen/shared/dist/object-storage.d.ts +17 -0
  107. package/node_modules/@oxygen/shared/dist/object-storage.js +21 -0
  108. package/node_modules/@oxygen/shared/dist/operational-telemetry.d.ts +24 -0
  109. package/node_modules/@oxygen/shared/dist/operational-telemetry.js +73 -0
  110. package/node_modules/@oxygen/shared/dist/otlp-log-sink.d.ts +79 -0
  111. package/node_modules/@oxygen/shared/dist/otlp-log-sink.js +366 -0
  112. package/node_modules/@oxygen/shared/dist/plan-capabilities.js +1 -0
  113. package/node_modules/@oxygen/shared/dist/plan-limits.d.ts +23 -22
  114. package/node_modules/@oxygen/shared/dist/plan-limits.js +45 -18
  115. package/node_modules/@oxygen/shared/dist/pricing-sheet.d.ts +48 -41
  116. package/node_modules/@oxygen/shared/dist/pricing-sheet.js +36 -25
  117. package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.d.ts +22 -22
  118. package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.js +40 -34
  119. package/node_modules/@oxygen/shared/dist/product-analytics-environment.js +9 -0
  120. package/node_modules/@oxygen/shared/dist/product-analytics-events.d.ts +36 -2
  121. package/node_modules/@oxygen/shared/dist/product-analytics-events.js +36 -1
  122. package/node_modules/@oxygen/shared/dist/rate-window.d.ts +5 -0
  123. package/node_modules/@oxygen/shared/dist/rate-window.js +8 -0
  124. package/node_modules/@oxygen/shared/dist/redaction.js +4 -1
  125. package/node_modules/@oxygen/shared/dist/research-output-contract.js +1 -3
  126. package/node_modules/@oxygen/shared/dist/scraper-lane-credential.d.ts +18 -0
  127. package/node_modules/@oxygen/shared/dist/scraper-lane-credential.js +23 -0
  128. package/node_modules/@oxygen/shared/dist/search-vocab.js +4 -5
  129. package/node_modules/@oxygen/shared/dist/select-options.js +6 -1
  130. package/node_modules/@oxygen/shared/dist/sequence-failures.js +1 -5
  131. package/node_modules/@oxygen/shared/dist/sequences.d.ts +23 -0
  132. package/node_modules/@oxygen/shared/dist/sequences.js +115 -5
  133. package/node_modules/@oxygen/shared/dist/signup-lead-payload.d.ts +80 -0
  134. package/node_modules/@oxygen/shared/dist/signup-lead-payload.js +198 -0
  135. package/node_modules/@oxygen/shared/dist/social-capabilities.d.ts +6 -0
  136. package/node_modules/@oxygen/shared/dist/social-capabilities.js +25 -16
  137. package/node_modules/@oxygen/shared/dist/social-post-metrics-core.d.ts +32 -0
  138. package/node_modules/@oxygen/shared/dist/social-post-metrics-core.js +32 -0
  139. package/node_modules/@oxygen/shared/dist/social-post-metrics-linkedin.d.ts +31 -0
  140. package/node_modules/@oxygen/shared/dist/social-post-metrics-linkedin.js +103 -0
  141. package/node_modules/@oxygen/shared/dist/social-post-metrics-series.d.ts +96 -0
  142. package/node_modules/@oxygen/shared/dist/social-post-metrics-series.js +213 -0
  143. package/node_modules/@oxygen/shared/dist/social-post-metrics-x.d.ts +13 -0
  144. package/node_modules/@oxygen/shared/dist/social-post-metrics-x.js +78 -0
  145. package/node_modules/@oxygen/shared/dist/social-post-metrics.d.ts +36 -0
  146. package/node_modules/@oxygen/shared/dist/social-post-metrics.js +51 -0
  147. package/node_modules/@oxygen/shared/dist/spend-safety.d.ts +22 -10
  148. package/node_modules/@oxygen/shared/dist/spend-safety.js +15 -21
  149. package/node_modules/@oxygen/shared/dist/sql-rows.d.ts +1 -0
  150. package/node_modules/@oxygen/shared/dist/sql-rows.js +3 -0
  151. package/node_modules/@oxygen/shared/dist/stripe-price-catalog.d.ts +36 -0
  152. package/node_modules/@oxygen/shared/dist/stripe-price-catalog.js +184 -0
  153. package/node_modules/@oxygen/shared/dist/stripe-subscription-kind.d.ts +41 -0
  154. package/node_modules/@oxygen/shared/dist/stripe-subscription-kind.js +44 -0
  155. package/node_modules/@oxygen/shared/dist/table-limits.d.ts +3 -0
  156. package/node_modules/@oxygen/shared/dist/table-limits.js +3 -0
  157. package/node_modules/@oxygen/shared/dist/telemetry-export-observer.d.ts +94 -0
  158. package/node_modules/@oxygen/shared/dist/telemetry-export-observer.js +298 -0
  159. package/node_modules/@oxygen/shared/dist/telemetry.d.ts +11 -0
  160. package/node_modules/@oxygen/shared/dist/telemetry.js +28 -2
  161. package/node_modules/@oxygen/shared/dist/type-guards.d.ts +22 -0
  162. package/node_modules/@oxygen/shared/dist/type-guards.js +35 -0
  163. package/node_modules/@oxygen/shared/dist/ugc.d.ts +22 -11
  164. package/node_modules/@oxygen/shared/dist/ugc.js +10 -0
  165. package/node_modules/@oxygen/shared/dist/value-readers.d.ts +21 -0
  166. package/node_modules/@oxygen/shared/dist/value-readers.js +59 -0
  167. package/node_modules/@oxygen/shared/dist/version.generated.d.ts +1 -0
  168. package/node_modules/@oxygen/shared/dist/version.generated.js +2 -0
  169. package/node_modules/@oxygen/shared/dist/version.js +8 -1
  170. package/node_modules/@oxygen/shared/dist/workspace-event-catalog.js +0 -23
  171. package/node_modules/@oxygen/shared/dist/workspace-file-storage.d.ts +29 -0
  172. package/node_modules/@oxygen/shared/dist/workspace-file-storage.js +31 -0
  173. package/node_modules/@oxygen/shared/package.json +59 -0
  174. package/node_modules/@oxygen/workflows/dist/graph/expression.js +2 -5
  175. package/node_modules/@oxygen/workflows/dist/graph/params.js +1 -1
  176. package/package.json +3 -2
@@ -1,34 +1,62 @@
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";
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_100_CREDITS as SNAPSHOT_TOPUP_USD_CENTS_PER_100_CREDITS, } from "./pricing-snapshot.generated.js";
2
2
  import { freeTierEntitlementEnabled } from "./plan-capabilities.js";
3
+ import { asRecordOrNull } from "./type-guards.js";
3
4
  export const WEEKLY_USAGE_WINDOW_DAYS = 7;
4
5
  export const BILLING_CURRENCIES = ["usd"];
5
6
  export const DEFAULT_BILLING_CURRENCY = "usd";
7
+ /**
8
+ * The SIX purchasable rungs of the one "Oxygen" product (founder 2026-09-19).
9
+ *
10
+ * These are plan KEYS — what Stripe sells and what `subscriptions.tier` stores —
11
+ * not tiers. They all share the single `oxygen` entitlement tier, which is the
12
+ * coarse rung plan-limits.ts and spend-safety.ts enforce against.
13
+ *
14
+ * Why keys and not six tiers: six tiers would force six new columns into every
15
+ * `Record<PlanTier, …>` ladder (plan limits, trigger ceilings, BYOK caps), i.e.
16
+ * ~40 invented numbers on an axis nobody ratified. Why not reuse starter/pro/team
17
+ * instead: then `plan.tier` would be ambiguous between a grandfathered $99
18
+ * Starter and a $49 Oxygen, and plan-change.ts — which refuses when
19
+ * `currentTier === targetTier` — would call a real $49 -> $99 upgrade
20
+ * `plan_unchanged`.
21
+ */
22
+ export const OXYGEN_PLAN_KEYS = [
23
+ "oxygen_49",
24
+ "oxygen_99",
25
+ "oxygen_199",
26
+ "oxygen_499",
27
+ "oxygen_999",
28
+ "oxygen_1999",
29
+ ];
6
30
  // Scale/Enterprise contact routes to the shared support inbox, not to a
7
31
  // founder's calendar. priority@oxygen-agent.com is the Plain Email channel
8
32
  // (PLAIN_SUPPORT_EMAIL_ADDRESS), so an enterprise enquiry lands in the support
9
33
  // queue as a Thread that either founder can pick up, instead of hard-coding one
10
34
  // person's booking page into every public pricing card.
11
35
  export const CONTACT_SALES_URL = "mailto:priority@oxygen-agent.com";
12
- // One Oxygen credit is worth $0.001 of managed work: $1 = 1,000 credits.
36
+ // One Oxygen credit is worth $0.01 of managed work: $1 = 100 credits.
13
37
  // Subscription plans convert at face value — every subscription dollar grants
14
- // exactly 1,000 credits — while on-demand top-up packs price the same credit
15
- // at $1.25 per 1,000, making subscription credits exactly 20% cheaper than
38
+ // exactly 100 credits — while on-demand top-up packs price the same credit
39
+ // at $1.25 per 100, making subscription credits exactly 20% cheaper than
16
40
  // credits bought on demand.
17
41
  //
42
+ // Re-denominated from $1 = 1,000 credits on 2026-09-19. That was a PURE unit
43
+ // change: every credit-denominated price divided by 10 in the same release, so
44
+ // the USD cost of every action is unchanged.
45
+ //
18
46
  // Sourced from the generated snapshot rather than declared here: this pair was
19
47
  // previously written down in seven production modules, and if any two ever
20
48
  // disagreed every markup-derived price would be silently wrong by the ratio.
21
49
  // Change them in packages/control-db/src/pricing/seed.ts.
22
50
  export const CREDITS_PER_USD = SNAPSHOT_CREDITS_PER_USD;
23
- export const TOPUP_USD_CENTS_PER_1K_CREDITS = SNAPSHOT_TOPUP_USD_CENTS_PER_1K_CREDITS;
51
+ export const TOPUP_USD_CENTS_PER_100_CREDITS = SNAPSHOT_TOPUP_USD_CENTS_PER_100_CREDITS;
24
52
  // On-demand purchases start at the original fixed packs' $10 floor and run up
25
- // to a $1,250 ceiling, in any 1,000-credit increment. The ceiling is a
53
+ // to a $1,250 ceiling, in any 100-credit increment. The ceiling is a
26
54
  // single-checkout guard rail, not a spend cap: a workspace that needs more
27
55
  // tops up again (purchased credits never expire).
28
- export const CREDIT_TOPUP_MIN_CREDITS = 8_000;
29
- export const CREDIT_TOPUP_MAX_CREDITS = 1_000_000;
30
- export const CREDIT_TOPUP_STEP_CREDITS = 1_000;
31
- export const CREDIT_TOPUP_DEFAULT_CREDITS = 20_000;
56
+ export const CREDIT_TOPUP_MIN_CREDITS = 800;
57
+ export const CREDIT_TOPUP_MAX_CREDITS = 100_000;
58
+ export const CREDIT_TOPUP_STEP_CREDITS = 100;
59
+ export const CREDIT_TOPUP_DEFAULT_CREDITS = 2_000;
32
60
  export function isValidCreditTopupCredits(credits) {
33
61
  return Number.isInteger(credits)
34
62
  && credits >= CREDIT_TOPUP_MIN_CREDITS
@@ -58,7 +86,7 @@ export function creditTopupAmountForShortfall(credits) {
58
86
  export function creditTopupUsdCents(credits) {
59
87
  if (!isValidCreditTopupCredits(credits))
60
88
  return null;
61
- return (credits / 1_000) * TOPUP_USD_CENTS_PER_1K_CREDITS;
89
+ return (credits / 100) * TOPUP_USD_CENTS_PER_100_CREDITS;
62
90
  }
63
91
  // Workflow/automation actions (steps, tool calls, row writes, retries) draw
64
92
  // from the same credit pool at a flat capacity-guard rate — the old separate
@@ -101,12 +129,12 @@ export const CREDIT_COMMITMENT_KINDS = [
101
129
  * Gmail and Microsoft inboxes included — and it STACKS on Oxygen-sold mailboxes,
102
130
  * which additionally pay their own mailbox/warmup/placement lines.
103
131
  */
104
- export const SENDING_MAILBOX_MONTHLY_CREDITS = 1_000;
132
+ export const SENDING_MAILBOX_MONTHLY_CREDITS = 100;
105
133
  /**
106
134
  * Oxygen-sold managed mailbox per month, in credits ($3.00). Flat, superseding
107
135
  * the dynamic vendor-COGS x 1.25 quote for the recurring mailbox line.
108
136
  */
109
- export const MANAGED_MAILBOX_MONTHLY_CREDITS = 3_000;
137
+ export const MANAGED_MAILBOX_MONTHLY_CREDITS = 300;
110
138
  /**
111
139
  * Grace window after a commitment goes past_due before the owning subsystem may
112
140
  * suspend the resource. Notify -> pause -> lapse; never auto-cancel. Aligned with
@@ -168,10 +196,10 @@ export function ratifiedCommitmentCredits(kind) {
168
196
  }
169
197
  }
170
198
  export const CREDIT_TOPUP_PACKS = [
171
- { id: "10", usdCents: 1_000, credits: 8_000 },
172
- { id: "25", usdCents: 2_500, credits: 20_000 },
173
- { id: "100", usdCents: 10_000, credits: 80_000 },
174
- { id: "250", usdCents: 25_000, credits: 200_000 },
199
+ { id: "10", usdCents: 1_000, credits: 800 },
200
+ { id: "25", usdCents: 2_500, credits: 2_000 },
201
+ { id: "100", usdCents: 10_000, credits: 8_000 },
202
+ { id: "250", usdCents: 25_000, credits: 20_000 },
175
203
  ];
176
204
  export function resolveCreditTopupPack(id) {
177
205
  if (!id)
@@ -184,7 +212,7 @@ export function resolveCreditTopupPack(id) {
184
212
  // to that paid plan on credit exhaustion or when the trial clock elapses,
185
213
  // whichever comes first. Starter remains the signup default.
186
214
  export const TRIAL_PERIOD_DAYS = 7;
187
- export const TRIAL_CREDIT_GRANT = 20_000;
215
+ export const TRIAL_CREDIT_GRANT = 2_000;
188
216
  /**
189
217
  * Free-tier entry (founder-ratified 2026-08-16; grant collapsed to a single
190
218
  * 10,000-credit signup grant 2026-08-26).
@@ -210,7 +238,7 @@ export const TRIAL_CREDIT_GRANT = 20_000;
210
238
  * mints it a second time. Top-ups stay purchasable on free, which is what keeps
211
239
  * the metered surface open-ended rather than capped at the grant.
212
240
  */
213
- export const FREE_SIGNUP_GRANT_CREDITS = 10_000;
241
+ export const FREE_SIGNUP_GRANT_CREDITS = 1_000;
214
242
  export const FREE_TIER_GRANTS_ENABLED_ENV_VAR = "OXYGEN_FREE_TIER_GRANTS_ENABLED";
215
243
  /**
216
244
  * Fail-closed rollout switch for the free-tier grants, matching the staged
@@ -236,8 +264,8 @@ export function freeTierGrantsEnabled(env = process.env) {
236
264
  // "start scraping engagers" step may spend at most this many credits on the
237
265
  // managed cookieless scraper (founder-approved budget; the rest of the kickoff
238
266
  // is free connected-account reads).
239
- export const WALKTHROUGH_COMPLETION_BONUS_CREDITS = 1_000;
240
- export const WALKTHROUGH_SCRAPE_MAX_CREDITS = 2_000;
267
+ export const WALKTHROUGH_COMPLETION_BONUS_CREDITS = 100;
268
+ export const WALKTHROUGH_SCRAPE_MAX_CREDITS = 200;
241
269
  /**
242
270
  * The five NUMERIC fields of a plan, read from the generated pricing snapshot.
243
271
  *
@@ -262,6 +290,20 @@ function planPricing(planKey) {
262
290
  byokEnabled: row.byokEnabled,
263
291
  };
264
292
  }
293
+ /**
294
+ * The monthly-credit feature bullet, DERIVED rather than written down.
295
+ *
296
+ * These strings used to be hand-typed literals ("99,000 credits every month").
297
+ * A re-denomination then had to find and retype every one of them, which is
298
+ * exactly how a price sheet ends up quoting a number the product does not
299
+ * charge. Never reintroduce a literal here.
300
+ */
301
+ function monthlyCreditsFeature(planKey) {
302
+ const credits = planPricing(planKey).monthlyCredits;
303
+ if (credits === null)
304
+ return "Custom monthly credits";
305
+ return `${credits.toLocaleString("en-US")} credits every month`;
306
+ }
265
307
  export const BASE_PRICING_PLANS = {
266
308
  // The ENTRY tier since 2026-08-16, superseding the 2026-07-22 retirement that
267
309
  // left this as the unentitled fallback for churned / never-subscribed orgs.
@@ -294,9 +336,33 @@ export const BASE_PRICING_PLANS = {
294
336
  "Credits for enrichment and AI, topped up any time",
295
337
  ],
296
338
  },
339
+ // The one purchasable product since 2026-09-19. Its numbers are the ENTRY
340
+ // rung ($49); the other five live in OXYGEN_PLAN_OVERRIDES and share this
341
+ // tier. Anything that needs a rung's real allowance must resolve the plan
342
+ // KEY (resolveBasePricingPlan("oxygen_499")), never this representative row.
343
+ oxygen: {
344
+ tier: "oxygen",
345
+ name: "Oxygen",
346
+ ...planPricing("oxygen_49"),
347
+ monthlyAutomationActions: null,
348
+ automationOverageCentsPerMillion: null,
349
+ automationOverageEnabledDefault: false,
350
+ description: "Your whole GTM stack. One plan — choose the credits you need.",
351
+ badge: "Your whole GTM stack",
352
+ highlighted: true,
353
+ ctaLabel: "Get started",
354
+ features: [
355
+ monthlyCreditsFeature("oxygen_49"),
356
+ "All integrations",
357
+ "Workflows",
358
+ "Bring your own keys",
359
+ "Standard support",
360
+ ],
361
+ },
297
362
  starter: {
298
363
  tier: "starter",
299
364
  name: "Starter",
365
+ isLegacy: true,
300
366
  ...planPricing("starter"),
301
367
  monthlyAutomationActions: null,
302
368
  automationOverageCentsPerMillion: null,
@@ -304,7 +370,7 @@ export const BASE_PRICING_PLANS = {
304
370
  description: "For founders running focused managed enrichment and AI columns.",
305
371
  ctaLabel: "Choose Starter",
306
372
  features: [
307
- "99,000 credits every month",
373
+ monthlyCreditsFeature("starter"),
308
374
  "All integrations",
309
375
  "Workflows",
310
376
  "Bring your own keys",
@@ -314,6 +380,7 @@ export const BASE_PRICING_PLANS = {
314
380
  pro: {
315
381
  tier: "pro",
316
382
  name: "Pro",
383
+ isLegacy: true,
317
384
  ...planPricing("pro"),
318
385
  monthlyAutomationActions: null,
319
386
  automationOverageCentsPerMillion: null,
@@ -323,7 +390,7 @@ export const BASE_PRICING_PLANS = {
323
390
  highlighted: true,
324
391
  ctaLabel: "Choose Pro",
325
392
  features: [
326
- "249,000 credits every month",
393
+ monthlyCreditsFeature("pro"),
327
394
  "All integrations",
328
395
  "Workflows",
329
396
  "Bring your own keys",
@@ -333,6 +400,7 @@ export const BASE_PRICING_PLANS = {
333
400
  team: {
334
401
  tier: "team",
335
402
  name: "Team",
403
+ isLegacy: true,
336
404
  ...planPricing("team"),
337
405
  monthlyAutomationActions: null,
338
406
  automationOverageCentsPerMillion: null,
@@ -340,7 +408,7 @@ export const BASE_PRICING_PLANS = {
340
408
  description: "For small GTM teams and agencies running shared workflows with their own AI keys.",
341
409
  ctaLabel: "Choose Team",
342
410
  features: [
343
- "749,000 credits every month",
411
+ monthlyCreditsFeature("team"),
344
412
  "All integrations",
345
413
  "Workflows",
346
414
  "Bring your own keys",
@@ -358,7 +426,7 @@ export const BASE_PRICING_PLANS = {
358
426
  ctaLabel: "Contact sales",
359
427
  contactHref: CONTACT_SALES_URL,
360
428
  features: [
361
- "10,000,000+ managed credits / month",
429
+ monthlyCreditsFeature("scale"),
362
430
  "Workflows",
363
431
  "Phone number enrichment on managed credits",
364
432
  "Custom provider routing and BYOK policies",
@@ -383,16 +451,22 @@ export const BASE_PRICING_PLANS = {
383
451
  ],
384
452
  },
385
453
  };
386
- export const PUBLIC_PLAN_ORDER = [
387
- "starter",
388
- "pro",
389
- "team",
390
- ];
454
+ /** One public card; the six rungs are a selector inside it. */
455
+ export const PUBLIC_PLAN_ORDER = ["oxygen"];
456
+ /**
457
+ * Tiers whose Stripe prices must still RESOLVE. Deliberately wider than what is
458
+ * purchasable: starter/pro/team are grandfathered, so a live subscription on one
459
+ * of their prices must keep mapping to a plan. Use PURCHASABLE_PLAN_KEYS for
460
+ * anything that offers a plan for sale.
461
+ */
391
462
  export const SELF_SERVE_PLAN_TIERS = [
463
+ "oxygen",
392
464
  "starter",
393
465
  "pro",
394
466
  "team",
395
467
  ];
468
+ /** What a customer can actually buy or switch to, cheapest first. */
469
+ export const PURCHASABLE_PLAN_KEYS = OXYGEN_PLAN_KEYS;
396
470
  export const CONTACT_SALES_PLAN_TIERS = [
397
471
  "scale",
398
472
  "enterprise",
@@ -465,6 +539,37 @@ export function resolveSubscriptionEntitlement(input) {
465
539
  }
466
540
  return { entitled: false, reason: "missing_subscription", graceEndsAt: null };
467
541
  }
542
+ /**
543
+ * The six purchasable rungs, keyed by plan key.
544
+ *
545
+ * Each is the representative `oxygen` plan with that rung's real price and
546
+ * allowance substituted in. `tier` stays "oxygen" for all six — the rung is the
547
+ * key, the entitlement is the tier.
548
+ */
549
+ const OXYGEN_PLAN_OVERRIDES = {
550
+ oxygen_49: oxygenRung("oxygen_49"),
551
+ oxygen_99: oxygenRung("oxygen_99"),
552
+ oxygen_199: oxygenRung("oxygen_199"),
553
+ oxygen_499: oxygenRung("oxygen_499"),
554
+ oxygen_999: oxygenRung("oxygen_999"),
555
+ oxygen_1999: oxygenRung("oxygen_1999"),
556
+ };
557
+ function oxygenRung(planKey) {
558
+ const pricing = planPricing(planKey);
559
+ const dollars = pricing.monthlyPriceCents === null
560
+ ? null
561
+ : Math.round(pricing.monthlyPriceCents / 100);
562
+ return {
563
+ ...BASE_PRICING_PLANS.oxygen,
564
+ ...pricing,
565
+ name: dollars === null ? "Oxygen" : `Oxygen $${dollars.toLocaleString("en-US")}`,
566
+ ctaLabel: dollars === null ? "Get started" : `Choose $${dollars.toLocaleString("en-US")}`,
567
+ features: [
568
+ monthlyCreditsFeature(planKey),
569
+ ...BASE_PRICING_PLANS.oxygen.features.slice(1),
570
+ ],
571
+ };
572
+ }
468
573
  const LEGACY_PRICING_PLAN_OVERRIDES = {
469
574
  launch_50: {
470
575
  ...BASE_PRICING_PLANS.starter,
@@ -515,6 +620,12 @@ const LEGACY_PRICING_PLAN_OVERRIDES = {
515
620
  export function resolveBasePricingPlan(tier) {
516
621
  if (!tier)
517
622
  return null;
623
+ // Plan KEYS before tiers: `subscriptions.tier` stores "oxygen_499", and the
624
+ // rung's own allowance — not the representative $49 row — is what the
625
+ // customer paid for.
626
+ const oxygen = OXYGEN_PLAN_OVERRIDES[tier];
627
+ if (oxygen)
628
+ return oxygen;
518
629
  if (tier in BASE_PRICING_PLANS) {
519
630
  return BASE_PRICING_PLANS[tier];
520
631
  }
@@ -523,13 +634,13 @@ export function resolveBasePricingPlan(tier) {
523
634
  }
524
635
  export function applyPricingPlanMetadataOverrides(// skipcq: JS-R1005
525
636
  plan, metadata) {
526
- const record = readRecord(metadata);
637
+ const record = asRecordOrNull(metadata);
527
638
  if (!record)
528
639
  return plan;
529
- const config = readRecord(record.billing_plan)
530
- ?? readRecord(record.custom_plan)
531
- ?? readRecord(record.custom_limits)
532
- ?? readRecord(record.plan_limits)
640
+ const config = asRecordOrNull(record.billing_plan)
641
+ ?? asRecordOrNull(record.custom_plan)
642
+ ?? asRecordOrNull(record.custom_limits)
643
+ ?? asRecordOrNull(record.plan_limits)
533
644
  ?? record;
534
645
  const name = readString(config, "name")
535
646
  ?? readString(config, "plan_name")
@@ -567,17 +678,33 @@ plan, metadata) {
567
678
  export function isSelfServePlanTier(value) {
568
679
  return SELF_SERVE_PLAN_TIERS.includes(value);
569
680
  }
681
+ // Keyed by PlanTier so a new tier is a compile error here until someone decides
682
+ // it. The hand-typed list this replaced predated the 2026-09-19 "oxygen" tier,
683
+ // so every customer on the one purchasable plan was refused managed phone
684
+ // enrichment that Free and the grandfathered plans still had.
685
+ const MANAGED_PHONE_PLAN_TIER_ALLOWED = {
686
+ free: true,
687
+ oxygen: true,
688
+ starter: true,
689
+ pro: true,
690
+ team: true,
691
+ scale: true,
692
+ enterprise: true,
693
+ };
694
+ export const MANAGED_PHONE_PLAN_TIERS = Object.keys(MANAGED_PHONE_PLAN_TIER_ALLOWED).filter((tier) => MANAGED_PHONE_PLAN_TIER_ALLOWED[tier]);
695
+ export function planAllowsManagedPhoneEnrichment(tier) {
696
+ return MANAGED_PHONE_PLAN_TIERS.includes(tier);
697
+ }
698
+ /** True for the six rungs a customer may buy or switch to. */
699
+ export function isPurchasablePlanKey(value) {
700
+ return PURCHASABLE_PLAN_KEYS.includes(value);
701
+ }
570
702
  export function isBillingCurrency(value) {
571
703
  return BILLING_CURRENCIES.includes(value);
572
704
  }
573
705
  export function normalizeBillingCurrency(value) {
574
706
  return value && isBillingCurrency(value) ? value : DEFAULT_BILLING_CURRENCY;
575
707
  }
576
- function readRecord(value) {
577
- if (!value || typeof value !== "object" || Array.isArray(value))
578
- return null;
579
- return value;
580
- }
581
708
  function readString(record, key) {
582
709
  const value = record[key];
583
710
  return typeof value === "string" && value.trim() ? value.trim() : null;
@@ -645,6 +772,34 @@ function formatUtcDay(date) {
645
772
  timeZone: "UTC",
646
773
  });
647
774
  }
775
+ /**
776
+ * Whether a subscription row has a real Stripe object behind it that Stripe's
777
+ * own APIs (retrieve, update, invoice preview, portal flows) can act on.
778
+ *
779
+ * Three kinds of row say no: a staff-invoiced manual contract (`manual_contract_`
780
+ * id and/or `off_stripe` in either spelling the codebase has written), a
781
+ * control-plane-only internal grant (`internal_dogfood_…`), and a hand-inserted
782
+ * synthetic row that DOES carry a `sub_` prefix but marks itself
783
+ * `metadata.synthetic: true`. That last shape is why the id prefix alone is not
784
+ * the test: the dev dogfood org's `sub_synthetic_dev_dogfood_…` row passed every
785
+ * `startsWith("sub_")` check and reached `stripe.subscriptions.retrieve`, where
786
+ * Stripe answered "No such subscription" and the customer read the generic
787
+ * "Unable to preview the plan change." Every self-serve Stripe mutation must
788
+ * refuse these rows before it calls Stripe, with a message that names the fix.
789
+ */
790
+ export function isStripeManagedSubscription(subscription) {
791
+ // A row with no usable id is not something Stripe manages; answer false
792
+ // rather than throw, because this runs inside read-only pages (the billing
793
+ // summary) where a throw becomes a 500 for the whole page.
794
+ if (typeof subscription.stripeSubscriptionId !== "string")
795
+ return false;
796
+ if (!subscription.stripeSubscriptionId.startsWith("sub_"))
797
+ return false;
798
+ const metadata = subscription.metadata ?? {};
799
+ return (metadata.off_stripe !== true
800
+ && metadata.off_stripe !== "true"
801
+ && metadata.synthetic !== true);
802
+ }
648
803
  /**
649
804
  * The window the credit-allowance bar measures: "this month's credits", resolved
650
805
  * to the boundary at which the plan's grant actually renews.
@@ -72,13 +72,18 @@ export const NATIVE_API_KEY_CONNECT_INTEGRATION_IDS = new Set([
72
72
  // Native api-key connect added in catalog.ts (2026-09-10): connect writes an
73
73
  // integration_secrets api_key row the native parallel.* runner reads. T-104.
74
74
  "parallel",
75
+ "pipedrive",
75
76
  "predictleads",
76
77
  "prospeo",
77
78
  "scalelist",
79
+ // scrapeli / up2data (2026-09-25): the company-followers and LinkedIn post lanes
80
+ // offer "Bring your own API key"; both connect forms live in catalog.ts.
81
+ "scrapeli",
78
82
  // scraper: dev v1.920.2 added the HarvestAPI workspace-key connect (native api-key form).
79
83
  "scraper",
80
84
  "serper",
81
85
  "signalbase",
86
+ "up2data",
82
87
  "zapmail",
83
88
  "zerobounce",
84
89
  ]);
@@ -1,3 +1,4 @@
1
+ import { type EnrichmentIntentTaxonomyEntry } from "./enrichment-intents.js";
1
2
  import { type RecipePrimitive } from "./recipes.js";
2
3
  export type CapabilityLayer = "Control" | "Knowledge" | "Data" | "Action" | "External";
3
4
  export type CapabilityPosture = "read_only" | "workspace_write" | "paid_read" | "external_write" | "mixed";
@@ -29,6 +30,22 @@ export type CapabilityRouteMatch = {
29
30
  * intent is that narrow; `recommendedCommands[0]` is the same command.
30
31
  */
31
32
  exactCommand?: string;
33
+ /**
34
+ * The opinionated enrichment catalogue row a data-type ask resolves to
35
+ * ("get the funding round" -> enrich_company:funding). Present only when
36
+ * the ask names a data type; the catalogue command is then the first
37
+ * recommendation, ahead of any raw provider tool.
38
+ */
39
+ catalogIntent?: {
40
+ id: string;
41
+ label: string;
42
+ status: EnrichmentIntentTaxonomyEntry["status"];
43
+ group: EnrichmentIntentTaxonomyEntry["group"];
44
+ /** The catalogue-first path: read the row, then add/preview; `exactCommand` names the row. */
45
+ tools: string[];
46
+ commands: string[];
47
+ exactCommand: string;
48
+ };
32
49
  };
33
50
  export type PrimitiveRouteMatch = CapabilityRouteMatch & {
34
51
  card: PrimitiveRouteCard;
@@ -40,5 +57,15 @@ export declare function getCapabilityRouteMatch(id: string): CapabilityRouteMatc
40
57
  export declare function inferCapabilityRoute(query: string): CapabilityRouteMatch | null;
41
58
  export declare function inferPrimitiveRoute(query: string): PrimitiveRouteMatch | null;
42
59
  export declare function serializeCapabilityRoute(route: CapabilityRouteMatch | null): Record<string, unknown> | null;
60
+ export declare const COMPANY_PAGE_FOLLOWERS_COMMAND = "oxygen tables followers check --company <linkedin company page URL> --records <n> --json";
43
61
  /** The exact `companies search plan` invocation for the url_recovery intent. */
44
62
  export declare const COMPANY_URL_RECOVERY_COMMAND = "oxygen companies search plan --prompt \"Find the websites for these companies: <company names>\" --source-intent url_recovery";
63
+ /** The free native preview for Google Maps businesses; import follows its reviewed quote. */
64
+ export declare const LOCAL_BUSINESS_SCRAPE_COMMAND = "oxygen companies search preview --source google_maps --filters-json '{\"q\":\"<category> in <city, region>\"}'";
65
+ /**
66
+ * The free list of classification columns. Production answers it with its one
67
+ * classification template (B2B or B2C); a deployment that runs the native
68
+ * Classify column lists that entry here too. Any other scheme is an AI prompt
69
+ * column through `columns add`, which the route recommends next.
70
+ */
71
+ export declare const ROW_CLASSIFICATION_COMMAND = "oxygen columns catalog --family classification";