@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
@@ -10,12 +10,31 @@
10
10
  * constants live here alone, and both of those modules re-export them, keeping
11
11
  * one definition rather than a copy that drifts.
12
12
  */
13
+ const MIB = 1024 * 1024;
13
14
  /**
14
15
  * Formats that must be held in memory to parse at all — a JSON array has no
15
16
  * record boundary to stream from, and XLSX is a zip container. CSV and JSONL
16
- * stream, so only these two are capped this low.
17
+ * stream from object storage up to the plan's file limit, so only these two
18
+ * carry this lower ceiling (raised from 10 MiB by decision F.8, 2026-09-27).
19
+ *
20
+ * It also bounds every path that holds a whole file in one request: the inline
21
+ * multipart import used when object storage is not configured.
22
+ */
23
+ export const MAX_BUFFERED_IMPORT_PARSE_BYTES = 100 * MIB;
24
+ /**
25
+ * A link import (`--url`, `oxygen_tables_import_url`, the wizard's link card)
26
+ * is downloaded and parsed inside the web request, so it keeps its own small
27
+ * ceiling. It is not a plan limit: a bigger file is downloaded and imported
28
+ * with `tables import --file`, which stages it in object storage.
29
+ */
30
+ export const MAX_URL_IMPORT_BYTES = 10 * MIB;
31
+ /**
32
+ * The largest object one presigned PUT may carry on an S3-compatible store (S3
33
+ * and Ceph-based stores such as Hetzner Object Storage cap a single PUT at
34
+ * 5 GiB). A staged import is one PUT, so no plan's import file limit may
35
+ * exceed it.
17
36
  */
18
- export const MAX_BUFFERED_IMPORT_PARSE_BYTES = 10 * 1024 * 1024;
37
+ export const IMPORT_OBJECT_SINGLE_PUT_MAX_BYTES = 5 * 1024 * MIB;
19
38
  /**
20
39
  * The platform's request-body ceiling — infrastructure, not a plan limit.
21
40
  *
@@ -28,3 +47,17 @@ export const VERCEL_REQUEST_BODY_LIMIT_BYTES = 4 * 1024 * 1024;
28
47
  export function isBufferedOnlyImportFormat(format) {
29
48
  return format === "json" || format === "xlsx";
30
49
  }
50
+ /**
51
+ * The refusal every surface gives a JSON or XLSX file over the buffered
52
+ * ceiling. It names the ceiling and the two formats that take a larger file,
53
+ * so the reader knows the fix is a format change, not a plan change.
54
+ */
55
+ export function bufferedImportTooLargeMessage(format, fileBytes) {
56
+ const label = format.toUpperCase();
57
+ const size = typeof fileBytes === "number" ? ` This one is ${formatImportMib(fileBytes)}.` : "";
58
+ return `${label} files are read whole, so they are capped at ${formatImportMib(MAX_BUFFERED_IMPORT_PARSE_BYTES)}.${size} Save it as CSV or JSONL, which import up to your plan's file-size limit.`;
59
+ }
60
+ function formatImportMib(bytes) {
61
+ const mib = bytes / MIB;
62
+ return `${mib >= 10 ? Math.round(mib).toLocaleString("en-US") : mib.toFixed(1)} MiB`;
63
+ }
@@ -1,4 +1,4 @@
1
- export { MANAGED_INBOX_MINIMUM_CLI_VERSION, OXYGEN_MINIMUM_CLI_VERSION, OXYGEN_VERSION, SUPPORT_AGENT_REPLY_MINIMUM_CLI_VERSION, } from "./version.js";
1
+ export { OXYGEN_MINIMUM_CLI_VERSION, OXYGEN_VERSION, } from "./version.js";
2
2
  export { WORKFLOW_TRIGGER_AUTO_PAUSE_METADATA_KEYS, clearWorkflowTriggerAutoPauseMetadata, } from "./workflow-trigger-metadata.js";
