@oxygen-agent/cli 1.1010.650 → 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 +15 -1
- 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/inbox-needs-reply-notice.d.ts +12 -0
- package/dist/inbox-needs-reply-notice.js +51 -0
- package/dist/index.js +756 -177
- package/dist/run-wait.d.ts +3 -1
- package/dist/run-wait.js +19 -5
- package/dist/skills.js +48 -22
- 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 +50 -2
- package/node_modules/@oxygen/shared/dist/billing-anchors.js +94 -2
- package/node_modules/@oxygen/shared/dist/billing.d.ts +247 -37
- package/node_modules/@oxygen/shared/dist/billing.js +418 -45
- package/node_modules/@oxygen/shared/dist/capability-discovery.js +66 -6
- package/node_modules/@oxygen/shared/dist/copilot-skills.generated.d.ts +6 -6
- package/node_modules/@oxygen/shared/dist/copilot-skills.generated.js +6 -6
- 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 +27 -0
- package/node_modules/@oxygen/shared/dist/email-hard-bounce.js +27 -0
- 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 +10 -1
- package/node_modules/@oxygen/shared/dist/feature-gates.js +12 -1
- 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 +4 -23
- package/node_modules/@oxygen/shared/dist/index.js +4 -43
- package/node_modules/@oxygen/shared/dist/linkedin-sequences.d.ts +114 -0
- package/node_modules/@oxygen/shared/dist/linkedin-sequences.js +150 -0
- 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/otlp-log-sink.js +19 -2
- package/node_modules/@oxygen/shared/dist/plan-band.d.ts +234 -0
- package/node_modules/@oxygen/shared/dist/plan-band.js +312 -0
- 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 +335 -126
- package/node_modules/@oxygen/shared/dist/plan-limits.js +277 -86
- package/node_modules/@oxygen/shared/dist/pricing-sheet.d.ts +158 -49
- package/node_modules/@oxygen/shared/dist/pricing-sheet.js +139 -41
- package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.d.ts +42 -23
- package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.js +56 -37
- package/node_modules/@oxygen/shared/dist/process-resource.d.ts +4 -0
- package/node_modules/@oxygen/shared/dist/process-resource.js +25 -0
- 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 +257 -0
- package/node_modules/@oxygen/shared/dist/repricing.js +721 -0
- 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 +30 -0
- package/node_modules/@oxygen/shared/dist/sending-limits.js +43 -0
- 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/sequence-failures.js +4 -1
- 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 +33 -1
- package/node_modules/@oxygen/shared/dist/stripe-price-catalog.js +71 -1
- package/node_modules/@oxygen/shared/dist/table-capacity.d.ts +68 -10
- package/node_modules/@oxygen/shared/dist/table-capacity.js +85 -4
- 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.d.ts +9 -0
- package/node_modules/@oxygen/shared/dist/telemetry.js +41 -2
- package/node_modules/@oxygen/shared/dist/trace-context.d.ts +29 -0
- package/node_modules/@oxygen/shared/dist/trace-context.js +88 -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
- package/node_modules/@oxygen/shared/dist/email-warmup-readiness.d.ts +0 -64
- package/node_modules/@oxygen/shared/dist/email-warmup-readiness.js +0 -90
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { resolveBasePricingPlan } from "./billing.js";
|
|
2
|
+
import { OxygenError } from "./cli-result.js";
|
|
2
3
|
import { WORKSPACE_TABLE_CAPACITY } from "./table-capacity.js";
|
|
4
|
+
import { findRepricedValue, repricedValue } from "./repricing.js";
|
|
3
5
|
export const LIMITS_TIER_ORDER = [
|
|
4
6
|
"free",
|
|
5
7
|
"starter",
|
|
@@ -11,10 +13,12 @@ const HOUR = 60 * 60;
|
|
|
11
13
|
const DAY = 24 * HOUR;
|
|
12
14
|
const MIB = 1024 * 1024;
|
|
13
15
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* The per-rung Table import ceiling this matrix reports: today's envelope. The
|
|
17
|
+
* ceiling ENFORCED on an import is the workspace's plan-band rows-per-Table
|
|
18
|
+
* limit (`resolveWorkspaceTableCapacity`, PROPOSED P-60: a file can fill an
|
|
19
|
+
* empty Table exactly), which the web import paths resolve per organization.
|
|
20
|
+
* Imports into a non-empty Table remain subject to that Table's remaining
|
|
21
|
+
* capacity and the workspace-wide envelope.
|
|
18
22
|
*/
|
|
19
23
|
export const TABLE_IMPORT_ROW_LIMIT = WORKSPACE_TABLE_CAPACITY.tableRowLimit;
|
|
20
24
|
// The platform request-body ceiling belongs to the same question as the import
|
|
@@ -29,42 +33,77 @@ export { VERCEL_REQUEST_BODY_LIMIT_BYTES } from "./import-limits.js";
|
|
|
29
33
|
* in the CLI alone — an older server would still 413 at the old number.
|
|
30
34
|
*/
|
|
31
35
|
export const MAX_CLI_JSON_BODY_BYTES = 2_000_000;
|
|
36
|
+
/**
|
|
37
|
+
* Derived plan-scaled limits (repricing 2026-09 decision L6, spec § 4.7):
|
|
38
|
+
* import file size, columns per action run or standing auto-run and the signals
|
|
39
|
+
* feed. Each only rises against the pre-repricing ladder, so no paid customer
|
|
40
|
+
* loses a limit and nothing here waits for the effective-date switch. Rows per
|
|
41
|
+
* write request stay 5,000 free / 50,000 paid: the 2 MB request body
|
|
42
|
+
* (`MAX_CLI_JSON_BODY_BYTES`) binds first.
|
|
43
|
+
*
|
|
44
|
+
* `importFileBytes` is ratified (decision F.8, Philipp 2026-09-27): free 500 MiB,
|
|
45
|
+
* $49–$99 1 GiB, $199–$499 2 GiB, $999–$1,999 5 GiB. It is live on every plan
|
|
46
|
+
* and no longer waits for the per-Table capacity test: rows per Table still cap
|
|
47
|
+
* what one import can load, so a bigger file never buys more rows. 5 GiB is also
|
|
48
|
+
* the largest single PUT an S3-compatible store accepts
|
|
49
|
+
* (`IMPORT_OBJECT_SINGLE_PUT_MAX_BYTES`), which is the one upload a staged
|
|
50
|
+
* import makes.
|
|
51
|
+
*/
|
|
52
|
+
export const DERIVED_PLAN_LIMIT_TARGETS = {
|
|
53
|
+
free: { importFileBytes: 500 * MIB, maxActionsPerRun: 10, signals: { maxEvents: 100, maxWindowDays: 30 } },
|
|
54
|
+
starter: { importFileBytes: 1_024 * MIB, maxActionsPerRun: 25, signals: { maxEvents: 200, maxWindowDays: 90 } },
|
|
55
|
+
pro: { importFileBytes: 2_048 * MIB, maxActionsPerRun: 25, signals: { maxEvents: 500, maxWindowDays: 180 } },
|
|
56
|
+
team: { importFileBytes: 5_120 * MIB, maxActionsPerRun: 50, signals: { maxEvents: 1_000, maxWindowDays: 365 } },
|
|
57
|
+
scale: { importFileBytes: 5_120 * MIB, maxActionsPerRun: 50, signals: { maxEvents: 1_000, maxWindowDays: 365 } },
|
|
58
|
+
};
|
|
59
|
+
/** The import file size a rung enforces (see `DERIVED_PLAN_LIMIT_TARGETS`). */
|
|
60
|
+
export function importMaxFileBytesForTier(tier) {
|
|
61
|
+
return DERIVED_PLAN_LIMIT_TARGETS[tier].importFileBytes;
|
|
62
|
+
}
|
|
32
63
|
/**
|
|
33
64
|
* The per-rung limit matrix. `ai_live` deliberately equals `tool_live` at every
|
|
34
65
|
* rung — one "live actions" mental model; the per-call cost asymmetry between
|
|
35
66
|
* AI and tool calls is priced in credits, not throttled here.
|
|
67
|
+
*
|
|
68
|
+
* API rates follow the 2026-09 repricing (decision L2, ratified 2026-09-26):
|
|
69
|
+
* `cli.global` is the per-organization and per-key request total per minute,
|
|
70
|
+
* 1,200 / 3,000 / 6,000 / 12,000 / 30,000 from free to scale. The per-operation
|
|
71
|
+
* buckets below are PROPOSED (P-52, repricing spec § 4.2): each pre-repricing
|
|
72
|
+
* bucket multiplied by its rung's organization factor, 2x on free and 5x on
|
|
73
|
+
* every paid rung, and never above the rung's per-key total. `tenantSchema` and
|
|
74
|
+
* the flat `tenant_admin` guard in the web rate limiter are DDL abuse guards and
|
|
75
|
+
* stay unscaled.
|
|
36
76
|
*/
|
|
37
77
|
export const PLAN_LIMITS = {
|
|
38
78
|
free: {
|
|
39
79
|
cli: {
|
|
40
|
-
global: { keyRequestsPerMinute:
|
|
41
|
-
tenantRead: { requestsPerMinute:
|
|
42
|
-
tenantWrite: { requestsPerMinute:
|
|
43
|
-
bulkImport: { requestsPerMinute:
|
|
44
|
-
aiLive: { requestsPerMinute:
|
|
45
|
-
toolLive: { requestsPerMinute:
|
|
46
|
-
toolDryRun: { requestsPerMinute:
|
|
80
|
+
global: { keyRequestsPerMinute: 1_200, orgRequestsPerMinute: 1_200 },
|
|
81
|
+
tenantRead: { requestsPerMinute: 120, orgUnits: { limit: 1_000_000, windowSeconds: HOUR } },
|
|
82
|
+
tenantWrite: { requestsPerMinute: 60, orgUnits: { limit: 200_000, windowSeconds: HOUR } },
|
|
83
|
+
bulkImport: { requestsPerMinute: 20, orgRequests: { limit: 200, windowSeconds: HOUR } },
|
|
84
|
+
aiLive: { requestsPerMinute: 40, orgRequests: { limit: 200, windowSeconds: HOUR } },
|
|
85
|
+
toolLive: { requestsPerMinute: 40, orgRequests: { limit: 200, windowSeconds: HOUR } },
|
|
86
|
+
toolDryRun: { requestsPerMinute: 120, orgRequests: { limit: 1_000, windowSeconds: HOUR } },
|
|
47
87
|
tenantSchema: { requestsPerMinute: 20, orgRequests: { limit: 100, windowSeconds: HOUR } },
|
|
48
88
|
maxWriteRowsPerRequest: 5_000,
|
|
49
89
|
},
|
|
50
90
|
web: {
|
|
51
|
-
tenantRead: { requestsPerMinute:
|
|
52
|
-
tenantWrite: { requestsPerMinute:
|
|
53
|
-
columnRun: { requestsPerMinute:
|
|
54
|
-
import: { requestsPerMinute:
|
|
91
|
+
tenantRead: { requestsPerMinute: 240, orgUnits: { limit: 2_000_000, windowSeconds: HOUR } },
|
|
92
|
+
tenantWrite: { requestsPerMinute: 120, orgUnits: { limit: 1_000_000, windowSeconds: HOUR } },
|
|
93
|
+
columnRun: { requestsPerMinute: 60, orgUnits: { limit: 4_000, windowSeconds: HOUR } },
|
|
94
|
+
import: { requestsPerMinute: 20, orgRequests: { limit: 200, windowSeconds: HOUR } },
|
|
55
95
|
},
|
|
56
96
|
import: {
|
|
57
97
|
maxRowsPerFile: TABLE_IMPORT_ROW_LIMIT,
|
|
58
|
-
maxFileBytes:
|
|
98
|
+
maxFileBytes: importMaxFileBytesForTier("free"),
|
|
59
99
|
maxDirectCsvBodyBytes: 10_000_000,
|
|
60
100
|
},
|
|
61
|
-
// Sized against the free tier's actual funding (OXP-10.2.4, 2026-08-17):
|
|
62
|
-
//
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
// the whole signup grant before the customer saw anything work.
|
|
101
|
+
// Sized against the free tier's actual funding (OXP-10.2.4, 2026-08-17): one
|
|
102
|
+
// signup grant of FREE_SIGNUP_GRANT_CREDITS (1,000) with no monthly allowance
|
|
103
|
+
// to refill it. The previous ceilings were written when `free` meant a
|
|
104
|
+
// churned org that could spend nothing, so they were harmless at any value;
|
|
105
|
+
// an Agent run capped at the old 1,000 (today's unit) could consume the
|
|
106
|
+
// ENTIRE lifetime grant before the customer saw anything work.
|
|
68
107
|
//
|
|
69
108
|
// These are runaway guards, not the billing meter — a workspace that tops up
|
|
70
109
|
// is bounded by its balance, not by these. The intent is only that no single
|
|
@@ -75,36 +114,40 @@ export const PLAN_LIMITS = {
|
|
|
75
114
|
maxUpstreamCostUsd: 10,
|
|
76
115
|
maxUpstreamCostUsdByok: 10,
|
|
77
116
|
},
|
|
78
|
-
// Left at 5,000/1,500
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
// split grant exists to prevent.
|
|
117
|
+
// Left at 500/150 deliberately (5,000/1,500 before the 2026-09-19 unit
|
|
118
|
+
// change). A founder who picks the in-product path over the CLI must be able
|
|
119
|
+
// to complete one real Copilot session on the 1,000-credit signup grant
|
|
120
|
+
// alone. Lowering this would re-create a dead-on-arrival Copilot.
|
|
83
121
|
copilot: { maxSessionBudgetCredits: 500, defaultPerTurnCreditCeiling: 150 },
|
|
84
|
-
signals:
|
|
85
|
-
tableActions: { maxActionsPerRun:
|
|
122
|
+
signals: DERIVED_PLAN_LIMIT_TARGETS.free.signals,
|
|
123
|
+
tableActions: { maxActionsPerRun: DERIVED_PLAN_LIMIT_TARGETS.free.maxActionsPerRun },
|
|
124
|
+
// Decision L4.2 (ratified 2026-09-26): a free workspace schedules at most
|
|
125
|
+
// every 15 minutes, paid plans every minute. Dark until the repricing switch
|
|
126
|
+
// (FREE_CRON_MIN_INTERVAL_REPRICING); a faster schedule already armed is
|
|
127
|
+
// slowed to this interval at fire time, never deleted (proposal P-47).
|
|
128
|
+
workflows: { cronMinIntervalMinutes: 15 },
|
|
86
129
|
},
|
|
87
130
|
starter: {
|
|
88
131
|
cli: {
|
|
89
|
-
global: { keyRequestsPerMinute:
|
|
90
|
-
tenantRead: { requestsPerMinute:
|
|
91
|
-
tenantWrite: { requestsPerMinute:
|
|
92
|
-
bulkImport: { requestsPerMinute:
|
|
93
|
-
aiLive: { requestsPerMinute:
|
|
94
|
-
toolLive: { requestsPerMinute:
|
|
95
|
-
toolDryRun: { requestsPerMinute:
|
|
132
|
+
global: { keyRequestsPerMinute: 3_000, orgRequestsPerMinute: 3_000 },
|
|
133
|
+
tenantRead: { requestsPerMinute: 600, orgUnits: { limit: 125_000_000, windowSeconds: DAY } },
|
|
134
|
+
tenantWrite: { requestsPerMinute: 600, orgUnits: { limit: 25_000_000, windowSeconds: DAY } },
|
|
135
|
+
bulkImport: { requestsPerMinute: 150, orgRequests: { limit: 2_500, windowSeconds: HOUR } },
|
|
136
|
+
aiLive: { requestsPerMinute: 300, orgRequests: { limit: 25_000, windowSeconds: HOUR } },
|
|
137
|
+
toolLive: { requestsPerMinute: 300, orgRequests: { limit: 25_000, windowSeconds: HOUR } },
|
|
138
|
+
toolDryRun: { requestsPerMinute: 600, orgRequests: { limit: 10_000, windowSeconds: HOUR } },
|
|
96
139
|
tenantSchema: { requestsPerMinute: 60, orgRequests: { limit: 600, windowSeconds: HOUR } },
|
|
97
140
|
maxWriteRowsPerRequest: 50_000,
|
|
98
141
|
},
|
|
99
142
|
web: {
|
|
100
|
-
tenantRead: { requestsPerMinute:
|
|
101
|
-
tenantWrite: { requestsPerMinute:
|
|
102
|
-
columnRun: { requestsPerMinute:
|
|
103
|
-
import: { requestsPerMinute:
|
|
143
|
+
tenantRead: { requestsPerMinute: 1_500, orgUnits: { limit: 100_000_000, windowSeconds: DAY } },
|
|
144
|
+
tenantWrite: { requestsPerMinute: 900, orgUnits: { limit: 50_000_000, windowSeconds: DAY } },
|
|
145
|
+
columnRun: { requestsPerMinute: 600, orgUnits: { limit: 2_500_000, windowSeconds: DAY } },
|
|
146
|
+
import: { requestsPerMinute: 150, orgRequests: { limit: 2_500, windowSeconds: HOUR } },
|
|
104
147
|
},
|
|
105
148
|
import: {
|
|
106
149
|
maxRowsPerFile: TABLE_IMPORT_ROW_LIMIT,
|
|
107
|
-
maxFileBytes:
|
|
150
|
+
maxFileBytes: importMaxFileBytesForTier("starter"),
|
|
108
151
|
maxDirectCsvBodyBytes: 10_000_000,
|
|
109
152
|
},
|
|
110
153
|
agents: {
|
|
@@ -114,30 +157,31 @@ export const PLAN_LIMITS = {
|
|
|
114
157
|
maxUpstreamCostUsdByok: 250,
|
|
115
158
|
},
|
|
116
159
|
copilot: { maxSessionBudgetCredits: 500, defaultPerTurnCreditCeiling: 150 },
|
|
117
|
-
signals:
|
|
118
|
-
tableActions: { maxActionsPerRun:
|
|
160
|
+
signals: DERIVED_PLAN_LIMIT_TARGETS.starter.signals,
|
|
161
|
+
tableActions: { maxActionsPerRun: DERIVED_PLAN_LIMIT_TARGETS.starter.maxActionsPerRun },
|
|
162
|
+
workflows: { cronMinIntervalMinutes: 1 },
|
|
119
163
|
},
|
|
120
164
|
pro: {
|
|
121
165
|
cli: {
|
|
122
|
-
global: { keyRequestsPerMinute:
|
|
123
|
-
tenantRead: { requestsPerMinute:
|
|
124
|
-
tenantWrite: { requestsPerMinute:
|
|
125
|
-
bulkImport: { requestsPerMinute:
|
|
126
|
-
aiLive: { requestsPerMinute:
|
|
127
|
-
toolLive: { requestsPerMinute:
|
|
128
|
-
toolDryRun: { requestsPerMinute:
|
|
166
|
+
global: { keyRequestsPerMinute: 6_000, orgRequestsPerMinute: 6_000 },
|
|
167
|
+
tenantRead: { requestsPerMinute: 1_200, orgUnits: { limit: 250_000_000, windowSeconds: DAY } },
|
|
168
|
+
tenantWrite: { requestsPerMinute: 1_200, orgUnits: { limit: 50_000_000, windowSeconds: DAY } },
|
|
169
|
+
bulkImport: { requestsPerMinute: 300, orgRequests: { limit: 5_000, windowSeconds: HOUR } },
|
|
170
|
+
aiLive: { requestsPerMinute: 600, orgRequests: { limit: 60_000, windowSeconds: HOUR } },
|
|
171
|
+
toolLive: { requestsPerMinute: 600, orgRequests: { limit: 60_000, windowSeconds: HOUR } },
|
|
172
|
+
toolDryRun: { requestsPerMinute: 600, orgRequests: { limit: 10_000, windowSeconds: HOUR } },
|
|
129
173
|
tenantSchema: { requestsPerMinute: 60, orgRequests: { limit: 600, windowSeconds: HOUR } },
|
|
130
174
|
maxWriteRowsPerRequest: 50_000,
|
|
131
175
|
},
|
|
132
176
|
web: {
|
|
133
|
-
tenantRead: { requestsPerMinute:
|
|
134
|
-
tenantWrite: { requestsPerMinute:
|
|
135
|
-
columnRun: { requestsPerMinute:
|
|
136
|
-
import: { requestsPerMinute:
|
|
177
|
+
tenantRead: { requestsPerMinute: 3_000, orgUnits: { limit: 200_000_000, windowSeconds: DAY } },
|
|
178
|
+
tenantWrite: { requestsPerMinute: 1_800, orgUnits: { limit: 100_000_000, windowSeconds: DAY } },
|
|
179
|
+
columnRun: { requestsPerMinute: 1_200, orgUnits: { limit: 5_000_000, windowSeconds: DAY } },
|
|
180
|
+
import: { requestsPerMinute: 300, orgRequests: { limit: 5_000, windowSeconds: HOUR } },
|
|
137
181
|
},
|
|
138
182
|
import: {
|
|
139
183
|
maxRowsPerFile: TABLE_IMPORT_ROW_LIMIT,
|
|
140
|
-
maxFileBytes:
|
|
184
|
+
maxFileBytes: importMaxFileBytesForTier("pro"),
|
|
141
185
|
maxDirectCsvBodyBytes: 10_000_000,
|
|
142
186
|
},
|
|
143
187
|
agents: {
|
|
@@ -147,30 +191,31 @@ export const PLAN_LIMITS = {
|
|
|
147
191
|
maxUpstreamCostUsdByok: 250,
|
|
148
192
|
},
|
|
149
193
|
copilot: { maxSessionBudgetCredits: 1_000, defaultPerTurnCreditCeiling: 250 },
|
|
150
|
-
signals:
|
|
151
|
-
tableActions: { maxActionsPerRun:
|
|
194
|
+
signals: DERIVED_PLAN_LIMIT_TARGETS.pro.signals,
|
|
195
|
+
tableActions: { maxActionsPerRun: DERIVED_PLAN_LIMIT_TARGETS.pro.maxActionsPerRun },
|
|
196
|
+
workflows: { cronMinIntervalMinutes: 1 },
|
|
152
197
|
},
|
|
153
198
|
team: {
|
|
154
199
|
cli: {
|
|
155
|
-
global: { keyRequestsPerMinute:
|
|
156
|
-
tenantRead: { requestsPerMinute:
|
|
157
|
-
tenantWrite: { requestsPerMinute:
|
|
158
|
-
bulkImport: { requestsPerMinute:
|
|
159
|
-
aiLive: { requestsPerMinute:
|
|
160
|
-
toolLive: { requestsPerMinute:
|
|
161
|
-
toolDryRun: { requestsPerMinute:
|
|
200
|
+
global: { keyRequestsPerMinute: 12_000, orgRequestsPerMinute: 12_000 },
|
|
201
|
+
tenantRead: { requestsPerMinute: 2_400, orgUnits: { limit: 500_000_000, windowSeconds: DAY } },
|
|
202
|
+
tenantWrite: { requestsPerMinute: 2_400, orgUnits: { limit: 125_000_000, windowSeconds: DAY } },
|
|
203
|
+
bulkImport: { requestsPerMinute: 600, orgRequests: { limit: 10_000, windowSeconds: HOUR } },
|
|
204
|
+
aiLive: { requestsPerMinute: 1_200, orgRequests: { limit: 150_000, windowSeconds: HOUR } },
|
|
205
|
+
toolLive: { requestsPerMinute: 1_200, orgRequests: { limit: 150_000, windowSeconds: HOUR } },
|
|
206
|
+
toolDryRun: { requestsPerMinute: 600, orgRequests: { limit: 10_000, windowSeconds: HOUR } },
|
|
162
207
|
tenantSchema: { requestsPerMinute: 60, orgRequests: { limit: 600, windowSeconds: HOUR } },
|
|
163
208
|
maxWriteRowsPerRequest: 50_000,
|
|
164
209
|
},
|
|
165
210
|
web: {
|
|
166
|
-
tenantRead: { requestsPerMinute:
|
|
167
|
-
tenantWrite: { requestsPerMinute:
|
|
168
|
-
columnRun: { requestsPerMinute:
|
|
169
|
-
import: { requestsPerMinute:
|
|
211
|
+
tenantRead: { requestsPerMinute: 6_000, orgUnits: { limit: 500_000_000, windowSeconds: DAY } },
|
|
212
|
+
tenantWrite: { requestsPerMinute: 3_600, orgUnits: { limit: 250_000_000, windowSeconds: DAY } },
|
|
213
|
+
columnRun: { requestsPerMinute: 2_400, orgUnits: { limit: 12_500_000, windowSeconds: DAY } },
|
|
214
|
+
import: { requestsPerMinute: 600, orgRequests: { limit: 10_000, windowSeconds: HOUR } },
|
|
170
215
|
},
|
|
171
216
|
import: {
|
|
172
217
|
maxRowsPerFile: TABLE_IMPORT_ROW_LIMIT,
|
|
173
|
-
maxFileBytes:
|
|
218
|
+
maxFileBytes: importMaxFileBytesForTier("team"),
|
|
174
219
|
maxDirectCsvBodyBytes: 10_000_000,
|
|
175
220
|
},
|
|
176
221
|
agents: {
|
|
@@ -180,30 +225,31 @@ export const PLAN_LIMITS = {
|
|
|
180
225
|
maxUpstreamCostUsdByok: 250,
|
|
181
226
|
},
|
|
182
227
|
copilot: { maxSessionBudgetCredits: 2_000, defaultPerTurnCreditCeiling: 500 },
|
|
183
|
-
signals:
|
|
184
|
-
tableActions: { maxActionsPerRun:
|
|
228
|
+
signals: DERIVED_PLAN_LIMIT_TARGETS.team.signals,
|
|
229
|
+
tableActions: { maxActionsPerRun: DERIVED_PLAN_LIMIT_TARGETS.team.maxActionsPerRun },
|
|
230
|
+
workflows: { cronMinIntervalMinutes: 1 },
|
|
185
231
|
},
|
|
186
232
|
scale: {
|
|
187
233
|
cli: {
|
|
188
|
-
global: { keyRequestsPerMinute:
|
|
189
|
-
tenantRead: { requestsPerMinute:
|
|
190
|
-
tenantWrite: { requestsPerMinute:
|
|
191
|
-
bulkImport: { requestsPerMinute:
|
|
192
|
-
aiLive: { requestsPerMinute:
|
|
193
|
-
toolLive: { requestsPerMinute:
|
|
194
|
-
toolDryRun: { requestsPerMinute:
|
|
234
|
+
global: { keyRequestsPerMinute: 30_000, orgRequestsPerMinute: 30_000 },
|
|
235
|
+
tenantRead: { requestsPerMinute: 5_000, orgUnits: { limit: 2_500_000_000, windowSeconds: DAY } },
|
|
236
|
+
tenantWrite: { requestsPerMinute: 5_000, orgUnits: { limit: 500_000_000, windowSeconds: DAY } },
|
|
237
|
+
bulkImport: { requestsPerMinute: 1_200, orgRequests: { limit: 25_000, windowSeconds: HOUR } },
|
|
238
|
+
aiLive: { requestsPerMinute: 3_000, orgRequests: { limit: 500_000, windowSeconds: HOUR } },
|
|
239
|
+
toolLive: { requestsPerMinute: 3_000, orgRequests: { limit: 500_000, windowSeconds: HOUR } },
|
|
240
|
+
toolDryRun: { requestsPerMinute: 600, orgRequests: { limit: 10_000, windowSeconds: HOUR } },
|
|
195
241
|
tenantSchema: { requestsPerMinute: 60, orgRequests: { limit: 600, windowSeconds: HOUR } },
|
|
196
242
|
maxWriteRowsPerRequest: 50_000,
|
|
197
243
|
},
|
|
198
244
|
web: {
|
|
199
|
-
tenantRead: { requestsPerMinute:
|
|
200
|
-
tenantWrite: { requestsPerMinute:
|
|
201
|
-
columnRun: { requestsPerMinute:
|
|
202
|
-
import: { requestsPerMinute:
|
|
245
|
+
tenantRead: { requestsPerMinute: 10_000, orgUnits: { limit: 2_000_000_000, windowSeconds: DAY } },
|
|
246
|
+
tenantWrite: { requestsPerMinute: 7_500, orgUnits: { limit: 1_000_000_000, windowSeconds: DAY } },
|
|
247
|
+
columnRun: { requestsPerMinute: 5_000, orgUnits: { limit: 50_000_000, windowSeconds: DAY } },
|
|
248
|
+
import: { requestsPerMinute: 1_200, orgRequests: { limit: 25_000, windowSeconds: HOUR } },
|
|
203
249
|
},
|
|
204
250
|
import: {
|
|
205
251
|
maxRowsPerFile: TABLE_IMPORT_ROW_LIMIT,
|
|
206
|
-
maxFileBytes:
|
|
252
|
+
maxFileBytes: importMaxFileBytesForTier("scale"),
|
|
207
253
|
maxDirectCsvBodyBytes: 10_000_000,
|
|
208
254
|
},
|
|
209
255
|
agents: {
|
|
@@ -213,10 +259,32 @@ export const PLAN_LIMITS = {
|
|
|
213
259
|
maxUpstreamCostUsdByok: 250,
|
|
214
260
|
},
|
|
215
261
|
copilot: { maxSessionBudgetCredits: 5_000, defaultPerTurnCreditCeiling: 1_000 },
|
|
216
|
-
signals:
|
|
217
|
-
tableActions: { maxActionsPerRun:
|
|
262
|
+
signals: DERIVED_PLAN_LIMIT_TARGETS.scale.signals,
|
|
263
|
+
tableActions: { maxActionsPerRun: DERIVED_PLAN_LIMIT_TARGETS.scale.maxActionsPerRun },
|
|
264
|
+
workflows: { cronMinIntervalMinutes: 1 },
|
|
218
265
|
},
|
|
219
266
|
};
|
|
267
|
+
/**
|
|
268
|
+
* The fastest cron cadence, in minutes, a rung may schedule right now. The free
|
|
269
|
+
* rung's cut from every minute to every 15 (decision L4.2) is a tightening, so
|
|
270
|
+
* it resolves through the 2026-09 repricing switch: every minute until the
|
|
271
|
+
* effective instant, `PLAN_LIMITS.free.workflows.cronMinIntervalMinutes` from it.
|
|
272
|
+
*/
|
|
273
|
+
export function resolveCronMinIntervalMinutes(tier, options = {}) {
|
|
274
|
+
const scheduled = findRepricedValue(`workflows.${tier}.cron_min_interval_minutes`);
|
|
275
|
+
if (scheduled && typeof scheduled.before === "number" && typeof scheduled.after === "number") {
|
|
276
|
+
return repricedValue({ before: scheduled.before, after: scheduled.after }, options);
|
|
277
|
+
}
|
|
278
|
+
return PLAN_LIMITS[tier].workflows.cronMinIntervalMinutes;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* The largest minimum cron interval any rung enforces right now. A schedule
|
|
282
|
+
* whose fires are never closer than this fits every plan, so a caller can skip
|
|
283
|
+
* the plan lookup for it.
|
|
284
|
+
*/
|
|
285
|
+
export function highestCronMinIntervalMinutes(options = {}) {
|
|
286
|
+
return Math.max(...LIMITS_TIER_ORDER.map((tier) => resolveCronMinIntervalMinutes(tier, options)));
|
|
287
|
+
}
|
|
220
288
|
/**
|
|
221
289
|
* Which enforcement rung a rung of the six-rung Oxygen plan lands on.
|
|
222
290
|
*
|
|
@@ -260,3 +328,126 @@ export function limitsTierForPlanTier(tier) {
|
|
|
260
328
|
export function planLimitsForTier(tier) {
|
|
261
329
|
return PLAN_LIMITS[tier];
|
|
262
330
|
}
|
|
331
|
+
/**
|
|
332
|
+
* The default credit ceilings of one Agent run in force on a rung right now.
|
|
333
|
+
*
|
|
334
|
+
* `PLAN_LIMITS[tier].agents` holds the values each rung read before the 2026-09
|
|
335
|
+
* repricing. The team and scale rungs drop to 2,500 total / 1,250 inference
|
|
336
|
+
* (decision L5.4, inference half PROPOSED P-56), and a cut waits for the
|
|
337
|
+
* effective-date switch (`agents.<tier>.*` in `REPRICING_2026_09_SCHEDULE`).
|
|
338
|
+
* Every Agent budget check reads this, never the table, so the cut lands
|
|
339
|
+
* everywhere at the same instant.
|
|
340
|
+
*/
|
|
341
|
+
export function resolvePlanAgentCreditLimits(tier, options = {}) {
|
|
342
|
+
const agents = PLAN_LIMITS[tier].agents;
|
|
343
|
+
const resolve = (field, fallback) => {
|
|
344
|
+
const scheduled = findRepricedValue(`agents.${tier}.${field}`);
|
|
345
|
+
return scheduled && typeof scheduled.before === "number" && typeof scheduled.after === "number"
|
|
346
|
+
? repricedValue({ before: scheduled.before, after: scheduled.after }, options)
|
|
347
|
+
: fallback;
|
|
348
|
+
};
|
|
349
|
+
return {
|
|
350
|
+
maxTotalCredits: resolve("max_total_credits", agents.maxTotalCredits),
|
|
351
|
+
maxInferenceCredits: resolve("max_inference_credits", agents.maxInferenceCredits),
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* The platform's hard backstop on one Agent run's approved credit ceiling
|
|
356
|
+
* (decision F.4, ratified 2026-09-27).
|
|
357
|
+
*
|
|
358
|
+
* The plan's Agent run ceiling is a DEFAULT: a run or trigger whose approved
|
|
359
|
+
* `max_credits` fits under it needs only the usual approval. Going above it
|
|
360
|
+
* takes a second, explicit per-run (or per-trigger) approval, and even that
|
|
361
|
+
* approval stops here. 25,000 credits ($250) is the largest standing
|
|
362
|
+
* per-delivery default any plan has carried (the pre-2026-09 scale trigger
|
|
363
|
+
* default), so an approved Agent run never outspends one delivery of the
|
|
364
|
+
* largest plan's own automation. The other backstops stay in force whatever is
|
|
365
|
+
* approved: the plan's upstream-USD clamp, the iteration, tool-call and
|
|
366
|
+
* active-minute bounds, the balance check on every reservation and the org
|
|
367
|
+
* daily spend guard.
|
|
368
|
+
*/
|
|
369
|
+
export const AGENT_RUN_APPROVED_CEILING_MAX_CREDITS = 25_000;
|
|
370
|
+
/**
|
|
371
|
+
* The default Agent run ceilings for an org: its plan rung's, unless an
|
|
372
|
+
* enterprise contract names its own default (decision F.4). The contract value
|
|
373
|
+
* replaces the rung's total and model calls take half of it, as on every paid
|
|
374
|
+
* rung. A contract value is clamped to the platform backstop; an unusable one
|
|
375
|
+
* (absent, non-numeric, not positive) is ignored.
|
|
376
|
+
*/
|
|
377
|
+
export function resolveAgentRunDefaultCeilings(input) {
|
|
378
|
+
const contract = input.enterpriseDefaultCredits;
|
|
379
|
+
if (typeof contract === "number" && Number.isFinite(contract) && contract >= 1) {
|
|
380
|
+
const total = Math.min(Math.floor(contract), AGENT_RUN_APPROVED_CEILING_MAX_CREDITS);
|
|
381
|
+
return {
|
|
382
|
+
maxTotalCredits: total,
|
|
383
|
+
maxInferenceCredits: Math.max(1, Math.floor(total / 2)),
|
|
384
|
+
source: "enterprise_contract",
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
return { ...resolvePlanAgentCreditLimits(input.tier, input.options), source: "plan" };
|
|
388
|
+
}
|
|
389
|
+
/** `details.approval_kind` on the refusal below, so every surface can render its own approval card. */
|
|
390
|
+
export const AGENT_CEILING_ABOVE_DEFAULT_APPROVAL_KIND = "agent_ceiling_above_default";
|
|
391
|
+
/**
|
|
392
|
+
* Check one run's (or one trigger's per-delivery) approved ceiling against the
|
|
393
|
+
* org's default before anything is created.
|
|
394
|
+
*
|
|
395
|
+
* - At or under the default: nothing more to approve.
|
|
396
|
+
* - Above it: refused with `approval_required` unless the caller also passed the
|
|
397
|
+
* explicit above-default approval. The details carry the numbers and the flag
|
|
398
|
+
* each surface re-sends, so the refusal is the approval card.
|
|
399
|
+
* - Above the platform backstop: refused outright; no approval raises it.
|
|
400
|
+
*
|
|
401
|
+
* Returns whether the approved ceiling is above the default; the caller persists
|
|
402
|
+
* that as the run's or trigger's above-default approval.
|
|
403
|
+
*/
|
|
404
|
+
export function assertAgentRunCeilingApproval(input) {
|
|
405
|
+
const defaultMax = input.defaults.maxTotalCredits;
|
|
406
|
+
const subject = input.scope === "trigger" ? "each delivery of this trigger" : "this run";
|
|
407
|
+
if (input.maxCredits > AGENT_RUN_APPROVED_CEILING_MAX_CREDITS) {
|
|
408
|
+
throw new OxygenError("invalid_request", `max_credits ${input.maxCredits} is above the platform maximum of ${AGENT_RUN_APPROVED_CEILING_MAX_CREDITS} credits for ${subject}.`, {
|
|
409
|
+
exitCode: 2,
|
|
410
|
+
details: {
|
|
411
|
+
requested_max_credits: input.maxCredits,
|
|
412
|
+
default_max_credits: defaultMax,
|
|
413
|
+
platform_max_credits: AGENT_RUN_APPROVED_CEILING_MAX_CREDITS,
|
|
414
|
+
},
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
if (input.maxCredits <= defaultMax)
|
|
418
|
+
return { aboveDefault: false };
|
|
419
|
+
if (input.approveAboveDefault)
|
|
420
|
+
return { aboveDefault: true };
|
|
421
|
+
throw new OxygenError("approval_required", `max_credits ${input.maxCredits} is above your default Agent run ceiling of ${defaultMax} credits. Approve the higher ceiling for ${subject} with approve_above_default=true, or lower max_credits to ${defaultMax} or less.`, {
|
|
422
|
+
exitCode: 7,
|
|
423
|
+
details: {
|
|
424
|
+
approval_kind: AGENT_CEILING_ABOVE_DEFAULT_APPROVAL_KIND,
|
|
425
|
+
scope: input.scope,
|
|
426
|
+
requested_max_credits: input.maxCredits,
|
|
427
|
+
default_max_credits: defaultMax,
|
|
428
|
+
default_source: input.defaults.source,
|
|
429
|
+
platform_max_credits: AGENT_RUN_APPROVED_CEILING_MAX_CREDITS,
|
|
430
|
+
next_step: `Re-send with approve_above_default=true (CLI: --approve-above-default) to approve up to ${input.maxCredits} credits for ${subject}.`,
|
|
431
|
+
},
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* The ceilings one run is held to, for the worker and every budget check that
|
|
436
|
+
* reads a run's authority. A run with a recorded above-default approval is
|
|
437
|
+
* bounded by its own approved `max_credits` (applied by the caller as a user
|
|
438
|
+
* limit) under the platform backstop. Every other run is bounded by the default,
|
|
439
|
+
* so a ceiling approved without the second approval — including every one
|
|
440
|
+
* approved before this rule — still binds at the default exactly as before.
|
|
441
|
+
*/
|
|
442
|
+
export function agentRunEnforcedCeilings(input) {
|
|
443
|
+
if (!input.aboveDefaultApproved) {
|
|
444
|
+
return {
|
|
445
|
+
maxTotalCredits: input.defaults.maxTotalCredits,
|
|
446
|
+
maxInferenceCredits: input.defaults.maxInferenceCredits,
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
return {
|
|
450
|
+
maxTotalCredits: AGENT_RUN_APPROVED_CEILING_MAX_CREDITS,
|
|
451
|
+
maxInferenceCredits: AGENT_RUN_APPROVED_CEILING_MAX_CREDITS,
|
|
452
|
+
};
|
|
453
|
+
}
|