@oxygen-agent/cli 1.1010.721 → 1.1010.905

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/README.md +1 -1
  2. package/dist/auto-update.d.ts +129 -0
  3. package/dist/auto-update.js +392 -0
  4. package/dist/command-manifest.js +14 -0
  5. package/dist/credentials.d.ts +2 -0
  6. package/dist/credentials.js +6 -3
  7. package/dist/functions-commands.js +1 -1
  8. package/dist/http-client.js +28 -4
  9. package/dist/index.js +583 -145
  10. package/dist/run-wait.d.ts +3 -1
  11. package/dist/run-wait.js +19 -5
  12. package/dist/streamed-file-import.d.ts +58 -0
  13. package/dist/streamed-file-import.js +115 -0
  14. package/dist/update.d.ts +29 -0
  15. package/dist/update.js +62 -16
  16. package/dist/workflow-plan-limit-notices.d.ts +8 -0
  17. package/dist/workflow-plan-limit-notices.js +28 -0
  18. package/node_modules/@oxygen/cli-ugc/dist/commands.js +3 -3
  19. package/node_modules/@oxygen/shared/dist/billing-anchors.d.ts +17 -0
  20. package/node_modules/@oxygen/shared/dist/billing-anchors.js +27 -0
  21. package/node_modules/@oxygen/shared/dist/billing.d.ts +191 -35
  22. package/node_modules/@oxygen/shared/dist/billing.js +333 -42
  23. package/node_modules/@oxygen/shared/dist/capability-discovery.js +55 -5
  24. package/node_modules/@oxygen/shared/dist/copilot-skills.generated.d.ts +2 -2
  25. package/node_modules/@oxygen/shared/dist/copilot-skills.generated.js +2 -2
  26. package/node_modules/@oxygen/shared/dist/cost-estimate-view.d.ts +50 -0
  27. package/node_modules/@oxygen/shared/dist/cost-estimate-view.js +90 -0
  28. package/node_modules/@oxygen/shared/dist/cost-estimate.d.ts +167 -0
  29. package/node_modules/@oxygen/shared/dist/cost-estimate.js +361 -0
  30. package/node_modules/@oxygen/shared/dist/credit-gate.d.ts +26 -0
  31. package/node_modules/@oxygen/shared/dist/credit-gate.js +65 -0
  32. package/node_modules/@oxygen/shared/dist/email-deliverability-policy.d.ts +51 -0
  33. package/node_modules/@oxygen/shared/dist/email-deliverability-policy.js +101 -0
  34. package/node_modules/@oxygen/shared/dist/email-hard-bounce.d.ts +3 -1
  35. package/node_modules/@oxygen/shared/dist/email-hard-bounce.js +3 -3
  36. package/node_modules/@oxygen/shared/dist/error-redaction.d.ts +1 -1
  37. package/node_modules/@oxygen/shared/dist/error-redaction.js +1 -1
  38. package/node_modules/@oxygen/shared/dist/feature-gates.d.ts +4 -0
  39. package/node_modules/@oxygen/shared/dist/feature-gates.js +5 -0
  40. package/node_modules/@oxygen/shared/dist/file-import.d.ts +13 -1
  41. package/node_modules/@oxygen/shared/dist/file-import.js +33 -6
  42. package/node_modules/@oxygen/shared/dist/hosted-ai.d.ts +73 -3
  43. package/node_modules/@oxygen/shared/dist/hosted-ai.js +246 -24
  44. package/node_modules/@oxygen/shared/dist/import-limits.d.ts +25 -1
  45. package/node_modules/@oxygen/shared/dist/import-limits.js +35 -2
  46. package/node_modules/@oxygen/shared/dist/index.d.ts +2 -22
  47. package/node_modules/@oxygen/shared/dist/index.js +2 -42
  48. package/node_modules/@oxygen/shared/dist/object-storage.d.ts +9 -0
  49. package/node_modules/@oxygen/shared/dist/object-storage.js +17 -0
  50. package/node_modules/@oxygen/shared/dist/operational-telemetry.d.ts +41 -0
  51. package/node_modules/@oxygen/shared/dist/operational-telemetry.js +55 -0
  52. package/node_modules/@oxygen/shared/dist/plan-band.d.ts +117 -1
  53. package/node_modules/@oxygen/shared/dist/plan-band.js +175 -10
  54. package/node_modules/@oxygen/shared/dist/plan-capabilities.d.ts +77 -7
  55. package/node_modules/@oxygen/shared/dist/plan-capabilities.js +87 -7
  56. package/node_modules/@oxygen/shared/dist/plan-limits-view.d.ts +219 -0
  57. package/node_modules/@oxygen/shared/dist/plan-limits-view.js +330 -0
  58. package/node_modules/@oxygen/shared/dist/plan-limits.d.ts +204 -6
  59. package/node_modules/@oxygen/shared/dist/plan-limits.js +197 -15
  60. package/node_modules/@oxygen/shared/dist/pricing-sheet.d.ts +80 -36
  61. package/node_modules/@oxygen/shared/dist/pricing-sheet.js +80 -31
  62. package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.d.ts +38 -20
  63. package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.js +47 -34
  64. package/node_modules/@oxygen/shared/dist/provider-http-error.d.ts +10 -0
  65. package/node_modules/@oxygen/shared/dist/provider-http-error.js +27 -0
  66. package/node_modules/@oxygen/shared/dist/repricing.d.ts +127 -0
  67. package/node_modules/@oxygen/shared/dist/repricing.js +407 -6
  68. package/node_modules/@oxygen/shared/dist/semver.d.ts +21 -0
  69. package/node_modules/@oxygen/shared/dist/semver.js +41 -0
  70. package/node_modules/@oxygen/shared/dist/sending-limits.d.ts +5 -7
  71. package/node_modules/@oxygen/shared/dist/sending-limits.js +10 -16
  72. package/node_modules/@oxygen/shared/dist/sending-seats.d.ts +18 -15
  73. package/node_modules/@oxygen/shared/dist/sending-seats.js +22 -17
  74. package/node_modules/@oxygen/shared/dist/spend-safety.d.ts +57 -8
  75. package/node_modules/@oxygen/shared/dist/spend-safety.js +64 -11
  76. package/node_modules/@oxygen/shared/dist/stripe-price-catalog.d.ts +15 -7
  77. package/node_modules/@oxygen/shared/dist/stripe-price-catalog.js +18 -5
  78. package/node_modules/@oxygen/shared/dist/table-capacity.d.ts +34 -5
  79. package/node_modules/@oxygen/shared/dist/table-capacity.js +25 -8
  80. package/node_modules/@oxygen/shared/dist/telemetry-export-observer.d.ts +6 -0
  81. package/node_modules/@oxygen/shared/dist/telemetry-export-observer.js +13 -5
  82. package/node_modules/@oxygen/shared/dist/telemetry-resource.d.ts +40 -0
  83. package/node_modules/@oxygen/shared/dist/telemetry-resource.js +35 -0
  84. package/node_modules/@oxygen/shared/dist/telemetry.js +5 -0
  85. package/node_modules/@oxygen/shared/dist/ugc.d.ts +15 -0
  86. package/node_modules/@oxygen/shared/dist/ugc.js +29 -0
  87. package/node_modules/@oxygen/shared/dist/version.d.ts +1 -3
  88. package/node_modules/@oxygen/shared/dist/version.generated.d.ts +1 -1
  89. package/node_modules/@oxygen/shared/dist/version.generated.js +1 -1
  90. package/node_modules/@oxygen/shared/dist/version.js +14 -27
  91. package/node_modules/@oxygen/shared/dist/workspace-file-storage.d.ts +5 -0
  92. package/node_modules/@oxygen/shared/dist/workspace-file-storage.js +5 -0
  93. package/node_modules/@oxygen/workflows/dist/graph/manifest-schema.d.ts +3 -3
  94. package/node_modules/@oxygen/workflows/dist/graph/types.d.ts +15 -1
  95. package/node_modules/@oxygen/workflows/dist/graph/types.js +15 -1
  96. package/node_modules/@oxygen/workflows/dist/index.d.ts +45 -0
  97. package/node_modules/@oxygen/workflows/dist/index.js +152 -2
  98. package/node_modules/@oxygen/workflows/dist/usage-estimate.d.ts +10 -1
  99. package/node_modules/@oxygen/workflows/dist/usage-estimate.js +33 -29
  100. package/package.json +1 -1