3
3
  export { WORKFLOW_STATUS_CHANGE_METADATA_KEY, type WorkflowStatusChange, type WorkflowStatusChangeActor, type WorkflowStatusChangeSource, describeWorkflowStatusChange, formatWorkflowStatusChangeTimestamp, parseWorkflowStatusChange, readWorkflowStatusChange, } from "./workflow-status-change.js";
4
4
  export * from "./billing.js";
@@ -111,27 +111,7 @@ export * from "./workspace-event-catalog.js";
111
111
  export * from "./notetaker-events.js";
112
112
  export * from "./workspace-agents.js";
113
113
  export declare const MAX_ROW_LOOP_WRITE_ROWS = 500;
114
- export type SemanticVersion = {
115
- major: number;
116
- minor: number;
117
- patch: number;
118
- };
119
- /**
120
- * Parse a three-segment semantic version (e.g. `1.142.17`). Pre-release and
121
- * build metadata suffixes (`-rc.1`, `+build`) are tolerated but ignored.
122
- * Returns `null` when the input is not a parseable `major.minor.patch` string.
123
- */
124
- export declare function parseSemver(version: string): SemanticVersion | null;
125
- /**
126
- * Compare two semantic versions. Returns -1 when `a < b`, 1 when `a > b`, and
127
- * 0 when they are equal. Unparseable inputs compare as equal (0) so callers
128
- * fail open rather than misordering garbage.
129
- */
130
- export declare function compareSemver(a: string, b: string): -1 | 0 | 1;
131
- /** True when `a` is a strictly greater semantic version than `b`. */
132
- export declare function isVersionGreater(a: string, b: string): boolean;
133
- /** True when `a` is a strictly lesser semantic version than `b`. */
134
- export declare function isVersionLess(a: string, b: string): boolean;
114
+ export * from "./semver.js";
135
115
  export * from "./ugc.js";
136
116
  export * from "./ugc-amplification-identity.js";
137
117
  export * from "./knowledge-repository.js";
@@ -1,4 +1,4 @@
1
- export { MANAGED_INBOX_MINIMUM_CLI_VERSION, OXYGEN_MINIMUM_CLI_VERSION, OXYGEN_VERSION, SUPPORT_AGENT_REPLY_MINIMUM_CLI_VERSION, } from "./version.js";
1
+ export { OXYGEN_MINIMUM_CLI_VERSION, OXYGEN_VERSION, } from "./version.js";
2
2
  export { WORKFLOW_TRIGGER_AUTO_PAUSE_METADATA_KEYS, clearWorkflowTriggerAutoPauseMetadata, } from "./workflow-trigger-metadata.js";
3
3
  export { WORKFLOW_STATUS_CHANGE_METADATA_KEY, describeWorkflowStatusChange, formatWorkflowStatusChangeTimestamp, parseWorkflowStatusChange, readWorkflowStatusChange, } from "./workflow-status-change.js";
4
4
  export * from "./billing.js";
@@ -127,47 +127,7 @@ export * from "./workspace-agents.js";
127
127
  // the COPY-based bulk engine. Tenant-db enforces this and the CLI/API row caps
128
128
  // reference it so they never advertise a batch the row-loop will reject.
129
129
  export const MAX_ROW_LOOP_WRITE_ROWS = 500;
130
- /**
131
- * Parse a three-segment semantic version (e.g. `1.142.17`). Pre-release and
132
- * build metadata suffixes (`-rc.1`, `+build`) are tolerated but ignored.
133
- * Returns `null` when the input is not a parseable `major.minor.patch` string.
134
- */
135
- export function parseSemver(version) {
136
- const match = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(version);
137
- if (!match)
138
- return null;
139
- return {
140
- major: Number(match[1]),
141
- minor: Number(match[2]),
142
- patch: Number(match[3]),
143
- };
144
- }
145
- /**
146
- * Compare two semantic versions. Returns -1 when `a < b`, 1 when `a > b`, and
147
- * 0 when they are equal. Unparseable inputs compare as equal (0) so callers
148
- * fail open rather than misordering garbage.
149
- */
150
- export function compareSemver(a, b) {
151
- const left = parseSemver(a);
152
- const right = parseSemver(b);
153
- if (!left || !right)
154
- return 0;
155
- for (const key of ["major", "minor", "patch"]) {
156
- if (left[key] > right[key])
157
- return 1;
158
- if (left[key] < right[key])
159
- return -1;
160
- }
161
- return 0;
162
- }
163
- /** True when `a` is a strictly greater semantic version than `b`. */
164
- export function isVersionGreater(a, b) {
165
- return compareSemver(a, b) > 0;
166
- }
167
- /** True when `a` is a strictly lesser semantic version than `b`. */
168
- export function isVersionLess(a, b) {
169
- return compareSemver(a, b) < 0;
170
- }
130
+ export * from "./semver.js";
171
131
  export * from "./ugc.js";
