@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,7 +1,8 @@
|
|
|
1
|
-
import { resolveBasePricingPlan } from "./billing.js";
|
|
2
|
-
import { planLimitsForTier, limitsTierForPlanTier } from "./plan-limits.js";
|
|
3
|
-
import { DEFAULT_AUTO_RUN_BATCH_CREDIT_CEILING, DEFAULT_BYOK_COLUMN_RUN_MAX_ROWS, DEFAULT_BYOK_PROVIDER_DAILY_CALL_CAP, DEFAULT_TRIGGER_RUN_CREDIT_CEILING, resolveOrgDailySpendGuard, } from "./spend-safety.js";
|
|
4
|
-
import {
|
|
1
|
+
import { planVolumeBonusCredits, planVolumeBonusPercent, resolveBasePricingPlan } from "./billing.js";
|
|
2
|
+
import { planLimitsForTier, limitsTierForPlanTier, resolveCronMinIntervalMinutes, resolvePlanAgentCreditLimits, } from "./plan-limits.js";
|
|
3
|
+
import { COPILOT_ATTENDED_INFERENCE_CREDIT_CEILING, DEFAULT_AUTO_RUN_BATCH_CREDIT_CEILING, DEFAULT_BYOK_COLUMN_RUN_MAX_ROWS, DEFAULT_BYOK_PROVIDER_DAILY_CALL_CAP, DEFAULT_TRIGGER_RUN_CREDIT_CEILING, copilotApprovalFreeAllowanceForBand, resolveOrgDailySpendGuard, resolveSpendSafetyPlanTier, } from "./spend-safety.js";
|
|
4
|
+
import { isRepricingInForce, tenthOfMonthlyCreditsRoundedHalfUp, } from "./repricing.js";
|
|
5
|
+
import { describeWorkspaceTableCapacityStatus, resolveWorkspaceTableCapacity, } from "./table-capacity.js";
|
|
5
6
|
export const PLAN_BAND_ORDER = [
|
|
6
7
|
"free",
|
|
7
8
|
"49",
|
|
@@ -21,6 +22,24 @@ export const PLAN_BAND_LIMITS_TIER = Object.freeze({
|
|
|
21
22
|
"999": "team",
|
|
22
23
|
"1999": "scale",
|
|
23
24
|
});
|
|
25
|
+
/**
|
|
26
|
+
* L4.1 (repricing 2026-09, slice S55; decision record "Worker share"): a
|
|
27
|
+
* tenant's share of the legacy worker's tenant queue while other tenants wait.
|
|
28
|
+
* A weight-16 tenant gets sixteen quanta for every one a weight-1 tenant gets;
|
|
29
|
+
* with nobody else waiting every tenant is served at once, whatever its weight.
|
|
30
|
+
* It is a scheduling share, never a sold limit: capacity is sized so contention
|
|
31
|
+
* is rare (`.agents/skills/oxygen-worker-performance`, "Worker share target").
|
|
32
|
+
* An org whose band is not yet resolved schedules at the free weight.
|
|
33
|
+
*/
|
|
34
|
+
export const PLAN_BAND_WORKER_SHARE_WEIGHT = Object.freeze({
|
|
35
|
+
free: 1,
|
|
36
|
+
"49": 2,
|
|
37
|
+
"99": 2,
|
|
38
|
+
"199": 4,
|
|
39
|
+
"499": 4,
|
|
40
|
+
"999": 8,
|
|
41
|
+
"1999": 16,
|
|
42
|
+
});
|
|
24
43
|
/**
|
|
25
44
|
* PROPOSED (P-57, repricing spec § 4): a grandfathered or legacy plan takes the
|
|
26
45
|
* band of the limits rung it enforces at today, so no grandfathered customer
|
|
@@ -82,6 +101,115 @@ export function planBandMonthlyPriceCents(band) {
|
|
|
82
101
|
export function planBandLabel(band) {
|
|
83
102
|
return band === "free" ? "Free" : `Oxygen $${Number(band).toLocaleString("en-US")}`;
|
|
84
103
|
}
|
|
104
|
+
// ---- Default credit ceiling of one unattended delivery (decision L5.1) ------
|
|
105
|
+
/**
|
|
106
|
+
* PROPOSED (P-54): the free plan keeps a 50-credit default. It has no monthly
|
|
107
|
+
* grant to take 10% of (10% of its one-time 1,000 would be 100).
|
|
108
|
+
*/
|
|
109
|
+
export const FREE_PLAN_DEFAULT_DELIVERY_CREDIT_CEILING = 50;
|
|
110
|
+
/**
|
|
111
|
+
* The grandfathered plans that take 10% of their OWN monthly credits (PROPOSED
|
|
112
|
+
* P-62: Starter 990, Pro 2,490, Team 7,490) rather than their band's. Other
|
|
113
|
+
* legacy plan keys take the band of the rung they enforce at (P-57).
|
|
114
|
+
*/
|
|
115
|
+
const OWN_CREDITS_DELIVERY_CEILING_PLAN_KEYS = new Set(["starter", "pro", "team"]);
|
|
116
|
+
/**
|
|
117
|
+
* A plan size's target default delivery ceiling: 10% of its monthly credits,
|
|
118
|
+
* rounded half up (decision L5.1; the volume bonus counts, the partner bonus
|
|
119
|
+
* does not, P-54). This is the target, not always the value in force: see
|
|
120
|
+
* `resolveDefaultTriggerRunCreditCeilingForPlan`.
|
|
121
|
+
*/
|
|
122
|
+
export function planBandDeliveryCreditCeilingTarget(band) {
|
|
123
|
+
if (band === "free")
|
|
124
|
+
return FREE_PLAN_DEFAULT_DELIVERY_CREDIT_CEILING;
|
|
125
|
+
const monthlyCredits = resolveBasePricingPlan(`oxygen_${band}`)?.monthlyCredits;
|
|
126
|
+
if (typeof monthlyCredits !== "number" || !(monthlyCredits > 0)) {
|
|
127
|
+
throw new Error(`plan-band: no monthly credits for the ${band} plan`);
|
|
128
|
+
}
|
|
129
|
+
return tenthOfMonthlyCreditsRoundedHalfUp(monthlyCredits);
|
|
130
|
+
}
|
|
131
|
+
function planBandForSpendResolution(resolution) {
|
|
132
|
+
return resolution.planTier
|
|
133
|
+
? planBandForPlanTier(resolution.planTier)
|
|
134
|
+
: LEGACY_PLAN_BAND_BY_LIMITS_TIER[resolution.limitsTier];
|
|
135
|
+
}
|
|
136
|
+
function resolveDeliveryCreditCeiling(resolution, preRepricingTable, options) {
|
|
137
|
+
const before = preRepricingTable[resolveSpendSafetyPlanTier(resolution.planTier, resolution.limitsTier)];
|
|
138
|
+
// Enterprise runs on custom limits and has no default, before or after.
|
|
139
|
+
if (before === null || before === undefined)
|
|
140
|
+
return null;
|
|
141
|
+
const ownPlan = resolution.planTier && OWN_CREDITS_DELIVERY_CEILING_PLAN_KEYS.has(resolution.planTier)
|
|
142
|
+
? resolveBasePricingPlan(resolution.planTier)
|
|
143
|
+
: null;
|
|
144
|
+
const target = ownPlan && typeof ownPlan.monthlyCredits === "number" && ownPlan.monthlyCredits > 0
|
|
145
|
+
? tenthOfMonthlyCreditsRoundedHalfUp(ownPlan.monthlyCredits)
|
|
146
|
+
: planBandDeliveryCreditCeilingTarget(planBandForSpendResolution(resolution));
|
|
147
|
+
// A larger default ships on release; a smaller one is a tightening and waits
|
|
148
|
+
// for the effective-date switch (`spend.<band>.default_delivery_credit_ceiling`).
|
|
149
|
+
if (target >= before)
|
|
150
|
+
return target;
|
|
151
|
+
return isRepricingInForce(options) ? target : before;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* The default credit ceiling of one LIVE workflow run fired by an unattended
|
|
155
|
+
* trigger (cron, webhook, event) that declares no `max_credits`, for a plan.
|
|
156
|
+
* `null` means no default applies (enterprise); an explicit cap always wins and
|
|
157
|
+
* is never clamped by this.
|
|
158
|
+
*/
|
|
159
|
+
export function resolveDefaultTriggerRunCreditCeilingForPlan(resolution, options = {}) {
|
|
160
|
+
return resolveDeliveryCreditCeiling(resolution, DEFAULT_TRIGGER_RUN_CREDIT_CEILING, options);
|
|
161
|
+
}
|
|
162
|
+
/** The same default for one standing or webhook table auto-run batch. */
|
|
163
|
+
export function resolveDefaultAutoRunBatchCreditCeilingForPlan(resolution, options = {}) {
|
|
164
|
+
return resolveDeliveryCreditCeiling(resolution, DEFAULT_AUTO_RUN_BATCH_CREDIT_CEILING, options);
|
|
165
|
+
}
|
|
166
|
+
/** The spend resolution a plan size stands for (its Oxygen plan key). */
|
|
167
|
+
function bandSpendResolution(band) {
|
|
168
|
+
return { planTier: band === "free" ? null : `oxygen_${band}`, limitsTier: PLAN_BAND_LIMITS_TIER[band] };
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Public webhook ingress (repricing 2026-09, decision L4.4, ratified
|
|
172
|
+
* 2026-09-26): deliveries a minute that one webhook target (a Table endpoint,
|
|
173
|
+
* a workflow or Agent trigger, the workspace's RB2B receiver) accepts. It used
|
|
174
|
+
* to be a flat 600 per target, with 60 per sender IP on most targets. Every
|
|
175
|
+
* value rises, so this is live on release rather than held behind the
|
|
176
|
+
* repricing switch.
|
|
177
|
+
*/
|
|
178
|
+
export const PUBLIC_WEBHOOK_INGRESS_PER_TARGET_PER_MINUTE = Object.freeze({
|
|
179
|
+
free: 600,
|
|
180
|
+
"49": 1_500,
|
|
181
|
+
"99": 1_500,
|
|
182
|
+
"199": 3_000,
|
|
183
|
+
"499": 3_000,
|
|
184
|
+
"999": 6_000,
|
|
185
|
+
"1999": 12_000,
|
|
186
|
+
});
|
|
187
|
+
/**
|
|
188
|
+
* PROPOSED (P-50, repricing spec § 4.4): the decision raises the per-IP limit
|
|
189
|
+
* "to match", read as equal to the per-target limit (a share of 1). One sender
|
|
190
|
+
* (a form tool, a CRM) usually posts from one address, so any lower per-IP
|
|
191
|
+
* limit would cap a real integration below its plan's rate.
|
|
192
|
+
*/
|
|
193
|
+
export const PUBLIC_WEBHOOK_INGRESS_PER_IP_SHARE_OF_TARGET = 1;
|
|
194
|
+
export const PUBLIC_WEBHOOK_INGRESS_WINDOW_SECONDS = 60;
|
|
195
|
+
/** The webhook ingress limits a band enforces. */
|
|
196
|
+
export function resolvePublicWebhookIngressLimits(band) {
|
|
197
|
+
const perTarget = PUBLIC_WEBHOOK_INGRESS_PER_TARGET_PER_MINUTE[band];
|
|
198
|
+
return {
|
|
199
|
+
perTargetPerMinute: perTarget,
|
|
200
|
+
perSenderIpPerMinute: Math.round(perTarget * PUBLIC_WEBHOOK_INGRESS_PER_IP_SHARE_OF_TARGET),
|
|
201
|
+
windowSeconds: PUBLIC_WEBHOOK_INGRESS_WINDOW_SECONDS,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
/** The webhook ingress limits of one band, as the `/api/cli/limits` contract reports them. */
|
|
205
|
+
export function describeWebhookIngress(band) {
|
|
206
|
+
const ingress = resolvePublicWebhookIngressLimits(band);
|
|
207
|
+
return {
|
|
208
|
+
ingress_requests_per_target_per_minute: ingress.perTargetPerMinute,
|
|
209
|
+
ingress_requests_per_sender_ip_per_minute: ingress.perSenderIpPerMinute,
|
|
210
|
+
window_seconds: ingress.windowSeconds,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
85
213
|
/** The API rates a limits rung enforces, read from `PLAN_LIMITS`. */
|
|
86
214
|
export function describeApiRateLimits(tier) {
|
|
87
215
|
const cli = planLimitsForTier(tier).cli;
|
|
@@ -100,6 +228,24 @@ export function describeApiRateLimits(tier) {
|
|
|
100
228
|
rows_written_per_org: { rows: cli.tenantWrite.orgUnits.limit, window_seconds: cli.tenantWrite.orgUnits.windowSeconds },
|
|
101
229
|
};
|
|
102
230
|
}
|
|
231
|
+
function storageTarget(limits) {
|
|
232
|
+
return {
|
|
233
|
+
table_row_limit: limits.tableRowLimit,
|
|
234
|
+
workspace_row_limit: limits.workspaceRowLimit,
|
|
235
|
+
workspace_database_warning_bytes: limits.workspaceDatabaseWarningBytes,
|
|
236
|
+
workspace_database_limit_bytes: limits.workspaceDatabaseLimitBytes,
|
|
237
|
+
import_max_rows_per_file: limits.tableRowLimit,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
/** A band's storage status as `/api/cli/limits` reports it (`plan_bands[].storage` and `storage_capacity`). */
|
|
241
|
+
export function describePlanBandStorageStatus(band, options = {}) {
|
|
242
|
+
const status = describeWorkspaceTableCapacityStatus(band, options);
|
|
243
|
+
return {
|
|
244
|
+
status: status.status,
|
|
245
|
+
effective_at: status.effectiveAt?.toISOString() ?? null,
|
|
246
|
+
target: status.target ? storageTarget(status.target) : null,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
103
249
|
export function describePlanBandLimits(band, options = {}) {
|
|
104
250
|
const limitsTier = PLAN_BAND_LIMITS_TIER[band];
|
|
105
251
|
const storage = resolveWorkspaceTableCapacity(band, options);
|
|
@@ -108,12 +254,20 @@ export function describePlanBandLimits(band, options = {}) {
|
|
|
108
254
|
const monthlyCredits = band === "free"
|
|
109
255
|
? null
|
|
110
256
|
: resolveBasePricingPlan(`oxygen_${band}`)?.monthlyCredits ?? null;
|
|
111
|
-
const dailyGuard = resolveOrgDailySpendGuard(monthlyCredits);
|
|
257
|
+
const dailyGuard = resolveOrgDailySpendGuard(monthlyCredits, { ...options, freePlan: band === "free" });
|
|
258
|
+
const agentLimits = resolvePlanAgentCreditLimits(limitsTier, options);
|
|
259
|
+
const copilotAllowance = copilotApprovalFreeAllowanceForBand(band);
|
|
112
260
|
return {
|
|
113
261
|
band,
|
|
114
262
|
label: planBandLabel(band),
|
|
115
263
|
monthly_price_usd: monthlyPriceCents === null ? null : monthlyPriceCents / 100,
|
|
116
264
|
monthly_credits: monthlyCredits,
|
|
265
|
+
volume_bonus_credits: band === "free" ? null : planVolumeBonusCredits(`oxygen_${band}`),
|
|
266
|
+
volume_bonus_percent: band === "free" ? null : planVolumeBonusPercent(`oxygen_${band}`),
|
|
267
|
+
usd_per_100_credits: monthlyPriceCents === null || !monthlyCredits
|
|
268
|
+
? null
|
|
269
|
+
// Four decimals: $1,999 buys 229,885 credits, $0.8696 per 100.
|
|
270
|
+
: Math.round((monthlyPriceCents / monthlyCredits) * 10_000) / 10_000,
|
|
117
271
|
limits_tier: limitsTier,
|
|
118
272
|
api: describeApiRateLimits(limitsTier),
|
|
119
273
|
storage: {
|
|
@@ -123,22 +277,33 @@ export function describePlanBandLimits(band, options = {}) {
|
|
|
123
277
|
workspace_database_limit_bytes: storage.workspaceDatabaseLimitBytes,
|
|
124
278
|
import_max_rows_per_file: storage.tableRowLimit,
|
|
125
279
|
import_max_file_bytes: limits.import.maxFileBytes,
|
|
280
|
+
...describePlanBandStorageStatus(band, options),
|
|
126
281
|
},
|
|
127
282
|
spend: {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
283
|
+
trigger_run_credit_ceiling: resolveDefaultTriggerRunCreditCeilingForPlan(bandSpendResolution(band), options),
|
|
284
|
+
auto_run_batch_credit_ceiling: resolveDefaultAutoRunBatchCreditCeilingForPlan(bandSpendResolution(band), options),
|
|
285
|
+
agent_run_max_total_credits: agentLimits.maxTotalCredits,
|
|
286
|
+
agent_run_max_inference_credits: agentLimits.maxInferenceCredits,
|
|
287
|
+
copilot_approval_free_per_call_credits: copilotAllowance.perCallCredits,
|
|
288
|
+
copilot_approval_free_per_session_credits: copilotAllowance.perSessionCredits,
|
|
289
|
+
copilot_attended_turn_backstop_credits: COPILOT_ATTENDED_INFERENCE_CREDIT_CEILING,
|
|
133
290
|
org_daily_guard_warn_credits: dailyGuard?.warnCredits ?? null,
|
|
134
291
|
org_daily_guard_block_credits: dailyGuard?.blockCredits ?? null,
|
|
135
292
|
byok_column_run_max_rows: DEFAULT_BYOK_COLUMN_RUN_MAX_ROWS[limitsTier],
|
|
136
293
|
byok_provider_daily_calls: DEFAULT_BYOK_PROVIDER_DAILY_CALL_CAP[limitsTier],
|
|
137
294
|
},
|
|
295
|
+
tables: {
|
|
296
|
+
max_rows_per_write_request: limits.cli.maxWriteRowsPerRequest,
|
|
297
|
+
max_columns_per_run: limits.tableActions.maxActionsPerRun,
|
|
298
|
+
},
|
|
138
299
|
signals: {
|
|
139
300
|
max_events: limits.signals.maxEvents,
|
|
140
301
|
max_window_days: limits.signals.maxWindowDays,
|
|
141
302
|
},
|
|
303
|
+
webhooks: describeWebhookIngress(band),
|
|
304
|
+
workflows: {
|
|
305
|
+
cron_min_interval_minutes: resolveCronMinIntervalMinutes(limitsTier, options),
|
|
306
|
+
},
|
|
142
307
|
};
|
|
143
308
|
}
|
|
144
309
|
/** Every band's limits in force today, smallest first. */
|
|
@@ -25,14 +25,22 @@
|
|
|
25
25
|
* live in docs/free-tier-capability-matrix.md.
|
|
26
26
|
*
|
|
27
27
|
* WHY GATES ARE CHECKED BEFORE BALANCE. A connected sender is also a FIXED
|
|
28
|
-
* monthly credit commitment (
|
|
29
|
-
*
|
|
30
|
-
* workspace could buy
|
|
28
|
+
* monthly credit commitment (100 per mailbox, 1,000 per LinkedIn account,
|
|
29
|
+
* 1,000 per WhatsApp number), and top-ups stay purchasable on free — so a free
|
|
30
|
+
* workspace could buy 1,000 credits for $12.50 and fund a LinkedIn seat that
|
|
31
31
|
* Starter prices at $99. Enforcing by balance would therefore price sending at
|
|
32
32
|
* an eighth of Starter. The capability gate must run BEFORE any balance or
|
|
33
33
|
* commitment check so the customer is told to upgrade, not to top up.
|
|
34
|
+
*
|
|
35
|
+
* THE 2026-09 REPRICING ENDS THAT ARGUMENT (decision 2.7). Connected accounts
|
|
36
|
+
* are repriced as credit reservations at their real price, so once they are
|
|
37
|
+
* billed that way (the credit gate, `OXYGEN_CREDIT_GATE_ENABLED`) credits are the
|
|
38
|
+
* only gate and the sending walls retire: `CREDIT_GATE_RETIRED_CAPABILITIES`.
|
|
39
|
+
* Until the gate is in force everything above still holds.
|
|
34
40
|
*/
|
|
35
|
-
import type { PlanTier } from "./billing.js";
|
|
41
|
+
import type { PlanTier, PricingPlanDefinition } from "./billing.js";
|
|
42
|
+
import { CREDIT_GATE_RETIRED_CAPABILITIES, isCapabilityRetiredByCreditGate } from "./credit-gate.js";
|
|
43
|
+
import { type RepricingOptions } from "./repricing.js";
|
|
36
44
|
export declare const PLAN_GATED_CAPABILITIES: readonly ["sender_connect", "sequence_dispatch", "publishing_connect", "publishing_deliver", "managed_email_infrastructure", "byok_provider_keys"];
|
|
37
45
|
export type PlanGatedCapability = (typeof PLAN_GATED_CAPABILITIES)[number];
|
|
38
46
|
/**
|
|
@@ -56,14 +64,76 @@ export type PlanGatedCapabilityCopy = {
|
|
|
56
64
|
export declare const PLAN_GATED_CAPABILITY_COPY: Record<PlanGatedCapability, PlanGatedCapabilityCopy>;
|
|
57
65
|
export declare function isPlanGatedCapability(value: unknown): value is PlanGatedCapability;
|
|
58
66
|
/**
|
|
59
|
-
* The
|
|
67
|
+
* The walls credits replace once the credit gate is in force (repricing
|
|
68
|
+
* 2026-09, decision 2.7, slice S22).
|
|
69
|
+
*
|
|
70
|
+
* "Credits are the only gate": a free workspace may connect accounts, send,
|
|
71
|
+
* publish and buy managed infrastructure if it holds the credits. Each of these
|
|
72
|
+
* is paid for by a monthly credit reservation or a credit purchase, so the plan
|
|
73
|
+
* wall has nothing left to protect — but only once those reservations are
|
|
74
|
+
* actually BILLED, which is exactly what the credit gate asserts at boot
|
|
75
|
+
* (`assertCreditGateArmed`). Until then every one of them stays walled exactly
|
|
76
|
+
* as before.
|
|
77
|
+
*
|
|
78
|
+
* Two capabilities stay walled under the gate:
|
|
79
|
+
* - `byok_provider_keys`: bringing your own keys needs a plan that includes it
|
|
80
|
+
* ($99 or above from the repricing's effective date).
|
|
81
|
+
* - `publishing_connect`: the only publishing connects that still reach it are
|
|
82
|
+
* Instagram, TikTok, YouTube and Facebook, which have no reservation price yet
|
|
83
|
+
* (P-18). Connecting them without one would make them free and unmetered, so
|
|
84
|
+
* they stay on the plan until that price is ratified.
|
|
85
|
+
*/
|
|
86
|
+
export { CREDIT_GATE_RETIRED_CAPABILITIES, isCapabilityRetiredByCreditGate };
|
|
87
|
+
/**
|
|
88
|
+
* The whole boundary for one plan, in one expression.
|
|
60
89
|
*
|
|
61
|
-
* Note this asks about the
|
|
90
|
+
* Note this asks about the PLAN, not about entitlement. A churned workspace and
|
|
62
91
|
* a brand-new one both resolve to `free` and both get the same answer here —
|
|
63
92
|
* one entitlement, one capability set, differing only in grants and upgrade
|
|
64
93
|
* copy (founder, 2026-08-17).
|
|
94
|
+
*
|
|
95
|
+
* BYOK reads the plan's own `byokEnabled`, never the tier: every Oxygen size
|
|
96
|
+
* shares the tier `oxygen`, and only $99 and above include BYOK once the
|
|
97
|
+
* repricing is in force. Everything else is the founder's one line, free versus
|
|
98
|
+
* paid. `creditGateInForce` retires the walls credits pay for (above).
|
|
99
|
+
*/
|
|
100
|
+
export declare function planAllowsCapability(plan: Pick<PricingPlanDefinition, "tier" | "byokEnabled">, capability: PlanGatedCapability, options?: {
|
|
101
|
+
creditGateInForce?: boolean;
|
|
102
|
+
}): boolean;
|
|
103
|
+
/**
|
|
104
|
+
* The smallest purchasable plan that includes BYOK right now: `oxygen_49` until
|
|
105
|
+
* the repricing's effective date, `oxygen_99` from it (decision 2.7).
|
|
65
106
|
*/
|
|
66
|
-
export declare function
|
|
107
|
+
export declare function byokMinimumPlanKey(options?: RepricingOptions): "oxygen_49" | "oxygen_99";
|
|
108
|
+
/** How a refusal names `byokMinimumPlanKey` in a sentence: "a paid plan" or "the $99 plan or above". */
|
|
109
|
+
export declare function byokMinimumPlanPhrase(options?: RepricingOptions): string;
|
|
110
|
+
/**
|
|
111
|
+
* P-20 (PROPOSED default): a $49 workspace keeps using the keys it connected
|
|
112
|
+
* before the repricing's effective date until its plan changes; new keys need
|
|
113
|
+
* $99. True only for a connection made before an instant that has passed, on
|
|
114
|
+
* the plan key `oxygen_49` — any other plan either includes BYOK or never did.
|
|
115
|
+
*
|
|
116
|
+
* "Until the plan changes" is read from the current plan key: a move off $49
|
|
117
|
+
* ends the grandfathering. No plan-change history is stored, so a workspace that
|
|
118
|
+
* leaves $49 and comes back after the date is not told apart from one that
|
|
119
|
+
* stayed; only keys connected before the date ever qualify.
|
|
120
|
+
*/
|
|
121
|
+
export declare function byokConnectionGrandfathered(input: {
|
|
122
|
+
planKey: string | null | undefined;
|
|
123
|
+
connectionCreatedAt: Date | string | null | undefined;
|
|
124
|
+
}, options?: RepricingOptions): boolean;
|
|
125
|
+
/**
|
|
126
|
+
* The access requirements of a tool that runs ONLY on the customer's own key.
|
|
127
|
+
*
|
|
128
|
+
* One definition for the provider catalog, the Composio catalog and both access
|
|
129
|
+
* resolvers. The tier list is display metadata: every paid tier has at least one
|
|
130
|
+
* plan with BYOK, and whether THIS plan has it is `plan.byokEnabled`, which the
|
|
131
|
+
* access resolver checks separately.
|
|
132
|
+
*/
|
|
133
|
+
export declare function byokOnlyAccessRequirements(): {
|
|
134
|
+
byok_required: true;
|
|
135
|
+
required_plan_tiers: PlanTier[];
|
|
136
|
+
};
|
|
67
137
|
export declare const FREE_TIER_ENTITLEMENT_ENABLED_ENV_VAR = "OXYGEN_FREE_TIER_ENTITLEMENT_ENABLED";
|
|
68
138
|
/**
|
|
69
139
|
* Fail-closed rollout switch for capability-scoped entitlement.
|
|
@@ -25,13 +25,21 @@
|
|
|
25
25
|
* live in docs/free-tier-capability-matrix.md.
|
|
26
26
|
*
|
|
27
27
|
* WHY GATES ARE CHECKED BEFORE BALANCE. A connected sender is also a FIXED
|
|
28
|
-
* monthly credit commitment (
|
|
29
|
-
*
|
|
30
|
-
* workspace could buy
|
|
28
|
+
* monthly credit commitment (100 per mailbox, 1,000 per LinkedIn account,
|
|
29
|
+
* 1,000 per WhatsApp number), and top-ups stay purchasable on free — so a free
|
|
30
|
+
* workspace could buy 1,000 credits for $12.50 and fund a LinkedIn seat that
|
|
31
31
|
* Starter prices at $99. Enforcing by balance would therefore price sending at
|
|
32
32
|
* an eighth of Starter. The capability gate must run BEFORE any balance or
|
|
33
33
|
* commitment check so the customer is told to upgrade, not to top up.
|
|
34
|
+
*
|
|
35
|
+
* THE 2026-09 REPRICING ENDS THAT ARGUMENT (decision 2.7). Connected accounts
|
|
36
|
+
* are repriced as credit reservations at their real price, so once they are
|
|
37
|
+
* billed that way (the credit gate, `OXYGEN_CREDIT_GATE_ENABLED`) credits are the
|
|
38
|
+
* only gate and the sending walls retire: `CREDIT_GATE_RETIRED_CAPABILITIES`.
|
|
39
|
+
* Until the gate is in force everything above still holds.
|
|
34
40
|
*/
|
|
41
|
+
import { CREDIT_GATE_RETIRED_CAPABILITIES, isCapabilityRetiredByCreditGate } from "./credit-gate.js";
|
|
42
|
+
import { OXYGEN_49_BYOK_REPRICING, readRepricingSwitch, repricedValue, } from "./repricing.js";
|
|
35
43
|
export const PLAN_GATED_CAPABILITIES = [
|
|
36
44
|
"sender_connect",
|
|
37
45
|
"sequence_dispatch",
|
|
@@ -94,15 +102,87 @@ export function isPlanGatedCapability(value) {
|
|
|
94
102
|
&& PLAN_GATED_CAPABILITIES.includes(value));
|
|
95
103
|
}
|
|
96
104
|
/**
|
|
97
|
-
* The
|
|
105
|
+
* The walls credits replace once the credit gate is in force (repricing
|
|
106
|
+
* 2026-09, decision 2.7, slice S22).
|
|
107
|
+
*
|
|
108
|
+
* "Credits are the only gate": a free workspace may connect accounts, send,
|
|
109
|
+
* publish and buy managed infrastructure if it holds the credits. Each of these
|
|
110
|
+
* is paid for by a monthly credit reservation or a credit purchase, so the plan
|
|
111
|
+
* wall has nothing left to protect — but only once those reservations are
|
|
112
|
+
* actually BILLED, which is exactly what the credit gate asserts at boot
|
|
113
|
+
* (`assertCreditGateArmed`). Until then every one of them stays walled exactly
|
|
114
|
+
* as before.
|
|
115
|
+
*
|
|
116
|
+
* Two capabilities stay walled under the gate:
|
|
117
|
+
* - `byok_provider_keys`: bringing your own keys needs a plan that includes it
|
|
118
|
+
* ($99 or above from the repricing's effective date).
|
|
119
|
+
* - `publishing_connect`: the only publishing connects that still reach it are
|
|
120
|
+
* Instagram, TikTok, YouTube and Facebook, which have no reservation price yet
|
|
121
|
+
* (P-18). Connecting them without one would make them free and unmetered, so
|
|
122
|
+
* they stay on the plan until that price is ratified.
|
|
123
|
+
*/
|
|
124
|
+
export { CREDIT_GATE_RETIRED_CAPABILITIES, isCapabilityRetiredByCreditGate };
|
|
125
|
+
/**
|
|
126
|
+
* The whole boundary for one plan, in one expression.
|
|
98
127
|
*
|
|
99
|
-
* Note this asks about the
|
|
128
|
+
* Note this asks about the PLAN, not about entitlement. A churned workspace and
|
|
100
129
|
* a brand-new one both resolve to `free` and both get the same answer here —
|
|
101
130
|
* one entitlement, one capability set, differing only in grants and upgrade
|
|
102
131
|
* copy (founder, 2026-08-17).
|
|
132
|
+
*
|
|
133
|
+
* BYOK reads the plan's own `byokEnabled`, never the tier: every Oxygen size
|
|
134
|
+
* shares the tier `oxygen`, and only $99 and above include BYOK once the
|
|
135
|
+
* repricing is in force. Everything else is the founder's one line, free versus
|
|
136
|
+
* paid. `creditGateInForce` retires the walls credits pay for (above).
|
|
137
|
+
*/
|
|
138
|
+
export function planAllowsCapability(plan, capability, options = {}) {
|
|
139
|
+
if (options.creditGateInForce && isCapabilityRetiredByCreditGate(capability))
|
|
140
|
+
return true;
|
|
141
|
+
if (capability === "byok_provider_keys")
|
|
142
|
+
return plan.byokEnabled;
|
|
143
|
+
return plan.tier !== "free";
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* The smallest purchasable plan that includes BYOK right now: `oxygen_49` until
|
|
147
|
+
* the repricing's effective date, `oxygen_99` from it (decision 2.7).
|
|
148
|
+
*/
|
|
149
|
+
export function byokMinimumPlanKey(options = {}) {
|
|
150
|
+
return repricedValue(OXYGEN_49_BYOK_REPRICING, options) ? "oxygen_49" : "oxygen_99";
|
|
151
|
+
}
|
|
152
|
+
/** How a refusal names `byokMinimumPlanKey` in a sentence: "a paid plan" or "the $99 plan or above". */
|
|
153
|
+
export function byokMinimumPlanPhrase(options = {}) {
|
|
154
|
+
return byokMinimumPlanKey(options) === "oxygen_99" ? "the $99 plan or above" : "a paid plan";
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* P-20 (PROPOSED default): a $49 workspace keeps using the keys it connected
|
|
158
|
+
* before the repricing's effective date until its plan changes; new keys need
|
|
159
|
+
* $99. True only for a connection made before an instant that has passed, on
|
|
160
|
+
* the plan key `oxygen_49` — any other plan either includes BYOK or never did.
|
|
161
|
+
*
|
|
162
|
+
* "Until the plan changes" is read from the current plan key: a move off $49
|
|
163
|
+
* ends the grandfathering. No plan-change history is stored, so a workspace that
|
|
164
|
+
* leaves $49 and comes back after the date is not told apart from one that
|
|
165
|
+
* stayed; only keys connected before the date ever qualify.
|
|
166
|
+
*/
|
|
167
|
+
export function byokConnectionGrandfathered(input, options = {}) {
|
|
168
|
+
if (input.planKey !== "oxygen_49" || !input.connectionCreatedAt)
|
|
169
|
+
return false;
|
|
170
|
+
const state = readRepricingSwitch(options);
|
|
171
|
+
if (state.status !== "in_force")
|
|
172
|
+
return false;
|
|
173
|
+
const createdAt = new Date(input.connectionCreatedAt).getTime();
|
|
174
|
+
return Number.isFinite(createdAt) && createdAt < state.effectiveAt.getTime();
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* The access requirements of a tool that runs ONLY on the customer's own key.
|
|
178
|
+
*
|
|
179
|
+
* One definition for the provider catalog, the Composio catalog and both access
|
|
180
|
+
* resolvers. The tier list is display metadata: every paid tier has at least one
|
|
181
|
+
* plan with BYOK, and whether THIS plan has it is `plan.byokEnabled`, which the
|
|
182
|
+
* access resolver checks separately.
|
|
103
183
|
*/
|
|
104
|
-
export function
|
|
105
|
-
return
|
|
184
|
+
export function byokOnlyAccessRequirements() {
|
|
185
|
+
return { byok_required: true, required_plan_tiers: [...PLAN_GATE_QUALIFYING_TIERS] };
|
|
106
186
|
}
|
|
107
187
|
export const FREE_TIER_ENTITLEMENT_ENABLED_ENV_VAR = "OXYGEN_FREE_TIER_ENTITLEMENT_ENABLED";
|
|
108
188
|
/**
|