@@ -1,3 +1,4 @@
1
+ import { type RepricingOptions } from "./repricing.js";
1
2
  /**
2
3
  * The five enforcement rungs of the per-plan limits ladder. Every PlanTier maps
3
4
  * onto exactly one rung: "free" doubles as the unentitled fallback (no live
@@ -8,6 +9,12 @@
8
9
  * Credits are the billing meter; these limits are abuse/runaway guards. They
9
10
  * are sized so legitimate interactive use never hits them and scale with plan
10
11
  * price so worst-case burn stays proportional to what the org pays.
12
+ *
13
+ * Org-scope unit budgets (`orgUnits`) bound row-weighted bulk volume only: a
14
+ * call whose normalized amount is 1 is counted against the budget but never
15
+ * refused by it, so single-item work is bounded by `requestsPerMinute` and the
16
+ * global `cli` buckets. A budget accumulated under a higher rung keeps refusing
17
+ * bulk calls until its window resets after a downgrade; there is no rebase.
11
18
  */
12
19
  export type LimitsTier = "free" | "starter" | "pro" | "team" | "scale";
13
20
  export declare const LIMITS_TIER_ORDER: readonly LimitsTier[];
@@ -16,7 +23,10 @@ export type OrgWindowLimit = {
16
23
  limit: number;
17
24
  windowSeconds: number;
18
25
  };
