@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
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { RepricingOptions } from "./repricing.js";
|
|
2
2
|
/**
|
|
3
3
|
* The daily cap a new mailbox gets when nobody names one. New mailboxes were
|
|
4
4
|
* created at 18 a day before the repricing, so this is an increase and is live.
|
|
5
5
|
*/
|
|
6
|
-
export declare const EMAIL_MAILBOX_DEFAULT_DAILY_CAP
|
|
6
|
+
export declare const EMAIL_MAILBOX_DEFAULT_DAILY_CAP: 25;
|
|
7
7
|
/**
|
|
8
8
|
* Who set a mailbox's stored cap. `default` = OXYGEN's default at creation,
|
|
9
9
|
* `user` = a person or agent set it. A row written before the column existed
|
|
@@ -12,15 +12,13 @@ export declare const EMAIL_MAILBOX_DEFAULT_DAILY_CAP = 25;
|
|
|
12
12
|
export declare const EMAIL_MAILBOX_DAILY_CAP_SOURCES: readonly ["default", "user"];
|
|
13
13
|
export type EmailMailboxDailyCapSource = (typeof EMAIL_MAILBOX_DAILY_CAP_SOURCES)[number];
|
|
14
14
|
export declare function readEmailMailboxDailyCapSource(value: unknown): EmailMailboxDailyCapSource | null;
|
|
15
|
-
/** The most a mailbox may be set to send a day,
|
|
16
|
-
export declare function emailMailboxDailyCapMaximum(
|
|
15
|
+
/** The most a mailbox may be set to send a day, independent of billing activation. */
|
|
16
|
+
export declare function emailMailboxDailyCapMaximum(_options?: RepricingOptions): number | null;
|
|
17
17
|
/**
|
|
18
18
|
* The configured cap in force for a stored one: every surface that reads a
|
|
19
19
|
* mailbox's cap, and the send claim, go through this.
|
|
20
20
|
*
|
|
21
|
-
*
|
|
22
|
-
* set by hand sends at the new default, and any cap above the maximum sends at
|
|
23
|
-
* the maximum. Before it, the stored cap is returned unchanged.
|
|
21
|
+
* Stored choices are retained up to the technical maximum.
|
|
24
22
|
*/
|
|
25
23
|
export declare function emailMailboxDailyCapInForce(input: {
|
|
26
24
|
storedCap: number;
|
|
@@ -2,18 +2,17 @@
|
|
|
2
2
|
// decision L3; specification slice S52). Limits guard, credits meter: none of
|
|
3
3
|
// these is sold, and none charges for sending.
|
|
4
4
|
//
|
|
5
|
-
// Email
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
// 2026-09 effective-date switch (`./repricing.ts`), so they are read through it.
|
|
5
|
+
// Email campaign ramp: 5 / 10 / 20 based on accepted-send days, then the
|
|
6
|
+
// configured cap (25 by default, maximum 50), independent of billing activation.
|
|
7
|
+
// The versioned migration changes old defaults once; later choices are retained.
|
|
9
8
|
//
|
|
10
9
|
// Voice per number: 100 dials a day by default, adjustable up to 300.
|
|
11
|
-
import {
|
|
10
|
+
import { EMAIL_DELIVERABILITY_DEFAULTS } from "./email-deliverability-policy.js";
|
|
12
11
|
/**
|
|
13
12
|
* The daily cap a new mailbox gets when nobody names one. New mailboxes were
|
|
14
13
|
* created at 18 a day before the repricing, so this is an increase and is live.
|
|
15
14
|
*/
|
|
16
|
-
export const EMAIL_MAILBOX_DEFAULT_DAILY_CAP =
|
|
15
|
+
export const EMAIL_MAILBOX_DEFAULT_DAILY_CAP = EMAIL_DELIVERABILITY_DEFAULTS.campaignDailyCap;
|
|
17
16
|
/**
|
|
18
17
|
* Who set a mailbox's stored cap. `default` = OXYGEN's default at creation,
|
|
19
18
|
* `user` = a person or agent set it. A row written before the column existed
|
|
@@ -23,25 +22,20 @@ export const EMAIL_MAILBOX_DAILY_CAP_SOURCES = ["default", "user"];
|
|
|
23
22
|
export function readEmailMailboxDailyCapSource(value) {
|
|
24
23
|
return value === "default" || value === "user" ? value : null;
|
|
25
24
|
}
|
|
26
|
-
/** The most a mailbox may be set to send a day,
|
|
27
|
-
export function emailMailboxDailyCapMaximum(
|
|
28
|
-
return
|
|
25
|
+
/** The most a mailbox may be set to send a day, independent of billing activation. */
|
|
26
|
+
export function emailMailboxDailyCapMaximum(_options = {}) {
|
|
27
|
+
return 50;
|
|
29
28
|
}
|
|
30
29
|
/**
|
|
31
30
|
* The configured cap in force for a stored one: every surface that reads a
|
|
32
31
|
* mailbox's cap, and the send claim, go through this.
|
|
33
32
|
*
|
|
34
|
-
*
|
|
35
|
-
* set by hand sends at the new default, and any cap above the maximum sends at
|
|
36
|
-
* the maximum. Before it, the stored cap is returned unchanged.
|
|
33
|
+
* Stored choices are retained up to the technical maximum.
|
|
37
34
|
*/
|
|
38
35
|
export function emailMailboxDailyCapInForce(input, options = {}) {
|
|
39
36
|
const stored = Number.isFinite(input.storedCap) ? Math.max(0, Math.floor(input.storedCap)) : 0;
|
|
40
|
-
const cap = input.source !== "user" && stored === EMAIL_MAILBOX_OLD_DEFAULT_DAILY_CAP.before
|
|
41
|
-
? repricedValue(EMAIL_MAILBOX_OLD_DEFAULT_DAILY_CAP, options)
|
|
42
|
-
: stored;
|
|
43
37
|
const maximum = emailMailboxDailyCapMaximum(options);
|
|
44
|
-
return maximum === null ?
|
|
38
|
+
return maximum === null ? stored : Math.min(stored, maximum);
|
|
45
39
|
}
|
|
46
40
|
/** A new voice number may dial this many times a day once it has warmed up. */
|
|
47
41
|
export const VOICE_NUMBER_DEFAULT_DAILY_CAP = 100;
|
|
@@ -25,24 +25,15 @@
|
|
|
25
25
|
* "skips the seat check", it is one that holds a legacy entitlement which
|
|
26
26
|
* SATISFIES the seat check. The gate always runs.
|
|
27
27
|
*/
|
|
28
|
-
import {
|
|
28
|
+
import { SENDING_SEAT_USD_CENTS, type SendingSeatKey } from "./pricing-snapshot.generated.js";
|
|
29
|
+
import type { CreditGateKind } from "./credit-gate.js";
|
|
29
30
|
export { SENDING_SEAT_USD_CENTS };
|
|
30
31
|
/**
|
|
31
|
-
* Email infrastructure
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* warm-up. A customer buying an Oxygen mailbox buys ONE thing at ONE price and
|
|
36
|
-
* can never end up holding a mailbox with no seat to send from. A customer
|
|
37
|
-
* bringing their own mailbox pays SENDING_SEAT_USD_CENTS.email_sender instead.
|
|
38
|
-
* The two are alternatives, never both for the same mailbox.
|
|
39
|
-
*
|
|
40
|
-
* The practical consequence for the connect gate: email capacity is
|
|
41
|
-
* (email_sender seats) + (managed inbox slots). Counting only seats would refuse
|
|
42
|
-
* a customer who bought inboxes, which is exactly the assemble-the-parts failure
|
|
43
|
-
* this bundle exists to prevent.
|
|
32
|
+
* Email infrastructure is no longer sold in dollars (repricing 2026-09, S32/S33):
|
|
33
|
+
* a managed inbox is priced in credits, 400 for a Google mailbox plus the 100-credit
|
|
34
|
+
* mailbox connection every sending mailbox holds, warm-up included. A customer
|
|
35
|
+
* bringing their own mailbox pays the connection alone.
|
|
44
36
|
*/
|
|
45
|
-
export { MANAGED_INBOX_USD_CENTS, SENDING_DOMAIN_USD_CENTS };
|
|
46
37
|
export type { SendingSeatKey };
|
|
47
38
|
export declare const SENDING_SEAT_KEYS: readonly ["linkedin", "whatsapp", "phone_number", "email_sender"];
|
|
48
39
|
export type SendingSeatDefinition = {
|
|
@@ -64,6 +55,18 @@ export type SendingSeatDefinition = {
|
|
|
64
55
|
export declare const SENDING_SEAT_CATALOG: {
|
|
65
56
|
readonly [K in SendingSeatKey]: SendingSeatDefinition;
|
|
66
57
|
};
|
|
58
|
+
/**
|
|
59
|
+
* The credit reservation each sending-seat channel becomes once seats are
|
|
60
|
+
* retired (repricing 2026-09, decisions 2.7 and 2.9). A seat still held keeps
|
|
61
|
+
* covering accounts of its kind until the seat subscription ends at renewal, and
|
|
62
|
+
* only then does each account start reserving credits, so an account is never
|
|
63
|
+
* paid for twice.
|
|
64
|
+
*/
|
|
65
|
+
export declare const SEAT_RESERVATION_KIND: {
|
|
66
|
+
readonly [K in SendingSeatKey]: CreditGateKind;
|
|
67
|
+
};
|
|
68
|
+
/** The seat channel whose seats cover a reservation kind, or null for a kind no seat ever covered. */
|
|
69
|
+
export declare function seatKeyForReservationKind(kind: string): SendingSeatKey | null;
|
|
67
70
|
export declare const SENDING_SEAT_LEGACY_CHARGE_KEYS: readonly string[];
|
|
68
71
|
export declare function isSendingSeatKey(value: unknown): value is SendingSeatKey;
|
|
69
72
|
export declare function sendingSeatMonthlyPriceCents(key: SendingSeatKey): number;
|
|
@@ -25,24 +25,8 @@
|
|
|
25
25
|
* "skips the seat check", it is one that holds a legacy entitlement which
|
|
26
26
|
* SATISFIES the seat check. The gate always runs.
|
|
27
27
|
*/
|
|
28
|
-
import {
|
|
28
|
+
import { SENDING_SEAT_USD_CENTS, } from "./pricing-snapshot.generated.js";
|
|
29
29
|
export { SENDING_SEAT_USD_CENTS };
|
|
30
|
-
/**
|
|
31
|
-
* Email infrastructure, kept next to the seats because the relationship between
|
|
32
|
-
* them is the whole design.
|
|
33
|
-
*
|
|
34
|
-
* MANAGED_INBOX_USD_CENTS is ALL-IN: it already contains the sending seat and
|
|
35
|
-
* warm-up. A customer buying an Oxygen mailbox buys ONE thing at ONE price and
|
|
36
|
-
* can never end up holding a mailbox with no seat to send from. A customer
|
|
37
|
-
* bringing their own mailbox pays SENDING_SEAT_USD_CENTS.email_sender instead.
|
|
38
|
-
* The two are alternatives, never both for the same mailbox.
|
|
39
|
-
*
|
|
40
|
-
* The practical consequence for the connect gate: email capacity is
|
|
41
|
-
* (email_sender seats) + (managed inbox slots). Counting only seats would refuse
|
|
42
|
-
* a customer who bought inboxes, which is exactly the assemble-the-parts failure
|
|
43
|
-
* this bundle exists to prevent.
|
|
44
|
-
*/
|
|
45
|
-
export { MANAGED_INBOX_USD_CENTS, SENDING_DOMAIN_USD_CENTS };
|
|
46
30
|
export const SENDING_SEAT_KEYS = [
|
|
47
31
|
"linkedin",
|
|
48
32
|
"whatsapp",
|
|
@@ -88,6 +72,27 @@ export const SENDING_SEAT_CATALOG = {
|
|
|
88
72
|
stripePriceEnvVar: "STRIPE_PRICE_SEAT_EMAIL_USD",
|
|
89
73
|
},
|
|
90
74
|
};
|
|
75
|
+
/**
|
|
76
|
+
* The credit reservation each sending-seat channel becomes once seats are
|
|
77
|
+
* retired (repricing 2026-09, decisions 2.7 and 2.9). A seat still held keeps
|
|
78
|
+
* covering accounts of its kind until the seat subscription ends at renewal, and
|
|
79
|
+
* only then does each account start reserving credits, so an account is never
|
|
80
|
+
* paid for twice.
|
|
81
|
+
*/
|
|
82
|
+
export const SEAT_RESERVATION_KIND = {
|
|
83
|
+
linkedin: "linkedin_account",
|
|
84
|
+
whatsapp: "whatsapp_account",
|
|
85
|
+
phone_number: "phone_number",
|
|
86
|
+
email_sender: "sending_mailbox",
|
|
87
|
+
};
|
|
88
|
+
/** The seat channel whose seats cover a reservation kind, or null for a kind no seat ever covered. */
|
|
89
|
+
export function seatKeyForReservationKind(kind) {
|
|
90
|
+
for (const seatKey of SENDING_SEAT_KEYS) {
|
|
91
|
+
if (SEAT_RESERVATION_KIND[seatKey] === kind)
|
|
92
|
+
return seatKey;
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
91
96
|
export const SENDING_SEAT_LEGACY_CHARGE_KEYS = SENDING_SEAT_KEYS
|
|
92
97
|
.map((key) => SENDING_SEAT_CATALOG[key].legacyChargeKey)
|
|
93
98
|
.filter((chargeKey) => chargeKey !== null);
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { PlanTier } from "./billing.js";
|
|
2
|
+
import type { PlanBand } from "./plan-band.js";
|
|
2
3
|
import type { LimitsTier } from "./plan-limits.js";
|
|
4
|
+
import { type RepricingOptions } from "./repricing.js";
|
|
3
5
|
/**
|
|
4
6
|
* Spend-safety defaults: plan-scaled ceilings applied when an autonomous
|
|
5
7
|
* spend path was armed WITHOUT an explicit cap. They restore the ADR-0011
|
|
@@ -19,18 +21,29 @@ export declare const AUTONOMOUS_WORKFLOW_TRIGGER_TYPES: ReadonlySet<string>;
|
|
|
19
21
|
/**
|
|
20
22
|
* Per-run managed-credit ceiling for a LIVE workflow run fired by an autonomous
|
|
21
23
|
* trigger when neither run metadata nor the manifest declares `max_credits`.
|
|
24
|
+
*
|
|
25
|
+
* These are the ceilings each rung read BEFORE the 2026-09 repricing. The value
|
|
26
|
+
* in force is resolved per plan by `resolveDefaultTriggerRunCreditCeilingForPlan`
|
|
27
|
+
* (`plan-band.ts`): 10% of the plan's monthly credits (decision L5.1), where a
|
|
28
|
+
* larger default applies at once and a smaller one waits for the
|
|
29
|
+
* effective-date switch with this table as its old value. Read this table
|
|
30
|
+
* directly only to know the pre-repricing value.
|
|
22
31
|
*/
|
|
23
32
|
export declare const DEFAULT_TRIGGER_RUN_CREDIT_CEILING: Record<SpendSafetyPlanTier, number | null>;
|
|
24
33
|
/**
|
|
25
34
|
* Per-delivery credit ceiling stamped onto a standing/webhook table auto-run
|
|
26
|
-
* batch when the armed configuration carries no explicit `max_credits`.
|
|
35
|
+
* batch when the armed configuration carries no explicit `max_credits`. The
|
|
36
|
+
* pre-repricing values, like the trigger table above; resolve the value in force
|
|
37
|
+
* with `resolveDefaultAutoRunBatchCreditCeilingForPlan`.
|
|
27
38
|
*/
|
|
28
39
|
export declare const DEFAULT_AUTO_RUN_BATCH_CREDIT_CEILING: Record<SpendSafetyPlanTier, number | null>;
|
|
29
40
|
/**
|
|
30
41
|
* Row ceiling for a BYOK AI-column run whose caller gave no explicit row bound
|
|
31
42
|
* (no limit, no row_ids, selection "all"). BYOK bills the customer's own
|
|
32
|
-
* provider account, so
|
|
33
|
-
*
|
|
43
|
+
* provider account, so OXYGEN's credit ceiling bounds only its own platform fee
|
|
44
|
+
* (0.05 credits a call from the 2026-09 repricing instant, decision 6.3) and
|
|
45
|
+
* never the provider's bill — row count stays the enforceable unit for that.
|
|
46
|
+
* An explicit limit (up to the 500k platform row cap) wins.
|
|
34
47
|
*/
|
|
35
48
|
export declare const DEFAULT_BYOK_COLUMN_RUN_MAX_ROWS: Record<SpendSafetyPlanTier, number | null>;
|
|
36
49
|
/**
|
|
@@ -89,6 +102,12 @@ export declare function resolveByokProviderDailyCapEnforcementMode(configured?:
|
|
|
89
102
|
*
|
|
90
103
|
* This applies ONLY to the implicit default. An org that sets its own org-daily
|
|
91
104
|
* policy has chosen a real spend ceiling, and that ceiling counts everything.
|
|
105
|
+
*
|
|
106
|
+
* The monthly reservation renewals of the 2026-09 repricing (connected sending
|
|
107
|
+
* mailboxes, deliverability units, connected accounts and phone numbers, dedicated
|
|
108
|
+
* IPs) are the same shape and are excluded for the same reason (PROPOSED P-59).
|
|
109
|
+
* It matters most on the free plan, whose guard is a fixed 1,000 / 5,000 credits
|
|
110
|
+
* a day: one LinkedIn renewal would otherwise be most of a free day's warning.
|
|
92
111
|
*/
|
|
93
112
|
export declare const IMPLICIT_ORG_DAILY_GUARD_EXCLUDED_CATEGORIES: readonly string[];
|
|
94
113
|
export declare const DEFAULT_ORG_DAILY_SPEND_WARN_MULTIPLE = 5;
|
|
@@ -101,16 +120,46 @@ export type OrgDailySpendGuard = {
|
|
|
101
120
|
/**
|
|
102
121
|
* Resolve the implicit guard's thresholds from a plan's monthly grant. `null`
|
|
103
122
|
* means the guard is suppressed because the plan has no finite positive grant to
|
|
104
|
-
* scale from (
|
|
105
|
-
*
|
|
123
|
+
* scale from (enterprise/custom at null) — the single place that decision is
|
|
124
|
+
* made, so enforcement and the read surfaces cannot drift apart.
|
|
125
|
+
*
|
|
126
|
+
* The free plan has no monthly grant, so it gets a fixed guard instead (decision
|
|
127
|
+
* L5.2: 1,000 warn / 5,000 block a UTC day). It is a new bound on free, so it
|
|
128
|
+
* takes effect at the 2026-09 repricing instant; before then free has none.
|
|
106
129
|
*/
|
|
107
|
-
export declare function resolveOrgDailySpendGuard(monthlyCredits: number | null | undefined
|
|
130
|
+
export declare function resolveOrgDailySpendGuard(monthlyCredits: number | null | undefined, options?: RepricingOptions & {
|
|
131
|
+
freePlan?: boolean;
|
|
132
|
+
}): OrgDailySpendGuard | null;
|
|
108
133
|
/** Credits carry 3 decimals everywhere (control-DB numeric(18,3)). */
|
|
109
134
|
export declare function roundCredits(value: number): number;
|
|
110
135
|
/** Credit estimates that present to 2 decimals (cent-grained customer copy). */
|
|
111
136
|
export declare function roundCreditsToCents(value: number): number;
|
|
112
|
-
|
|
113
|
-
|
|
137
|
+
/**
|
|
138
|
+
* Platform-owned runaway ceiling for one attended Workspace Copilot turn: 3,000
|
|
139
|
+
* customer credits, $6 of upstream inference at the ratified 5x markup, the same
|
|
140
|
+
* on every plan. It is not a user budget or an authorization grant. It lives
|
|
141
|
+
* here rather than in `@oxygen/copilot` so the limits report can state it next
|
|
142
|
+
* to the approval-free allowance it must stay above; `@oxygen/copilot/config`
|
|
143
|
+
* re-exports it.
|
|
144
|
+
*/
|
|
145
|
+
export declare const COPILOT_ATTENDED_INFERENCE_CREDIT_CEILING = 3000;
|
|
146
|
+
/** How much paid work the attended Copilot may start without an approval card. */
|
|
147
|
+
export type CopilotApprovalFreeAllowance = {
|
|
148
|
+
/** The most one call may declare (its server-enforced `max_credits`). */
|
|
149
|
+
perCallCredits: number;
|
|
150
|
+
/** The most all such calls in one session may declare together. */
|
|
151
|
+
perSessionCredits: number;
|
|
152
|
+
};
|
|
153
|
+
/**
|
|
154
|
+
* The Copilot's approval-free spend allowance by plan size (decision L5.3). Only
|
|
155
|
+
* the two ends are ratified: free at 50 / 300 credits ($0.50 / $3) and $1,999 at
|
|
156
|
+
* 500 / 2,500 ($5 / $25). The middle sizes are PROPOSED (P-55). Every value is
|
|
157
|
+
* at least the 50 / 300 every plan had before, so the table ships live. The
|
|
158
|
+
* largest session allowance stays below the attended turn backstop above.
|
|
159
|
+
*/
|
|
160
|
+
export declare const COPILOT_APPROVAL_FREE_ALLOWANCE_BY_PLAN_BAND: Readonly<Record<PlanBand, CopilotApprovalFreeAllowance>>;
|
|
161
|
+
/** A plan size's approval-free Copilot allowance. */
|
|
162
|
+
export declare function copilotApprovalFreeAllowanceForBand(band: PlanBand): CopilotApprovalFreeAllowance;
|
|
114
163
|
export declare function resolveDefaultByokColumnRunMaxRows(tier: SpendSafetyPlanTier): number | null;
|
|
115
164
|
export declare function resolveDefaultByokProviderDailyCallCap(tier: SpendSafetyPlanTier): number | null;
|
|
116
165
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FREE_ORG_DAILY_GUARD_BLOCK_CREDITS, FREE_ORG_DAILY_GUARD_WARN_CREDITS, repricedValue, } from "./repricing.js";
|
|
1
2
|
/** Workflow run trigger types that fire without a human in the loop. */
|
|
2
3
|
export const AUTONOMOUS_WORKFLOW_TRIGGER_TYPES = new Set([
|
|
3
4
|
"cron",
|
|
@@ -7,6 +8,13 @@ export const AUTONOMOUS_WORKFLOW_TRIGGER_TYPES = new Set([
|
|
|
7
8
|
/**
|
|
8
9
|
* Per-run managed-credit ceiling for a LIVE workflow run fired by an autonomous
|
|
9
10
|
* trigger when neither run metadata nor the manifest declares `max_credits`.
|
|
11
|
+
*
|
|
12
|
+
* These are the ceilings each rung read BEFORE the 2026-09 repricing. The value
|
|
13
|
+
* in force is resolved per plan by `resolveDefaultTriggerRunCreditCeilingForPlan`
|
|
14
|
+
* (`plan-band.ts`): 10% of the plan's monthly credits (decision L5.1), where a
|
|
15
|
+
* larger default applies at once and a smaller one waits for the
|
|
16
|
+
* effective-date switch with this table as its old value. Read this table
|
|
17
|
+
* directly only to know the pre-repricing value.
|
|
10
18
|
*/
|
|
11
19
|
export const DEFAULT_TRIGGER_RUN_CREDIT_CEILING = {
|
|
12
20
|
free: 50,
|
|
@@ -18,7 +26,9 @@ export const DEFAULT_TRIGGER_RUN_CREDIT_CEILING = {
|
|
|
18
26
|
};
|
|
19
27
|
/**
|
|
20
28
|
* Per-delivery credit ceiling stamped onto a standing/webhook table auto-run
|
|
21
|
-
* batch when the armed configuration carries no explicit `max_credits`.
|
|
29
|
+
* batch when the armed configuration carries no explicit `max_credits`. The
|
|
30
|
+
* pre-repricing values, like the trigger table above; resolve the value in force
|
|
31
|
+
* with `resolveDefaultAutoRunBatchCreditCeilingForPlan`.
|
|
22
32
|
*/
|
|
23
33
|
export const DEFAULT_AUTO_RUN_BATCH_CREDIT_CEILING = {
|
|
24
34
|
free: 50,
|
|
@@ -31,8 +41,10 @@ export const DEFAULT_AUTO_RUN_BATCH_CREDIT_CEILING = {
|
|
|
31
41
|
/**
|
|
32
42
|
* Row ceiling for a BYOK AI-column run whose caller gave no explicit row bound
|
|
33
43
|
* (no limit, no row_ids, selection "all"). BYOK bills the customer's own
|
|
34
|
-
* provider account, so
|
|
35
|
-
*
|
|
44
|
+
* provider account, so OXYGEN's credit ceiling bounds only its own platform fee
|
|
45
|
+
* (0.05 credits a call from the 2026-09 repricing instant, decision 6.3) and
|
|
46
|
+
* never the provider's bill — row count stays the enforceable unit for that.
|
|
47
|
+
* An explicit limit (up to the 500k platform row cap) wins.
|
|
36
48
|
*/
|
|
37
49
|
export const DEFAULT_BYOK_COLUMN_RUN_MAX_ROWS = {
|
|
38
50
|
free: null,
|
|
@@ -106,20 +118,38 @@ export function resolveByokProviderDailyCapEnforcementMode(configured = process.
|
|
|
106
118
|
*
|
|
107
119
|
* This applies ONLY to the implicit default. An org that sets its own org-daily
|
|
108
120
|
* policy has chosen a real spend ceiling, and that ceiling counts everything.
|
|
121
|
+
*
|
|
122
|
+
* The monthly reservation renewals of the 2026-09 repricing (connected sending
|
|
123
|
+
* mailboxes, deliverability units, connected accounts and phone numbers, dedicated
|
|
124
|
+
* IPs) are the same shape and are excluded for the same reason (PROPOSED P-59).
|
|
125
|
+
* It matters most on the free plan, whose guard is a fixed 1,000 / 5,000 credits
|
|
126
|
+
* a day: one LinkedIn renewal would otherwise be most of a free day's warning.
|
|
109
127
|
*/
|
|
110
128
|
export const IMPLICIT_ORG_DAILY_GUARD_EXCLUDED_CATEGORIES = [
|
|
111
129
|
"managed_warmup",
|
|
112
130
|
"managed_inbox",
|
|
131
|
+
"managed_mailbox",
|
|
132
|
+
"managed_deliverability",
|
|
133
|
+
"managed_seat",
|
|
113
134
|
];
|
|
114
135
|
export const DEFAULT_ORG_DAILY_SPEND_WARN_MULTIPLE = 5;
|
|
115
136
|
export const DEFAULT_ORG_DAILY_SPEND_BLOCK_MULTIPLE = 20;
|
|
116
137
|
/**
|
|
117
138
|
* Resolve the implicit guard's thresholds from a plan's monthly grant. `null`
|
|
118
139
|
* means the guard is suppressed because the plan has no finite positive grant to
|
|
119
|
-
* scale from (
|
|
120
|
-
*
|
|
140
|
+
* scale from (enterprise/custom at null) — the single place that decision is
|
|
141
|
+
* made, so enforcement and the read surfaces cannot drift apart.
|
|
142
|
+
*
|
|
143
|
+
* The free plan has no monthly grant, so it gets a fixed guard instead (decision
|
|
144
|
+
* L5.2: 1,000 warn / 5,000 block a UTC day). It is a new bound on free, so it
|
|
145
|
+
* takes effect at the 2026-09 repricing instant; before then free has none.
|
|
121
146
|
*/
|
|
122
|
-
export function resolveOrgDailySpendGuard(monthlyCredits) {
|
|
147
|
+
export function resolveOrgDailySpendGuard(monthlyCredits, options = {}) {
|
|
148
|
+
if (options.freePlan) {
|
|
149
|
+
const warnCredits = repricedValue(FREE_ORG_DAILY_GUARD_WARN_CREDITS, options);
|
|
150
|
+
const blockCredits = repricedValue(FREE_ORG_DAILY_GUARD_BLOCK_CREDITS, options);
|
|
151
|
+
return warnCredits === null || blockCredits === null ? null : { warnCredits, blockCredits };
|
|
152
|
+
}
|
|
123
153
|
if (typeof monthlyCredits !== "number" || !Number.isFinite(monthlyCredits) || monthlyCredits <= 0) {
|
|
124
154
|
return null;
|
|
125
155
|
}
|
|
@@ -136,11 +166,34 @@ export function roundCredits(value) {
|
|
|
136
166
|
export function roundCreditsToCents(value) {
|
|
137
167
|
return Math.round((value + Number.EPSILON) * 100) / 100;
|
|
138
168
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
169
|
+
/**
|
|
170
|
+
* Platform-owned runaway ceiling for one attended Workspace Copilot turn: 3,000
|
|
171
|
+
* customer credits, $6 of upstream inference at the ratified 5x markup, the same
|
|
172
|
+
* on every plan. It is not a user budget or an authorization grant. It lives
|
|
173
|
+
* here rather than in `@oxygen/copilot` so the limits report can state it next
|
|
174
|
+
* to the approval-free allowance it must stay above; `@oxygen/copilot/config`
|
|
175
|
+
* re-exports it.
|
|
176
|
+
*/
|
|
177
|
+
export const COPILOT_ATTENDED_INFERENCE_CREDIT_CEILING = 3_000;
|
|
178
|
+
/**
|
|
179
|
+
* The Copilot's approval-free spend allowance by plan size (decision L5.3). Only
|
|
180
|
+
* the two ends are ratified: free at 50 / 300 credits ($0.50 / $3) and $1,999 at
|
|
181
|
+
* 500 / 2,500 ($5 / $25). The middle sizes are PROPOSED (P-55). Every value is
|
|
182
|
+
* at least the 50 / 300 every plan had before, so the table ships live. The
|
|
183
|
+
* largest session allowance stays below the attended turn backstop above.
|
|
184
|
+
*/
|
|
185
|
+
export const COPILOT_APPROVAL_FREE_ALLOWANCE_BY_PLAN_BAND = Object.freeze({
|
|
186
|
+
free: Object.freeze({ perCallCredits: 50, perSessionCredits: 300 }),
|
|
187
|
+
"49": Object.freeze({ perCallCredits: 100, perSessionCredits: 500 }),
|
|
188
|
+
"99": Object.freeze({ perCallCredits: 100, perSessionCredits: 500 }),
|
|
189
|
+
"199": Object.freeze({ perCallCredits: 200, perSessionCredits: 1_000 }),
|
|
190
|
+
"499": Object.freeze({ perCallCredits: 200, perSessionCredits: 1_000 }),
|
|
191
|
+
"999": Object.freeze({ perCallCredits: 300, perSessionCredits: 1_500 }),
|
|
192
|
+
"1999": Object.freeze({ perCallCredits: 500, perSessionCredits: 2_500 }),
|
|
193
|
+
});
|
|
194
|
+
/** A plan size's approval-free Copilot allowance. */
|
|
195
|
+
export function copilotApprovalFreeAllowanceForBand(band) {
|
|
196
|
+
return COPILOT_APPROVAL_FREE_ALLOWANCE_BY_PLAN_BAND[band];
|
|
144
197
|
}
|
|
145
198
|
export function resolveDefaultByokColumnRunMaxRows(tier) {
|
|
146
199
|
return DEFAULT_BYOK_COLUMN_RUN_MAX_ROWS[tier];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CONTACT_SALES_URL, PURCHASABLE_PLAN_KEYS, SELF_SERVE_PLAN_TIERS, normalizeBillingCurrency, type BillingCurrency, type OxygenPlanKey, type PricingPlanDefinition, type SelfServePlanTier } from "./billing.js";
|
|
1
|
+
import { CONTACT_SALES_URL, PURCHASABLE_PLAN_KEYS, SELF_SERVE_PLAN_TIERS, normalizeBillingCurrency, type BillingCurrency, type OxygenPlanKey, type PlanBillingInterval, type PricingPlanDefinition, type SelfServePlanTier } from "./billing.js";
|
|
2
2
|
export { CONTACT_SALES_URL, PURCHASABLE_PLAN_KEYS, SELF_SERVE_PLAN_TIERS, normalizeBillingCurrency, };
|
|
3
3
|
export type { BillingCurrency, OxygenPlanKey, SelfServePlanTier };
|
|
4
4
|
export type PricingPlan = PricingPlanDefinition & {
|
|
@@ -17,8 +17,7 @@ export declare const PRICING_PLANS: {
|
|
|
17
17
|
/** The six purchasable rungs with their Stripe price ids, cheapest first. */
|
|
18
18
|
export declare const PURCHASABLE_PLANS: Record<OxygenPlanKey, PricingPlan>;
|
|
19
19
|
export declare function getPlanPriceId(plan: PricingPlan, currency: BillingCurrency): string;
|
|
20
|
-
|
|
21
|
-
export type PlanBillingInterval = "month" | "year";
|
|
20
|
+
export type { PlanBillingInterval };
|
|
22
21
|
/**
|
|
23
22
|
* The configured Price for one rung and interval, or "" when that Price is not
|
|
24
23
|
* configured (every yearly Price, until S14 creates them).
|
|
@@ -34,6 +33,14 @@ export declare function getPurchasablePlanPriceId(planKey: OxygenPlanKey, interv
|
|
|
34
33
|
* whether a paid period is sliced.
|
|
35
34
|
*/
|
|
36
35
|
export declare function planBillingIntervalForPriceId(priceId: string | null | undefined): PlanBillingInterval | null;
|
|
36
|
+
/**
|
|
37
|
+
* Whether yearly billing can be sold: every purchasable rung has a configured
|
|
38
|
+
* yearly Price. False until S14 creates them, which is what keeps every annual
|
|
39
|
+
* control (the plan picker's Yearly switch, `--interval year`, the pricing
|
|
40
|
+
* page's yearly line) dark. All six or nothing, so no surface ever offers a
|
|
41
|
+
* yearly rung whose checkout would be refused.
|
|
42
|
+
*/
|
|
43
|
+
export declare function annualPlanBillingAvailable(currency?: BillingCurrency): boolean;
|
|
37
44
|
/** Every configured yearly plan Price id (empty until S14). */
|
|
38
45
|
export declare function configuredAnnualPlanPriceIds(): readonly string[];
|
|
39
46
|
/**
|
|
@@ -50,10 +57,11 @@ export declare function tierFromPriceId(priceId: string): string | null;
|
|
|
50
57
|
* family. Catalog audit/migration code consumes this instead of maintaining a
|
|
51
58
|
* second env map that can silently omit grandfathered subscriptions.
|
|
52
59
|
*
|
|
53
|
-
* The yearly Prices are
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* source set
|
|
60
|
+
* The configured yearly Prices are listed with the Oxygen family (slice S14):
|
|
61
|
+
* the migration planner compares the billing interval before it matches a
|
|
62
|
+
* rung by plan key or amount (S16, `stripeMigrationTargetFor`), so a yearly
|
|
63
|
+
* Price in the source set resolves to itself and can never become the target
|
|
64
|
+
* of a monthly subscription.
|
|
57
65
|
*/
|
|
58
66
|
export declare function recognizedStripePriceIdsBySelfServeTier(): Record<SelfServePlanTier, readonly string[]>;
|
|
59
67
|
export declare function formatMonthlyPrice(priceCents: number | null, currency?: BillingCurrency): string;
|
|
@@ -25,7 +25,8 @@ const STRIPE_PRICE_OXYGEN_IDS = {
|
|
|
25
25
|
* Product, since month and year are distinct intervals a Portal configuration
|
|
26
26
|
* accepts under one Product.
|
|
27
27
|
*
|
|
28
|
-
* DARK until the yearly
|
|
28
|
+
* DARK until the yearly Price ids are configured (`provision` creates the Prices
|
|
29
|
+
* and prints these names, slice S14): every env var is unset, an
|
|
29
30
|
* unset id is "", and tierFromPriceId refuses "" before any map is consulted, so
|
|
30
31
|
* no subscription resolves to a yearly interval and nothing is ever sliced.
|
|
31
32
|
*/
|
|
@@ -129,6 +130,16 @@ export function planBillingIntervalForPriceId(priceId) {
|
|
|
129
130
|
}
|
|
130
131
|
return tierFromPriceId(priceId) === null ? null : "month";
|
|
131
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* Whether yearly billing can be sold: every purchasable rung has a configured
|
|
135
|
+
* yearly Price. False until S14 creates them, which is what keeps every annual
|
|
136
|
+
* control (the plan picker's Yearly switch, `--interval year`, the pricing
|
|
137
|
+
* page's yearly line) dark. All six or nothing, so no surface ever offers a
|
|
138
|
+
* yearly rung whose checkout would be refused.
|
|
139
|
+
*/
|
|
140
|
+
export function annualPlanBillingAvailable(currency = DEFAULT_BILLING_CURRENCY) {
|
|
141
|
+
return PURCHASABLE_PLAN_KEYS.every((planKey) => getPurchasablePlanPriceId(planKey, "year", currency) !== "");
|
|
142
|
+
}
|
|
132
143
|
/** Every configured yearly plan Price id (empty until S14). */
|
|
133
144
|
export function configuredAnnualPlanPriceIds() {
|
|
134
145
|
return uniqueNonEmptyPriceIds(PURCHASABLE_PLAN_KEYS.flatMap((planKey) => Object.values(STRIPE_PRICE_OXYGEN_ANNUAL_IDS[planKey])));
|
|
@@ -175,15 +186,17 @@ export function tierFromPriceId(priceId) {
|
|
|
175
186
|
* family. Catalog audit/migration code consumes this instead of maintaining a
|
|
176
187
|
* second env map that can silently omit grandfathered subscriptions.
|
|
177
188
|
*
|
|
178
|
-
* The yearly Prices are
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
* source set
|
|
189
|
+
* The configured yearly Prices are listed with the Oxygen family (slice S14):
|
|
190
|
+
* the migration planner compares the billing interval before it matches a
|
|
191
|
+
* rung by plan key or amount (S16, `stripeMigrationTargetFor`), so a yearly
|
|
192
|
+
* Price in the source set resolves to itself and can never become the target
|
|
193
|
+
* of a monthly subscription.
|
|
182
194
|
*/
|
|
183
195
|
export function recognizedStripePriceIdsBySelfServeTier() {
|
|
184
196
|
const byTier = {
|
|
185
197
|
oxygen: [
|
|
186
198
|
...PURCHASABLE_PLAN_KEYS.flatMap((planKey) => Object.values(STRIPE_PRICE_OXYGEN_IDS[planKey])),
|
|
199
|
+
...configuredAnnualPlanPriceIds(),
|
|
187
200
|
...LEGACY_CURRENT_PLAN_PRICE_IDS.oxygen,
|
|
188
201
|
],
|
|
189
202
|
starter: [
|
|
@@ -25,18 +25,47 @@ export declare function workspaceDatabaseWarningBytesFor(limitBytes: number): nu
|
|
|
25
25
|
*/
|
|
26
26
|
export declare const PLAN_BAND_TABLE_CAPACITY_TARGETS: Readonly<Record<PlanBand, WorkspaceTableCapacityLimits>>;
|
|
27
27
|
/**
|
|
28
|
-
*
|
|
29
|
-
* rows per Table
|
|
30
|
-
*
|
|
28
|
+
* Rows per Table above today's 3,000,000 ship only after a capacity test at 5M
|
|
29
|
+
* and 10M rows per Table. The workspace totals of these bands (rows and
|
|
30
|
+
* database) are already in force (decision F.1, 2026-09-27, amending L1, which
|
|
31
|
+
* had gated both); shipping a band's rows per Table is removing it from this list.
|
|
31
32
|
*/
|
|
32
|
-
export declare const
|
|
33
|
+
export declare const PLAN_BANDS_AWAITING_TABLE_ROW_CAPACITY_TEST: readonly PlanBand[];
|
|
33
34
|
/**
|
|
34
35
|
* The storage limits in force for a plan band right now.
|
|
35
36
|
*
|
|
36
37
|
* - A limit that tightens (free, $49, $99) is a scheduled 2026-09 repricing value
|
|
37
38
|
* (`storage.<band>.*` in `REPRICING_2026_09_SCHEDULE`): today's envelope until
|
|
38
39
|
* the effective-date switch, the ratified ladder from it on.
|
|
39
|
-
* - A band awaiting the capacity test
|
|
40
|
+
* - A band awaiting the rows-per-Table capacity test has its ladder workspace
|
|
41
|
+
* totals and keeps today's rows per Table.
|
|
40
42
|
* - Every other band is already at its ladder value.
|
|
41
43
|
*/
|
|
42
44
|
export declare function resolveWorkspaceTableCapacity(band: PlanBand, options?: RepricingOptions): WorkspaceTableCapacityLimits;
|
|
45
|
+
/**
|
|
46
|
+
* Where a band's storage stands against its ratified ladder (decision F.2):
|
|
47
|
+
*
|
|
48
|
+
* - `in_force`: the band's values are in force; nothing is pending.
|
|
49
|
+
* - `scheduled`: the 2026-09 repricing moves them to `target` at `effectiveAt`.
|
|
50
|
+
* - `awaiting_capacity_test`: `target` applies once the rows-per-Table capacity
|
|
51
|
+
* test at that size passes; there is no date.
|
|
52
|
+
*
|
|
53
|
+
* A cut is reported only once an operator has set the repricing switch to a
|
|
54
|
+
* valid future instant. While the switch is unset or invalid a cutting band
|
|
55
|
+
* reads `in_force` and no target is revealed, the rule `describeRepricingSchedule`
|
|
56
|
+
* follows for every other scheduled value.
|
|
57
|
+
*/
|
|
58
|
+
export type WorkspaceTableCapacityStatus = {
|
|
59
|
+
status: "in_force";
|
|
60
|
+
effectiveAt: null;
|
|
61
|
+
target: null;
|
|
62
|
+
} | {
|
|
63
|
+
status: "scheduled";
|
|
64
|
+
effectiveAt: Date;
|
|
65
|
+
target: WorkspaceTableCapacityLimits;
|
|
66
|
+
} | {
|
|
67
|
+
status: "awaiting_capacity_test";
|
|
68
|
+
effectiveAt: null;
|
|
69
|
+
target: WorkspaceTableCapacityLimits;
|
|
70
|
+
};
|
|
71
|
+
export declare function describeWorkspaceTableCapacityStatus(band: PlanBand, options?: RepricingOptions): WorkspaceTableCapacityStatus;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { findRepricedValue, repricedValue } from "./repricing.js";
|
|
1
|
+
import { findRepricedValue, readRepricingSwitch, repricedValue, } from "./repricing.js";
|
|
2
2
|
const GIB = 1024 ** 3;
|
|
3
3
|
/**
|
|
4
4
|
* The envelope every workspace had before storage scaled with plan size, and
|
|
@@ -43,11 +43,12 @@ export const PLAN_BAND_TABLE_CAPACITY_TARGETS = Object.freeze({
|
|
|
43
43
|
"1999": ladderRow(10_000_000, 100_000_000, 120),
|
|
44
44
|
});
|
|
45
45
|
/**
|
|
46
|
-
*
|
|
47
|
-
* rows per Table
|
|
48
|
-
*
|
|
46
|
+
* Rows per Table above today's 3,000,000 ship only after a capacity test at 5M
|
|
47
|
+
* and 10M rows per Table. The workspace totals of these bands (rows and
|
|
48
|
+
* database) are already in force (decision F.1, 2026-09-27, amending L1, which
|
|
49
|
+
* had gated both); shipping a band's rows per Table is removing it from this list.
|
|
49
50
|
*/
|
|
50
|
-
export const
|
|
51
|
+
export const PLAN_BANDS_AWAITING_TABLE_ROW_CAPACITY_TEST = Object.freeze(["999", "1999"]);
|
|
51
52
|
const STORAGE_FIELDS = [
|
|
52
53
|
["tableRowLimit", "table_row_limit"],
|
|
53
54
|
["workspaceRowLimit", "workspace_row_limit"],
|
|
@@ -60,13 +61,15 @@ const STORAGE_FIELDS = [
|
|
|
60
61
|
* - A limit that tightens (free, $49, $99) is a scheduled 2026-09 repricing value
|
|
61
62
|
* (`storage.<band>.*` in `REPRICING_2026_09_SCHEDULE`): today's envelope until
|
|
62
63
|
* the effective-date switch, the ratified ladder from it on.
|
|
63
|
-
* - A band awaiting the capacity test
|
|
64
|
+
* - A band awaiting the rows-per-Table capacity test has its ladder workspace
|
|
65
|
+
* totals and keeps today's rows per Table.
|
|
64
66
|
* - Every other band is already at its ladder value.
|
|
65
67
|
*/
|
|
66
68
|
export function resolveWorkspaceTableCapacity(band, options = {}) {
|
|
67
|
-
if (PLAN_BANDS_AWAITING_CAPACITY_TEST.includes(band))
|
|
68
|
-
return WORKSPACE_TABLE_CAPACITY;
|
|
69
69
|
const target = PLAN_BAND_TABLE_CAPACITY_TARGETS[band];
|
|
70
|
+
if (PLAN_BANDS_AWAITING_TABLE_ROW_CAPACITY_TEST.includes(band)) {
|
|
71
|
+
return Object.freeze({ ...target, tableRowLimit: WORKSPACE_TABLE_CAPACITY.tableRowLimit });
|
|
72
|
+
}
|
|
70
73
|
const resolved = { ...target };
|
|
71
74
|
for (const [field, key] of STORAGE_FIELDS) {
|
|
72
75
|
const scheduled = findRepricedValue(`storage.${band}.${key}`);
|
|
@@ -76,3 +79,17 @@ export function resolveWorkspaceTableCapacity(band, options = {}) {
|
|
|
76
79
|
}
|
|
77
80
|
return Object.freeze(resolved);
|
|
78
81
|
}
|
|
82
|
+
export function describeWorkspaceTableCapacityStatus(band, options = {}) {
|
|
83
|
+
if (PLAN_BANDS_AWAITING_TABLE_ROW_CAPACITY_TEST.includes(band)) {
|
|
84
|
+
return { status: "awaiting_capacity_test", effectiveAt: null, target: PLAN_BAND_TABLE_CAPACITY_TARGETS[band] };
|
|
85
|
+
}
|
|
86
|
+
const state = readRepricingSwitch(options);
|
|
87
|
+
if (state.status === "scheduled") {
|
|
88
|
+
const current = resolveWorkspaceTableCapacity(band, options);
|
|
89
|
+
const target = resolveWorkspaceTableCapacity(band, { ...options, now: state.effectiveAt });
|
|
90
|
+
if (STORAGE_FIELDS.some(([field]) => current[field] !== target[field])) {
|
|
91
|
+
return { status: "scheduled", effectiveAt: state.effectiveAt, target };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return { status: "in_force", effectiveAt: null, target: null };
|
|
95
|
+
}
|