172
132
  export * from "./ugc-amplification-identity.js";
173
133
  export * from "./knowledge-repository.js";
@@ -62,6 +62,15 @@ export type PresignedImportUpload = {
62
62
  expiresInSeconds: number;
63
63
  };
64
64
  export type PresignedPublishingMediaUpload = PresignedImportUpload;
65
+ /**
66
+ * Presign the one PUT that stages an import file under `imports/<org>/`.
67
+ *
68
+ * Import staging is deliberately NOT a workspace file (`workspace-file-storage`):
69
+ * it is transient input the worker streams once and deletes, so neither the
70
+ * 100 MiB per-file nor the 1 GiB retained-files ceiling of that store applies.
71
+ * Its ceiling is the caller's plan import limit, checked by the route before it
72
+ * asks, plus the single-PUT maximum below, which no plan exceeds.
73
+ */
65
74
  export declare function presignImportUpload(input: {
66
75
  organizationId: string;
67
76
  fileName?: string | null;
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
2
2
  import { DeleteObjectCommand, GetObjectCommand, HeadObjectCommand, PutObjectCommand, S3Client, } from "@aws-sdk/client-s3";
3
3
  import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
4
4
  import { OxygenError } from "./cli-result.js";
5
+ import { IMPORT_OBJECT_SINGLE_PUT_MAX_BYTES } from "./import-limits.js";
5
6
  // S3-compatible object storage for large CSV/file imports. The CLI uploads the
6
7
  // raw file straight to the bucket via a presigned PUT URL (bypassing Vercel's
7
8
  // ~4.5MB request-body limit), then the Fly worker downloads it and COPY-loads
@@ -141,7 +142,23 @@ export function isCopilotAttachmentObjectKeyForOrganization(key, organizationId)
141
142
  export function isPublishingMediaObjectKeyForOrganization(key, organizationId) {
142
143
  return key.startsWith(`publishing-media/${organizationId}/`);
143
144
  }
145
+ /**
146
+ * Presign the one PUT that stages an import file under `imports/<org>/`.
147
+ *
148
+ * Import staging is deliberately NOT a workspace file (`workspace-file-storage`):
149
+ * it is transient input the worker streams once and deletes, so neither the
150
+ * 100 MiB per-file nor the 1 GiB retained-files ceiling of that store applies.
151
+ * Its ceiling is the caller's plan import limit, checked by the route before it
152
+ * asks, plus the single-PUT maximum below, which no plan exceeds.
153
+ */
144
154
  export async function presignImportUpload(input) {
155
+ if (!Number.isSafeInteger(input.contentLength) || input.contentLength < 1
156
+ || input.contentLength > IMPORT_OBJECT_SINGLE_PUT_MAX_BYTES) {
157
+ throw new OxygenError("request_too_large", "Import file is too large to stage in one upload.", {
158
+ details: { file_bytes: input.contentLength, max_file_bytes: IMPORT_OBJECT_SINGLE_PUT_MAX_BYTES },
159
+ exitCode: 1,
160
+ });
161
+ }
145
162
  const { client, config } = resolveClient();
146
163
  const storageKey = buildImportObjectKey({
147
164
  organizationId: input.organizationId,
@@ -21,4 +21,45 @@ export declare function operationalSpanSnapshot<T extends {
21
21
  instrumentationScope?: AttributeContainer;
22
22
  spanContext(): unknown;
23
23
  }>(span: T): T;
24
+ type ExportResource = {
25
+ attributes: Record<string, unknown>;
26
+ };
27
+ /**
28
+ * Maps the SDK resource onto the resource the collector destination exports,
29
+ * without touching the one every other destination reads. `rewrite` receives the
30
+ * SDK resource's attributes and returns the collector's; the result is
31
+ * sanitized once and memoized per source resource, so every span in a batch
32
+ * keeps sharing one resource object and the OTLP encoder still groups them into
33
+ * a single ResourceSpans entry.
34
+ *
35
+ * A resource whose async detectors are still pending (`asyncAttributesPending`)
36
+ * is mapped but not memoized, so attributes that settle later are picked up on
37
+ * a later export instead of an incomplete overlay being kept for the process.
38
+ */
39
+ export declare function collectorResourceMapper(rewrite: (attributes: Record<string, unknown>) => Record<string, unknown>): (resource: MappableResource | undefined) => ExportResource;
40
+ type MappableResource = {
41
+ attributes?: Record<string, unknown>;
42
+ asyncAttributesPending?: boolean;
43
+ };
44
+ type SnapshotSpan = Parameters<typeof operationalSpanSnapshot>[0];
45
+ /**
46
+ * The collector's per-span export transform: the sanitized snapshot, with the
47
+ * resource swapped for the collector's when a mapper is given. Without one it
48
+ * is exactly `operationalSpanSnapshot`.
49
+ */
50
+ export declare function collectorSpanSnapshot(resourceFor?: (resource: MappableResource | undefined) => ExportResource): <T extends SnapshotSpan>(span: T) => T;
51
+ type MetricExporterLike = {
52
+ export(metrics: unknown, resultCallback: (result: {
53
+ code: number;
54
+ error?: Error | undefined;
55
+ }) => void): void;
56
+ shutdown(): Promise<void>;
57
+ };
58
+ /**
59
+ * Wraps a metric exporter so each ResourceMetrics batch carries the collector's
60
+ * resource. The reader-facing methods (temporality/aggregation selection,
61
+ * forceFlush) are forwarded only when the inner exporter has them, so the
62
+ * PeriodicExportingMetricReader sees exactly what it would have unwrapped.
63
+ */
64
+ export declare function withCollectorMetricResource<E extends MetricExporterLike>(inner: E, resourceFor: (resource: MappableResource | undefined) => ExportResource): E;
24
65
  export {};
@@ -1,4 +1,5 @@
1
1
  import { normalizeTelemetryAttributes, redactSecretsInString } from "./redaction.js";
2
+ import { forwardExporterMethods } from "./telemetry-export-observer.js";
2
3
  // Namespaced semantic-convention fields evade the generic exact-key log policy.
3
4
  // Numeric usage remains operational data, subject to the shared secret policy.
4
5
  const PAYLOAD_KEY = /(?:^|[._-])(?:prompts?|completions?|inputs?|outputs?|payloads?|rows?|messages?|content|body|customer_data)(?:$|[._-])|^(?:request|response)$/i;
@@ -71,3 +72,57 @@ function stripOperationalUrl(value) {
71
72
  return value.split(/[?#]/, 1)[0] ?? "";
72
73
  }
73
74
  }
75
+ /**
76
+ * Maps the SDK resource onto the resource the collector destination exports,
77
+ * without touching the one every other destination reads. `rewrite` receives the
78
+ * SDK resource's attributes and returns the collector's; the result is
79
+ * sanitized once and memoized per source resource, so every span in a batch
80
+ * keeps sharing one resource object and the OTLP encoder still groups them into
81
+ * a single ResourceSpans entry.
82
+ *
83
+ * A resource whose async detectors are still pending (`asyncAttributesPending`)
84
+ * is mapped but not memoized, so attributes that settle later are picked up on
85
+ * a later export instead of an incomplete overlay being kept for the process.
86
+ */
87
+ export function collectorResourceMapper(rewrite) {
88
+ const cache = new WeakMap();
89
+ let withoutSource;
90
+ return (resource) => {
91
+ if (!resource)
92
+ return withoutSource ??= { attributes: operationalAttributes(rewrite({})) };
93
+ const cached = cache.get(resource);
94
+ if (cached)
95
+ return cached;
96
+ const mapped = { attributes: operationalAttributes(rewrite(resource.attributes ?? {})) };
97
+ if (!resource.asyncAttributesPending)
98
+ cache.set(resource, mapped);
99
+ return mapped;
100
+ };
101
+ }
102
+ /**
103
+ * The collector's per-span export transform: the sanitized snapshot, with the
104
+ * resource swapped for the collector's when a mapper is given. Without one it
105
+ * is exactly `operationalSpanSnapshot`.
106
+ */
107
+ export function collectorSpanSnapshot(resourceFor) {
108
+ if (!resourceFor)
109
+ return (span) => operationalSpanSnapshot(span);
110
+ return (span) => ({ ...operationalSpanSnapshot(span), resource: resourceFor(span.resource) });
111
+ }
112
+ /**
113
+ * Wraps a metric exporter so each ResourceMetrics batch carries the collector's
114
+ * resource. The reader-facing methods (temporality/aggregation selection,
115
+ * forceFlush) are forwarded only when the inner exporter has them, so the
116
+ * PeriodicExportingMetricReader sees exactly what it would have unwrapped.
117
+ */
118
+ export function withCollectorMetricResource(inner, resourceFor) {
119
+ const wrapped = {
120
+ export(metrics, resultCallback) {
121
+ const batch = metrics;
122
+ inner.export({ ...batch, resource: resourceFor(batch.resource) }, resultCallback);
123
+ },
124
+ shutdown: () => inner.shutdown(),
125
+ };
126
+ forwardExporterMethods(inner, wrapped);
127
+ return wrapped;
128
+ }
@@ -1,5 +1,6 @@
1
1
  import { type LimitsTier } from "./plan-limits.js";
2
- import type { RepricingOptions } from "./repricing.js";
2
+ import { type RepricingOptions } from "./repricing.js";
3
+ import { type WorkspaceTableCapacityStatus } from "./table-capacity.js";
3
4
  /**
4
5
  * The seven plan bands of the 2026-09 repricing (spec § 4, slice S06): free
5
6
  * plus one band per Oxygen plan size. They sit BESIDE the five-rung
@@ -15,6 +16,16 @@ export type PlanBand = "free" | "49" | "99" | "199" | "499" | "999" | "1999";
15
16
  export declare const PLAN_BAND_ORDER: readonly PlanBand[];
16
17
  /** The limits rung each band enforces at today. */
17
18
  export declare const PLAN_BAND_LIMITS_TIER: Readonly<Record<PlanBand, LimitsTier>>;
19
+ /**
20
+ * L4.1 (repricing 2026-09, slice S55; decision record "Worker share"): a
21
+ * tenant's share of the legacy worker's tenant queue while other tenants wait.
22
+ * A weight-16 tenant gets sixteen quanta for every one a weight-1 tenant gets;
23
+ * with nobody else waiting every tenant is served at once, whatever its weight.
24
+ * It is a scheduling share, never a sold limit: capacity is sized so contention
25
+ * is rare (`.agents/skills/oxygen-worker-performance`, "Worker share target").
26
+ * An org whose band is not yet resolved schedules at the free weight.
27
+ */
28
+ export declare const PLAN_BAND_WORKER_SHARE_WEIGHT: Readonly<Record<PlanBand, number>>;
18
29
  /**
19
30
  * PROPOSED (P-57, repricing spec § 4): a grandfathered or legacy plan takes the
20
31
  * band of the limits rung it enforces at today, so no grandfathered customer
@@ -42,6 +53,58 @@ export declare function planBandForLimitsResolution(resolution: {
42
53
  /** The band's monthly price, read from the Oxygen plan it names; null for free. */
43
54
  export declare function planBandMonthlyPriceCents(band: PlanBand): number | null;
44
55
  export declare function planBandLabel(band: PlanBand): string;
56
+ /**
57
+ * PROPOSED (P-54): the free plan keeps a 50-credit default. It has no monthly
58
+ * grant to take 10% of (10% of its one-time 1,000 would be 100).
59
+ */
60
+ export declare const FREE_PLAN_DEFAULT_DELIVERY_CREDIT_CEILING = 50;
61
+ /**
62
+ * A plan size's target default delivery ceiling: 10% of its monthly credits,
63
+ * rounded half up (decision L5.1; the volume bonus counts, the partner bonus
64
+ * does not, P-54). This is the target, not always the value in force: see
65
+ * `resolveDefaultTriggerRunCreditCeilingForPlan`.
66
+ */
67
+ export declare function planBandDeliveryCreditCeilingTarget(band: PlanBand): number;
68
+ /** What an org's plan resolves to: the raw plan tier and its limits rung. */
69
+ export type PlanSpendResolution = {
70
+ planTier: string | null;
71
+ limitsTier: LimitsTier;
72
+ };
73
+ /**
74
+ * The default credit ceiling of one LIVE workflow run fired by an unattended
75
+ * trigger (cron, webhook, event) that declares no `max_credits`, for a plan.
76
+ * `null` means no default applies (enterprise); an explicit cap always wins and
77
+ * is never clamped by this.
78
+ */
79
+ export declare function resolveDefaultTriggerRunCreditCeilingForPlan(resolution: PlanSpendResolution, options?: RepricingOptions): number | null;
80
+ /** The same default for one standing or webhook table auto-run batch. */
81
+ export declare function resolveDefaultAutoRunBatchCreditCeilingForPlan(resolution: PlanSpendResolution, options?: RepricingOptions): number | null;
82
+ /**
83
+ * Public webhook ingress (repricing 2026-09, decision L4.4, ratified
84
+ * 2026-09-26): deliveries a minute that one webhook target (a Table endpoint,
85
+ * a workflow or Agent trigger, the workspace's RB2B receiver) accepts. It used
86
+ * to be a flat 600 per target, with 60 per sender IP on most targets. Every
87
+ * value rises, so this is live on release rather than held behind the
88
+ * repricing switch.
89
+ */
90
+ export declare const PUBLIC_WEBHOOK_INGRESS_PER_TARGET_PER_MINUTE: Readonly<Record<PlanBand, number>>;
91
+ /**
92
+ * PROPOSED (P-50, repricing spec § 4.4): the decision raises the per-IP limit
93
+ * "to match", read as equal to the per-target limit (a share of 1). One sender
94
+ * (a form tool, a CRM) usually posts from one address, so any lower per-IP
95
+ * limit would cap a real integration below its plan's rate.
96
+ */
97
+ export declare const PUBLIC_WEBHOOK_INGRESS_PER_IP_SHARE_OF_TARGET = 1;
98
+ export declare const PUBLIC_WEBHOOK_INGRESS_WINDOW_SECONDS = 60;
99
+ export type PublicWebhookIngressLimits = {
100
+ perTargetPerMinute: number;
101
+ perSenderIpPerMinute: number;
102
+ windowSeconds: number;
103
+ };
104
+ /** The webhook ingress limits a band enforces. */
105
+ export declare function resolvePublicWebhookIngressLimits(band: PlanBand): PublicWebhookIngressLimits;
106
+ /** The webhook ingress limits of one band, as the `/api/cli/limits` contract reports them. */
107
+ export declare function describeWebhookIngress(band: PlanBand): PlanBandLimitsReport["webhooks"];
45
108
  /**
46
109
  * Request rates, per minute unless named otherwise. The totals bound every
47
110
  * `/api/cli/*` call; the operation buckets bind first for the calls a script
@@ -87,6 +150,15 @@ export type PlanBandLimitsReport = {
87
150
  label: string;
88
151
  monthly_price_usd: number | null;
89
152
  monthly_credits: number | null;
153
+ /**
154
+ * Credits granted above face value ($1 = 100 credits), repricing 2026-09
155
+ * decision 1.3: 0 up to $199, then +5% / +10% / +15% on $499 / $999 / $1,999.
156
+ * Already included in `monthly_credits`. Null on free, which has no plan grant.
157
+ */
158
+ volume_bonus_credits: number | null;
159
+ volume_bonus_percent: number | null;
160
+ /** What 100 plan credits cost on this size, the volume bonus included. */
161
+ usd_per_100_credits: number | null;
90
162
  limits_tier: LimitsTier;
91
163
  api: ApiRateLimitsReport;
92
164
  storage: {
@@ -97,22 +169,66 @@ export type PlanBandLimitsReport = {
97
169
  /** Equal to rows per Table (PROPOSED P-60): one file can fill an empty Table. */
98
170
  import_max_rows_per_file: number;
99
171
  import_max_file_bytes: number;
172
+ /**
173
+ * Where the values above stand against the ratified ladder (decision F.2):
174
+ * `in_force`, `scheduled` (they move to `target` at `effective_at`) or
175
+ * `awaiting_capacity_test` (`target` applies once the rows-per-Table
176
+ * capacity test at this size passes). A cut is announced only once the
177
+ * repricing date is set, so `target` is null while nothing is pending.
178
+ */
179
+ status: WorkspaceTableCapacityStatus["status"];
180
+ effective_at: string | null;
181
+ target: PlanBandStorageTarget | null;
100
182
  };
101
183
  spend: {
102
184
  trigger_run_credit_ceiling: number | null;
103
185
  auto_run_batch_credit_ceiling: number | null;
104
186
  agent_run_max_total_credits: number;
105
187
  agent_run_max_inference_credits: number;
188
+ /** Paid work the attended Copilot may start without an approval card. */
189
+ copilot_approval_free_per_call_credits: number;
190
+ copilot_approval_free_per_session_credits: number;
191
+ /** The platform's runaway bound on one attended Copilot turn (every plan). */
192
+ copilot_attended_turn_backstop_credits: number;
106
193
  org_daily_guard_warn_credits: number | null;
107
194
  org_daily_guard_block_credits: number | null;
108
195
  byok_column_run_max_rows: number | null;
109
196
  byok_provider_daily_calls: number | null;
110
197
  };
198
+ /**
199
+ * Table work limits (L6): rows one write request may carry, and columns one
200
+ * action run or standing auto-run may carry.
201
+ */
202
+ tables: {
203
+ max_rows_per_write_request: number;
204
+ max_columns_per_run: number;
205
+ };
206
+ /** The raw signal-event feed: events per read and look-back days. */
111
207
  signals: {
112
208
  max_events: number;
113
209
  max_window_days: number;
114
210
  };
211
+ /** Public webhook ingress, per target and per sender IP (decision L4.4). */
212
+ webhooks: {
213
+ ingress_requests_per_target_per_minute: number;
214
+ ingress_requests_per_sender_ip_per_minute: number;
215
+ window_seconds: number;
216
+ };
217
+ workflows: {
218
+ /** Fastest cron schedule a workflow or Agent trigger may use, in force now. */
219
+ cron_min_interval_minutes: number;
220
+ };
221
+ };
222
+ /** The storage values a band moves to, in the `/api/cli/limits` field names. */
223
+ export type PlanBandStorageTarget = {
224
+ table_row_limit: number;
225
+ workspace_row_limit: number;
226
+ workspace_database_warning_bytes: number;
227
+ workspace_database_limit_bytes: number;
228
+ import_max_rows_per_file: number;
115
229
  };
230
+ /** A band's storage status as `/api/cli/limits` reports it (`plan_bands[].storage` and `storage_capacity`). */
231
+ export declare function describePlanBandStorageStatus(band: PlanBand, options?: RepricingOptions): Pick<PlanBandLimitsReport["storage"], "status" | "effective_at" | "target">;
116
232
  export declare function describePlanBandLimits(band: PlanBand, options?: RepricingOptions): PlanBandLimitsReport;
117
233
  /** Every band's limits in force today, smallest first. */
118
234
  export declare function describePlanBandLadder(options?: RepricingOptions): PlanBandLimitsReport[];