19
- /** Request bucket (per api-key / web-user scope) + org-scope unit budget. */
26
+ /**
27
+ * Request bucket (per api-key / web-user scope) + org-scope unit budget. The
28
+ * unit budget never refuses a 1-unit call (rate-limits.ts `orgUnitBudget`).
29
+ */
20
30
  export type UnitPolicyLimits = {
21
31
  requestsPerMinute: number;
22
32
  orgUnits: OrgWindowLimit;
@@ -53,6 +63,11 @@ export type PlanLimits = {
53
63
  maxDirectCsvBodyBytes: number;
54
64
  };
55
65
  agents: {
66
+ /**
67
+ * The pre-2026-09-repricing default Agent run ceilings. Read the value in
68
+ * force with `resolvePlanAgentCreditLimits`: team and scale drop at the
69
+ * repricing instant.
70
+ */
56
71
  maxTotalCredits: number;
57
72
  maxInferenceCredits: number;
58
73
  maxUpstreamCostUsd: number;
@@ -69,6 +84,15 @@ export type PlanLimits = {
69
84
  tableActions: {
70
85
  maxActionsPerRun: number;
71
86
  };
87
+ /**
88
+ * `cronMinIntervalMinutes` is the fastest cron cadence a workflow or Agent
89
+ * trigger may be scheduled at (repricing 2026-09, decision L4.2). It is the
90
+ * ratified value; read the value in force with `resolveCronMinIntervalMinutes`,
91
+ * because the free rung's cut waits for the repricing switch.
92
+ */
93
+ workflows: {
94
+ cronMinIntervalMinutes: number;
95
+ };
72
96
  };
73
97
  /**
74
98
  * The per-rung Table import ceiling this matrix reports: today's envelope. The
@@ -88,6 +112,66 @@ export { VERCEL_REQUEST_BODY_LIMIT_BYTES } from "./import-limits.js";
88
112
  * in the CLI alone — an older server would still 413 at the old number.
89
113
  */
90
114
  export declare const MAX_CLI_JSON_BODY_BYTES = 2000000;
115
+ /**
116
+ * Derived plan-scaled limits (repricing 2026-09 decision L6, spec § 4.7):
117
+ * import file size, columns per action run or standing auto-run and the signals
118
+ * feed. Each only rises against the pre-repricing ladder, so no paid customer
119
+ * loses a limit and nothing here waits for the effective-date switch. Rows per
120
+ * write request stay 5,000 free / 50,000 paid: the 2 MB request body
121
+ * (`MAX_CLI_JSON_BODY_BYTES`) binds first.
122
+ *
123
+ * `importFileBytes` is ratified (decision F.8, Philipp 2026-09-27): free 500 MiB,
124
+ * $49–$99 1 GiB, $199–$499 2 GiB, $999–$1,999 5 GiB. It is live on every plan
125
+ * and no longer waits for the per-Table capacity test: rows per Table still cap
126
+ * what one import can load, so a bigger file never buys more rows. 5 GiB is also
127
+ * the largest single PUT an S3-compatible store accepts
128
+ * (`IMPORT_OBJECT_SINGLE_PUT_MAX_BYTES`), which is the one upload a staged
129
+ * import makes.
130
+ */
131
+ export declare const DERIVED_PLAN_LIMIT_TARGETS: {
132
+ readonly free: {
133
+ readonly importFileBytes: number;
134
+ readonly maxActionsPerRun: 10;
135
+ readonly signals: {
136
+ readonly maxEvents: 100;
137
+ readonly maxWindowDays: 30;
138
+ };
139
+ };
140
+ readonly starter: {
141
+ readonly importFileBytes: number;
142
+ readonly maxActionsPerRun: 25;
143
+ readonly signals: {
144
+ readonly maxEvents: 200;
145
+ readonly maxWindowDays: 90;
146
+ };
147
+ };
148
+ readonly pro: {
149
+ readonly importFileBytes: number;
150
+ readonly maxActionsPerRun: 25;
151
+ readonly signals: {
152
+ readonly maxEvents: 500;
153
+ readonly maxWindowDays: 180;
154
+ };
155
+ };
156
+ readonly team: {
157
+ readonly importFileBytes: number;
158
+ readonly maxActionsPerRun: 50;
159
+ readonly signals: {
160
+ readonly maxEvents: 1000;
161
+ readonly maxWindowDays: 365;
162
+ };
163
+ };
164
+ readonly scale: {
165
+ readonly importFileBytes: number;
166
+ readonly maxActionsPerRun: 50;
167
+ readonly signals: {
168
+ readonly maxEvents: 1000;
169
+ readonly maxWindowDays: 365;
170
+ };
171
+ };
172
+ };
173
+ /** The import file size a rung enforces (see `DERIVED_PLAN_LIMIT_TARGETS`). */
174
+ export declare function importMaxFileBytesForTier(tier: LimitsTier): number;
91
175
  /**
92
176
  * The per-rung limit matrix. `ai_live` deliberately equals `tool_live` at every
93
177
  * rung — one "live actions" mental model; the per-call cost asymmetry between
@@ -212,6 +296,9 @@ export declare const PLAN_LIMITS: {
212
296
  readonly tableActions: {
213
297
  readonly maxActionsPerRun: 10;
214
298
  };
299
+ readonly workflows: {
300
+ readonly cronMinIntervalMinutes: 15;
301
+ };
215
302
  };
216
303
  readonly starter: {
217
304
  readonly cli: {
@@ -322,6 +409,9 @@ export declare const PLAN_LIMITS: {
322
409
  readonly tableActions: {
323
410
  readonly maxActionsPerRun: 25;
324
411
  };
412
+ readonly workflows: {
413
+ readonly cronMinIntervalMinutes: 1;
414
+ };
325
415
  };
326
416
  readonly pro: {
327
417
  readonly cli: {
@@ -426,12 +516,15 @@ export declare const PLAN_LIMITS: {
426
516
  readonly defaultPerTurnCreditCeiling: 250;
427
517
  };
428
518
  readonly signals: {
429
- readonly maxEvents: 200;
519
+ readonly maxEvents: 500;
430
520
  readonly maxWindowDays: 180;
431
521
  };
432
522
  readonly tableActions: {
433
523
  readonly maxActionsPerRun: 25;
434
524
  };
525
+ readonly workflows: {
526
+ readonly cronMinIntervalMinutes: 1;
527
+ };
435
528
  };
436
529
  readonly team: {
437
530
  readonly cli: {
@@ -536,11 +629,14 @@ export declare const PLAN_LIMITS: {
536
629
  readonly defaultPerTurnCreditCeiling: 500;
537
630
  };
538
631
  readonly signals: {
539
- readonly maxEvents: 200;
632
+ readonly maxEvents: 1000;
540
633
  readonly maxWindowDays: 365;
541
634
  };
542
635
  readonly tableActions: {
543
- readonly maxActionsPerRun: 25;
636
+ readonly maxActionsPerRun: 50;
637
+ };
638
+ readonly workflows: {
639
+ readonly cronMinIntervalMinutes: 1;
544
640
  };
545
641
  };
546
642
  readonly scale: {
@@ -646,14 +742,30 @@ export declare const PLAN_LIMITS: {
646
742
  readonly defaultPerTurnCreditCeiling: 1000;
647
743
  };
648
744
  readonly signals: {
649
- readonly maxEvents: 200;
745
+ readonly maxEvents: 1000;
650
746
  readonly maxWindowDays: 365;
651
747
  };
652
748
  readonly tableActions: {
653
- readonly maxActionsPerRun: 25;
749
+ readonly maxActionsPerRun: 50;
750
+ };
751
+ readonly workflows: {
752
+ readonly cronMinIntervalMinutes: 1;
654
753
  };
655
754
  };
656
755
  };
756
+ /**
757
+ * The fastest cron cadence, in minutes, a rung may schedule right now. The free
758
+ * rung's cut from every minute to every 15 (decision L4.2) is a tightening, so
759
+ * it resolves through the 2026-09 repricing switch: every minute until the
760
+ * effective instant, `PLAN_LIMITS.free.workflows.cronMinIntervalMinutes` from it.
761
+ */
762
+ export declare function resolveCronMinIntervalMinutes(tier: LimitsTier, options?: RepricingOptions): number;
763
+ /**
764
+ * The largest minimum cron interval any rung enforces right now. A schedule
765
+ * whose fires are never closer than this fits every plan, so a caller can skip
766
+ * the plan lookup for it.
767
+ */
768
+ export declare function highestCronMinIntervalMinutes(options?: RepricingOptions): number;
657
769
  /**
658
770
  * Map a raw plan-tier string (subscription `tier`, metadata tier, legacy plan
659
771
  * key like `growth_250`, or an Oxygen rung key like `oxygen_499`) onto its
@@ -662,3 +774,89 @@ export declare const PLAN_LIMITS: {
662
774
  */
663
775
  export declare function limitsTierForPlanTier(tier: string | null | undefined): LimitsTier;
664
776
  export declare function planLimitsForTier(tier: LimitsTier): PlanLimits;
777
+ /** The default credit ceilings of one Agent run on a rung. */
778
+ export type PlanAgentCreditLimits = {
779
+ maxTotalCredits: number;
780
+ maxInferenceCredits: number;
781
+ };
782
+ /**
783
+ * The default credit ceilings of one Agent run in force on a rung right now.
784
+ *
785
+ * `PLAN_LIMITS[tier].agents` holds the values each rung read before the 2026-09
786
+ * repricing. The team and scale rungs drop to 2,500 total / 1,250 inference
787
+ * (decision L5.4, inference half PROPOSED P-56), and a cut waits for the
788
+ * effective-date switch (`agents.<tier>.*` in `REPRICING_2026_09_SCHEDULE`).
789
+ * Every Agent budget check reads this, never the table, so the cut lands
790
+ * everywhere at the same instant.
791
+ */
792
+ export declare function resolvePlanAgentCreditLimits(tier: LimitsTier, options?: RepricingOptions): PlanAgentCreditLimits;
793
+ /**
794
+ * The platform's hard backstop on one Agent run's approved credit ceiling
795
+ * (decision F.4, ratified 2026-09-27).
796
+ *
797
+ * The plan's Agent run ceiling is a DEFAULT: a run or trigger whose approved
798
+ * `max_credits` fits under it needs only the usual approval. Going above it
799
+ * takes a second, explicit per-run (or per-trigger) approval, and even that
800
+ * approval stops here. 25,000 credits ($250) is the largest standing
801
+ * per-delivery default any plan has carried (the pre-2026-09 scale trigger
802
+ * default), so an approved Agent run never outspends one delivery of the
803
+ * largest plan's own automation. The other backstops stay in force whatever is
804
+ * approved: the plan's upstream-USD clamp, the iteration, tool-call and
805
+ * active-minute bounds, the balance check on every reservation and the org
806
+ * daily spend guard.
807
+ */
808
+ export declare const AGENT_RUN_APPROVED_CEILING_MAX_CREDITS = 25000;
809
+ /** Where an org's default Agent run ceiling came from. */
810
+ export type AgentRunDefaultCeilingSource = "plan" | "enterprise_contract";
811
+ /** The Agent run ceilings an org's runs are held to without a further approval. */
812
+ export type AgentRunDefaultCeilings = PlanAgentCreditLimits & {
813
+ source: AgentRunDefaultCeilingSource;
814
+ };
815
+ /**
816
+ * The default Agent run ceilings for an org: its plan rung's, unless an
817
+ * enterprise contract names its own default (decision F.4). The contract value
818
+ * replaces the rung's total and model calls take half of it, as on every paid
819
+ * rung. A contract value is clamped to the platform backstop; an unusable one
820
+ * (absent, non-numeric, not positive) is ignored.
821
+ */
822
+ export declare function resolveAgentRunDefaultCeilings(input: {
823
+ tier: LimitsTier;
824
+ enterpriseDefaultCredits?: number | null;
825
+ options?: RepricingOptions;
826
+ }): AgentRunDefaultCeilings;
827
+ /** `details.approval_kind` on the refusal below, so every surface can render its own approval card. */
828
+ export declare const AGENT_CEILING_ABOVE_DEFAULT_APPROVAL_KIND = "agent_ceiling_above_default";
829
+ /**
830
+ * Check one run's (or one trigger's per-delivery) approved ceiling against the
831
+ * org's default before anything is created.
832
+ *
833
+ * - At or under the default: nothing more to approve.
834
+ * - Above it: refused with `approval_required` unless the caller also passed the
835
+ * explicit above-default approval. The details carry the numbers and the flag
836
+ * each surface re-sends, so the refusal is the approval card.
837
+ * - Above the platform backstop: refused outright; no approval raises it.
838
+ *
839
+ * Returns whether the approved ceiling is above the default; the caller persists
840
+ * that as the run's or trigger's above-default approval.
841
+ */
842
+ export declare function assertAgentRunCeilingApproval(input: {
843
+ maxCredits: number;
844
+ defaults: AgentRunDefaultCeilings;
845
+ approveAboveDefault: boolean;
846
+ /** "run" for one run, "trigger" for a trigger's per-delivery ceiling. */
847
+ scope: "run" | "trigger";
848
+ }): {
849
+ aboveDefault: boolean;
850
+ };
851
+ /**
852
+ * The ceilings one run is held to, for the worker and every budget check that
853
+ * reads a run's authority. A run with a recorded above-default approval is
854
+ * bounded by its own approved `max_credits` (applied by the caller as a user
855
+ * limit) under the platform backstop. Every other run is bounded by the default,
856
+ * so a ceiling approved without the second approval — including every one
857
+ * approved before this rule — still binds at the default exactly as before.
858
+ */
859
+ export declare function agentRunEnforcedCeilings(input: {
860
+ defaults: PlanAgentCreditLimits;
861
+ aboveDefaultApproved: boolean;
862
+ }): PlanAgentCreditLimits;
@@ -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",
@@ -31,6 +33,33 @@ export { VERCEL_REQUEST_BODY_LIMIT_BYTES } from "./import-limits.js";
31
33
  * in the CLI alone — an older server would still 413 at the old number.
32
34
  */
33
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
+ }
34
63
  /**
35
64
  * The per-rung limit matrix. `ai_live` deliberately equals `tool_live` at every
36
65
  * rung — one "live actions" mental model; the per-call cost asymmetry between
@@ -66,7 +95,7 @@ export const PLAN_LIMITS = {
66
95
  },
67
96
  import: {
68
97
  maxRowsPerFile: TABLE_IMPORT_ROW_LIMIT,
69
- maxFileBytes: 100 * MIB,
98
+ maxFileBytes: importMaxFileBytesForTier("free"),
70
99
  maxDirectCsvBodyBytes: 10_000_000,
71
100
  },
72
101
  // Sized against the free tier's actual funding (OXP-10.2.4, 2026-08-17): one
@@ -90,8 +119,13 @@ export const PLAN_LIMITS = {
90
119
  // to complete one real Copilot session on the 1,000-credit signup grant
91
120
  // alone. Lowering this would re-create a dead-on-arrival Copilot.
92
121
  copilot: { maxSessionBudgetCredits: 500, defaultPerTurnCreditCeiling: 150 },
93
- signals: { maxEvents: 100, maxWindowDays: 30 },
94
- tableActions: { maxActionsPerRun: 10 },
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 },
95
129
  },
96
130
  starter: {
97
131
  cli: {
@@ -113,7 +147,7 @@ export const PLAN_LIMITS = {
113
147
  },
114
148
  import: {
115
149
  maxRowsPerFile: TABLE_IMPORT_ROW_LIMIT,
116
- maxFileBytes: 250 * MIB,
150
+ maxFileBytes: importMaxFileBytesForTier("starter"),
117
151
  maxDirectCsvBodyBytes: 10_000_000,
118
152
  },
119
153
  agents: {
@@ -123,8 +157,9 @@ export const PLAN_LIMITS = {
123
157
  maxUpstreamCostUsdByok: 250,
124
158
  },
125
159
  copilot: { maxSessionBudgetCredits: 500, defaultPerTurnCreditCeiling: 150 },
126
- signals: { maxEvents: 200, maxWindowDays: 90 },
127
- tableActions: { maxActionsPerRun: 25 },
160
+ signals: DERIVED_PLAN_LIMIT_TARGETS.starter.signals,
161
+ tableActions: { maxActionsPerRun: DERIVED_PLAN_LIMIT_TARGETS.starter.maxActionsPerRun },
162
+ workflows: { cronMinIntervalMinutes: 1 },
128
163
  },
129
164
  pro: {
130
165
  cli: {
@@ -146,7 +181,7 @@ export const PLAN_LIMITS = {
146
181
  },
147
182
  import: {
148
183
  maxRowsPerFile: TABLE_IMPORT_ROW_LIMIT,
149
- maxFileBytes: 250 * MIB,
184
+ maxFileBytes: importMaxFileBytesForTier("pro"),
150
185
  maxDirectCsvBodyBytes: 10_000_000,
151
186
  },
152
187
  agents: {
@@ -156,8 +191,9 @@ export const PLAN_LIMITS = {
156
191
  maxUpstreamCostUsdByok: 250,
157
192
  },
158
193
  copilot: { maxSessionBudgetCredits: 1_000, defaultPerTurnCreditCeiling: 250 },
159
- signals: { maxEvents: 200, maxWindowDays: 180 },
160
- tableActions: { maxActionsPerRun: 25 },
194
+ signals: DERIVED_PLAN_LIMIT_TARGETS.pro.signals,
195
+ tableActions: { maxActionsPerRun: DERIVED_PLAN_LIMIT_TARGETS.pro.maxActionsPerRun },
196
+ workflows: { cronMinIntervalMinutes: 1 },
161
197
  },
162
198
  team: {
163
199
  cli: {
@@ -179,7 +215,7 @@ export const PLAN_LIMITS = {
179
215
  },
180
216
  import: {
181
217
  maxRowsPerFile: TABLE_IMPORT_ROW_LIMIT,
182
- maxFileBytes: 250 * MIB,
218
+ maxFileBytes: importMaxFileBytesForTier("team"),
183
219
  maxDirectCsvBodyBytes: 10_000_000,
184
220
  },
185
221
  agents: {
@@ -189,8 +225,9 @@ export const PLAN_LIMITS = {
189
225
  maxUpstreamCostUsdByok: 250,
190
226
  },
191
227
  copilot: { maxSessionBudgetCredits: 2_000, defaultPerTurnCreditCeiling: 500 },
192
- signals: { maxEvents: 200, maxWindowDays: 365 },
193
- tableActions: { maxActionsPerRun: 25 },
228
+ signals: DERIVED_PLAN_LIMIT_TARGETS.team.signals,
229
+ tableActions: { maxActionsPerRun: DERIVED_PLAN_LIMIT_TARGETS.team.maxActionsPerRun },
230
+ workflows: { cronMinIntervalMinutes: 1 },
194
231
  },
195
232
  scale: {
196
233
  cli: {
@@ -212,7 +249,7 @@ export const PLAN_LIMITS = {
212
249
  },
213
250
  import: {
214
251
  maxRowsPerFile: TABLE_IMPORT_ROW_LIMIT,
215
- maxFileBytes: 250 * MIB,
252
+ maxFileBytes: importMaxFileBytesForTier("scale"),
216
253
  maxDirectCsvBodyBytes: 10_000_000,
217
254
  },
218
255
  agents: {
@@ -222,10 +259,32 @@ export const PLAN_LIMITS = {
222
259
  maxUpstreamCostUsdByok: 250,
223
260
  },
224
261
  copilot: { maxSessionBudgetCredits: 5_000, defaultPerTurnCreditCeiling: 1_000 },
225
- signals: { maxEvents: 200, maxWindowDays: 365 },
226
- tableActions: { maxActionsPerRun: 25 },
262
+ signals: DERIVED_PLAN_LIMIT_TARGETS.scale.signals,
263
+ tableActions: { maxActionsPerRun: DERIVED_PLAN_LIMIT_TARGETS.scale.maxActionsPerRun },
264
+ workflows: { cronMinIntervalMinutes: 1 },
227
265
  },
228
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
+ }
229
288
  /**
230
289
  * Which enforcement rung a rung of the six-rung Oxygen plan lands on.
231
290
  *
@@ -269,3 +328,126 @@ export function limitsTierForPlanTier(tier) {
269
328
  export function planLimitsForTier(tier) {
270
329
  return PLAN_LIMITS[tier];
271
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
+ }