@oxygen-agent/cli 1.982.3 → 1.1003.12

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 (127) hide show
  1. package/README.md +1 -1
  2. package/dist/admin-primary-providers-render.d.ts +0 -2
  3. package/dist/admin-primary-providers-render.js +1 -1
  4. package/dist/browser-login.js +1 -4
  5. package/dist/command-manifest.d.ts +3 -2
  6. package/dist/command-manifest.js +10 -0
  7. package/dist/credentials.d.ts +1 -1
  8. package/dist/functions-commands.js +27 -7
  9. package/dist/help.d.ts +29 -0
  10. package/dist/help.js +139 -0
  11. package/dist/index.js +1875 -164
  12. package/dist/knowledge-mirror.d.ts +2 -2
  13. package/dist/runtime.d.ts +0 -15
  14. package/dist/runtime.js +1 -1
  15. package/dist/session.d.ts +4 -3
  16. package/dist/skills.d.ts +8 -7
  17. package/dist/skills.js +24 -10
  18. package/dist/transcript.d.ts +2 -1
  19. package/dist/ugc-commands.d.ts +3 -6
  20. package/dist/ugc-commands.js +2 -1200
  21. package/dist/util.d.ts +1 -1
  22. package/dist/util.js +1 -3
  23. package/node_modules/@oxygen/cli-ugc/dist/commands.d.ts +3 -0
  24. package/node_modules/@oxygen/cli-ugc/dist/commands.js +1178 -0
  25. package/node_modules/@oxygen/cli-ugc/dist/field-parser.d.ts +7 -0
  26. package/node_modules/@oxygen/cli-ugc/dist/field-parser.js +25 -0
  27. package/node_modules/@oxygen/cli-ugc/dist/index.d.ts +14 -0
  28. package/node_modules/@oxygen/cli-ugc/dist/index.js +5 -0
  29. package/node_modules/@oxygen/cli-ugc/package.json +15 -0
  30. package/node_modules/@oxygen/formula/dist/coerce.d.ts +10 -0
  31. package/node_modules/@oxygen/formula/dist/coerce.js +10 -0
  32. package/node_modules/@oxygen/formula/dist/expression.js +14 -1
  33. package/node_modules/@oxygen/formula/dist/formula-functions.js +136 -1
  34. package/node_modules/@oxygen/formula/dist/hash.d.ts +19 -0
  35. package/node_modules/@oxygen/formula/dist/hash.js +199 -0
  36. package/node_modules/@oxygen/formula/dist/index.d.ts +1 -0
  37. package/node_modules/@oxygen/formula/dist/index.js +1 -0
  38. package/node_modules/@oxygen/formula/dist/value-cleaners.d.ts +74 -0
  39. package/node_modules/@oxygen/formula/dist/value-cleaners.js +358 -0
  40. package/node_modules/@oxygen/shared/dist/array-utils.d.ts +5 -0
  41. package/node_modules/@oxygen/shared/dist/array-utils.js +11 -0
  42. package/node_modules/@oxygen/shared/dist/billing.d.ts +103 -47
  43. package/node_modules/@oxygen/shared/dist/billing.js +150 -40
  44. package/node_modules/@oxygen/shared/dist/capability-discovery.d.ts +17 -0
  45. package/node_modules/@oxygen/shared/dist/capability-discovery.js +114 -16
  46. package/node_modules/@oxygen/shared/dist/column-autofill.d.ts +52 -0
  47. package/node_modules/@oxygen/shared/dist/column-autofill.js +80 -0
  48. package/node_modules/@oxygen/shared/dist/column-output-fields.js +14 -10
  49. package/node_modules/@oxygen/shared/dist/company-enrichment-fields.d.ts +108 -0
  50. package/node_modules/@oxygen/shared/dist/company-enrichment-fields.js +545 -0
  51. package/node_modules/@oxygen/shared/dist/copilot-playbooks.d.ts +18 -0
  52. package/node_modules/@oxygen/shared/dist/copilot-playbooks.js +43 -0
  53. package/node_modules/@oxygen/shared/dist/copilot-skills.d.ts +15 -0
  54. package/node_modules/@oxygen/shared/dist/copilot-skills.generated.d.ts +31 -0
  55. package/node_modules/@oxygen/shared/dist/copilot-skills.generated.js +41 -0
  56. package/node_modules/@oxygen/shared/dist/copilot-skills.js +6 -0
  57. package/node_modules/@oxygen/shared/dist/deploy-env.d.ts +74 -0
  58. package/node_modules/@oxygen/shared/dist/deploy-env.js +82 -0
  59. package/node_modules/@oxygen/shared/dist/dnc-rules.d.ts +130 -0
  60. package/node_modules/@oxygen/shared/dist/dnc-rules.js +221 -0
  61. package/node_modules/@oxygen/shared/dist/enrichment-intents.d.ts +103 -0
  62. package/node_modules/@oxygen/shared/dist/enrichment-intents.js +819 -0
  63. package/node_modules/@oxygen/shared/dist/error-message.d.ts +1 -0
  64. package/node_modules/@oxygen/shared/dist/error-message.js +3 -0
  65. package/node_modules/@oxygen/shared/dist/error-redaction.js +1 -3
  66. package/node_modules/@oxygen/shared/dist/external-write-policy.d.ts +33 -0
  67. package/node_modules/@oxygen/shared/dist/external-write-policy.js +68 -0
  68. package/node_modules/@oxygen/shared/dist/format-percent.d.ts +8 -0
  69. package/node_modules/@oxygen/shared/dist/format-percent.js +13 -0
  70. package/node_modules/@oxygen/shared/dist/freemail-domains.d.ts +81 -0
  71. package/node_modules/@oxygen/shared/dist/freemail-domains.js +157 -0
  72. package/node_modules/@oxygen/shared/dist/future-signup-lifecycle-projection.d.ts +1 -0
  73. package/node_modules/@oxygen/shared/dist/future-signup-lifecycle-projection.js +1 -1
  74. package/node_modules/@oxygen/shared/dist/index.d.ts +14 -0
  75. package/node_modules/@oxygen/shared/dist/index.js +14 -0
  76. package/node_modules/@oxygen/shared/dist/json-path.js +1 -3
  77. package/node_modules/@oxygen/shared/dist/knowledge-bases.js +1 -3
  78. package/node_modules/@oxygen/shared/dist/knowledge-bootstrap.d.ts +17 -2
  79. package/node_modules/@oxygen/shared/dist/knowledge-bootstrap.js +28 -6
  80. package/node_modules/@oxygen/shared/dist/langfuse.d.ts +12 -1
  81. package/node_modules/@oxygen/shared/dist/langfuse.js +57 -8
  82. package/node_modules/@oxygen/shared/dist/linkedin-countries.d.ts +32 -0
  83. package/node_modules/@oxygen/shared/dist/linkedin-countries.js +359 -0
  84. package/node_modules/@oxygen/shared/dist/log-sink-selector.d.ts +39 -0
  85. package/node_modules/@oxygen/shared/dist/log-sink-selector.js +56 -0
  86. package/node_modules/@oxygen/shared/dist/log.d.ts +1 -0
  87. package/node_modules/@oxygen/shared/dist/log.js +6 -1
  88. package/node_modules/@oxygen/shared/dist/object-storage.d.ts +17 -0
  89. package/node_modules/@oxygen/shared/dist/object-storage.js +21 -0
  90. package/node_modules/@oxygen/shared/dist/otlp-log-sink.d.ts +54 -0
  91. package/node_modules/@oxygen/shared/dist/otlp-log-sink.js +213 -0
  92. package/node_modules/@oxygen/shared/dist/plan-capabilities.js +1 -0
  93. package/node_modules/@oxygen/shared/dist/plan-limits.d.ts +23 -22
  94. package/node_modules/@oxygen/shared/dist/plan-limits.js +45 -18
  95. package/node_modules/@oxygen/shared/dist/pricing-sheet.d.ts +48 -41
  96. package/node_modules/@oxygen/shared/dist/pricing-sheet.js +36 -25
  97. package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.d.ts +22 -22
  98. package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.js +40 -34
  99. package/node_modules/@oxygen/shared/dist/product-analytics-environment.js +9 -0
  100. package/node_modules/@oxygen/shared/dist/product-analytics-events.d.ts +15 -0
  101. package/node_modules/@oxygen/shared/dist/product-analytics-events.js +15 -0
  102. package/node_modules/@oxygen/shared/dist/rate-window.d.ts +5 -0
  103. package/node_modules/@oxygen/shared/dist/rate-window.js +8 -0
  104. package/node_modules/@oxygen/shared/dist/research-output-contract.d.ts +33 -1
  105. package/node_modules/@oxygen/shared/dist/research-output-contract.js +65 -5
  106. package/node_modules/@oxygen/shared/dist/search-vocab.js +4 -5
  107. package/node_modules/@oxygen/shared/dist/select-options.js +6 -1
  108. package/node_modules/@oxygen/shared/dist/sequence-crm-events.d.ts +1 -1
  109. package/node_modules/@oxygen/shared/dist/sequence-failures.js +1 -5
  110. package/node_modules/@oxygen/shared/dist/sequence-hubspot-sync.d.ts +1 -1
  111. package/node_modules/@oxygen/shared/dist/sequences.d.ts +49 -0
  112. package/node_modules/@oxygen/shared/dist/sequences.js +134 -4
  113. package/node_modules/@oxygen/shared/dist/spend-safety.d.ts +22 -10
  114. package/node_modules/@oxygen/shared/dist/spend-safety.js +15 -21
  115. package/node_modules/@oxygen/shared/dist/sql-rows.d.ts +1 -0
  116. package/node_modules/@oxygen/shared/dist/sql-rows.js +3 -0
  117. package/node_modules/@oxygen/shared/dist/telemetry.js +9 -1
  118. package/node_modules/@oxygen/shared/dist/type-guards.d.ts +22 -0
  119. package/node_modules/@oxygen/shared/dist/type-guards.js +35 -0
  120. package/node_modules/@oxygen/shared/dist/value-readers.d.ts +21 -0
  121. package/node_modules/@oxygen/shared/dist/value-readers.js +59 -0
  122. package/node_modules/@oxygen/shared/dist/version.js +1 -1
  123. package/node_modules/@oxygen/shared/package.json +60 -0
  124. package/node_modules/@oxygen/workflows/dist/graph/expression.js +2 -5
  125. package/node_modules/@oxygen/workflows/dist/graph/manifest-schema.d.ts +15 -15
  126. package/node_modules/@oxygen/workflows/dist/graph/params.js +1 -1
  127. package/package.json +6 -3
@@ -5,6 +5,23 @@ export declare function resolveObjectStorageClient(): {
5
5
  client: S3Client;
6
6
  bucket: string;
7
7
  };
8
+ /**
9
+ * The same endpoint and credentials, aimed at a DIFFERENT bucket.
10
+ *
11
+ * Every namespace above shares one private bucket because they share one access
12
+ * story: org-scoped keys, no anonymous reads. Public blobs (agency-directory
13
+ * logos; Payload media reaches the same bucket through its own plugin) are the
14
+ * opposite — their bytes are fetched by browsers with no credential at all — so
15
+ * on a self-hosted deploy they belong in a bucket whose read policy is public,
16
+ * never in the one holding customer lead lists.
17
+ * Same S3 account, different bucket: hence the client is reused and only the
18
+ * bucket is overridden. Empty is refused rather than silently falling back to
19
+ * the import bucket, which is exactly the mix-up this split exists to prevent.
20
+ */
21
+ export declare function resolveObjectStorageClientForBucket(bucket: string): {
22
+ client: S3Client;
23
+ bucket: string;
24
+ };
8
25
  export declare function buildImportObjectKey(input: {
9
26
  organizationId: string;
10
27
  fileName?: string | null;
@@ -82,6 +82,27 @@ export function resolveObjectStorageClient() {
82
82
  const { client, config } = resolveClient();
83
83
  return { client, bucket: config.bucket };
84
84
  }
85
+ /**
86
+ * The same endpoint and credentials, aimed at a DIFFERENT bucket.
87
+ *
88
+ * Every namespace above shares one private bucket because they share one access
89
+ * story: org-scoped keys, no anonymous reads. Public blobs (agency-directory
90
+ * logos; Payload media reaches the same bucket through its own plugin) are the
91
+ * opposite — their bytes are fetched by browsers with no credential at all — so
92
+ * on a self-hosted deploy they belong in a bucket whose read policy is public,
93
+ * never in the one holding customer lead lists.
94
+ * Same S3 account, different bucket: hence the client is reused and only the
95
+ * bucket is overridden. Empty is refused rather than silently falling back to
96
+ * the import bucket, which is exactly the mix-up this split exists to prevent.
97
+ */
98
+ export function resolveObjectStorageClientForBucket(bucket) {
99
+ const target = bucket.trim();
100
+ if (!target) {
101
+ throw new OxygenError("object_storage_bucket_missing", "No object-storage bucket was resolved for this operation.", { exitCode: 1 });
102
+ }
103
+ const { client } = resolveClient();
104
+ return { client, bucket: target };
105
+ }
85
106
  export function buildImportObjectKey(input) {
86
107
  const safeName = sanitizeFileName(input.fileName) || "import";
87
108
  return `imports/${input.organizationId}/${randomUUID()}/${safeName}`;
@@ -0,0 +1,54 @@
1
+ import type { LogSink } from "./log.js";
2
+ /**
3
+ * Ships `log()` records to an OTLP/HTTP logs receiver (the self-hosted SigNoz
4
+ * collector) instead of — or alongside — Axiom.
5
+ *
6
+ * WHY HAND-ROLLED. This is a few dozen lines of JSON against a stable, versioned
7
+ * wire format. `@opentelemetry/exporter-logs-otlp-http` would pull the OTel logs
8
+ * SDK, a LoggerProvider, a BatchLogRecordProcessor and their transitive protobuf
9
+ * machinery into a package that every workspace imports — including edge-safe
10
+ * ones — to serialize a shape this module states in full below. The exporter
11
+ * also owns its own batching, so the drop-oldest/never-block/never-throw
12
+ * contract the two existing shippers hold would become the SDK's opinion rather
13
+ * than ours. OTLP/JSON is chosen over protobuf for the same reason: no codegen,
14
+ * no dependency, and a body an operator can read with `curl` when a collector
15
+ * silently rejects a batch.
16
+ *
17
+ * DELIVERY IS BEST-EFFORT BY CONSTRUCTION, exactly like
18
+ * `apps/worker/src/log-shipper.ts`: the sink only enqueues (it is called from
19
+ * inside `log()`, so it must never await, throw, or block), the queue drops the
20
+ * OLDEST record on overflow because the newest lines describe the incident being
21
+ * debugged, and a collector outage is a dropped batch rather than a process
22
+ * failure. Failures are written to stderr directly and at most once per flush
23
+ * window — reporting them through `log()` would feed this sink its own error
24
+ * line and recurse.
25
+ */
26
+ export type OtlpLogSinkOptions = {
27
+ /** Full receiver URL, e.g. `http://collector.internal:4318/v1/logs`. */
28
+ endpoint: string;
29
+ /** Extra request headers (collector auth); see `parseOtlpHeaders`. */
30
+ headers?: Record<string, string> | undefined;
31
+ /** `service.name` resource attribute. */
32
+ serviceName: string;
33
+ /** Resource attributes merged after the defaults, so a caller can override them. */
34
+ resource?: Record<string, string | null | undefined> | undefined;
35
+ batchSize?: number | undefined;
36
+ /** `0` disables the interval timer, for runtimes that flush off a request lifecycle. */
37
+ flushIntervalMs?: number | undefined;
38
+ maxQueue?: number | undefined;
39
+ fetch?: typeof globalThis.fetch | undefined;
40
+ };
41
+ export type OtlpLogSinkHandle = {
42
+ sink: LogSink;
43
+ flush: () => Promise<void>;
44
+ stop: () => Promise<void>;
45
+ };
46
+ /**
47
+ * `k=v,k=v` — the same shape as `OTEL_EXPORTER_OTLP_HEADERS`, so an operator who
48
+ * knows the OTel variable can set ours without learning a second syntax. A pair
49
+ * with no `=`, or an empty key, is skipped rather than throwing: this is parsed
50
+ * at process start, and a malformed telemetry header must not stop a boot. The
51
+ * value keeps every `=` after the first (tokens contain them).
52
+ */
53
+ export declare function parseOtlpHeaders(raw: string | null | undefined): Record<string, string>;
54
+ export declare function createOtlpLogSink(options: OtlpLogSinkOptions): OtlpLogSinkHandle;
@@ -0,0 +1,213 @@
1
+ const DEFAULT_BATCH_SIZE = 500;
2
+ const DEFAULT_FLUSH_INTERVAL_MS = 2_000;
3
+ const DEFAULT_MAX_QUEUE = 10_000;
4
+ const EXPORT_TIMEOUT_MS = 10_000;
5
+ const NO_TIMER_REPORT_WINDOW_MS = 60_000;
6
+ const MAX_ERROR_BODY = 300;
7
+ // OTLP severity numbers (logs data model): the base of each 4-value band.
8
+ const SEVERITY_NUMBERS = { debug: 5, info: 9, warn: 13, error: 17 };
9
+ // Set by log() on every record; they become the OTLP record's own fields rather
10
+ // than attributes, so carrying them twice would just inflate every batch.
11
+ const RECORD_FIELDS_NOT_ATTRIBUTES = new Set(["ts", "level", "msg"]);
12
+ /**
13
+ * `k=v,k=v` — the same shape as `OTEL_EXPORTER_OTLP_HEADERS`, so an operator who
14
+ * knows the OTel variable can set ours without learning a second syntax. A pair
15
+ * with no `=`, or an empty key, is skipped rather than throwing: this is parsed
16
+ * at process start, and a malformed telemetry header must not stop a boot. The
17
+ * value keeps every `=` after the first (tokens contain them).
18
+ */
19
+ export function parseOtlpHeaders(raw) {
20
+ const headers = {};
21
+ if (!raw)
22
+ return headers;
23
+ for (const pair of raw.split(",")) {
24
+ const separator = pair.indexOf("=");
25
+ if (separator <= 0)
26
+ continue;
27
+ const key = pair.slice(0, separator).trim();
28
+ const value = pair.slice(separator + 1).trim();
29
+ if (!key)
30
+ continue;
31
+ headers[key] = value;
32
+ }
33
+ return headers;
34
+ }
35
+ function toAnyValue(value) {
36
+ if (typeof value === "string")
37
+ return { stringValue: value };
38
+ if (typeof value === "boolean")
39
+ return { boolValue: value };
40
+ if (typeof value === "number") {
41
+ if (!Number.isFinite(value))
42
+ return { stringValue: String(value) };
43
+ // OTLP/JSON encodes int64 as a STRING (proto3 JSON mapping); a bare number
44
+ // there is what makes a collector reject an otherwise valid record.
45
+ return Number.isInteger(value) ? { intValue: String(value) } : { doubleValue: value };
46
+ }
47
+ if (value === null || value === undefined)
48
+ return null;
49
+ try {
50
+ return { stringValue: JSON.stringify(value) ?? String(value) };
51
+ }
52
+ catch {
53
+ return { stringValue: "[unserializable]" };
54
+ }
55
+ }
56
+ function toAttributes(source, skip) {
57
+ const attributes = [];
58
+ for (const [key, value] of Object.entries(source)) {
59
+ if (skip?.has(key))
60
+ continue;
61
+ const anyValue = toAnyValue(value);
62
+ if (!anyValue)
63
+ continue;
64
+ attributes.push({ key, value: anyValue });
65
+ }
66
+ return attributes;
67
+ }
68
+ function timeUnixNano(ts) {
69
+ const parsed = typeof ts === "string" ? Date.parse(ts) : Number.NaN;
70
+ const millis = Number.isFinite(parsed) ? parsed : Date.now();
71
+ // Decimal string, not a number: nanoseconds since the epoch exceed 2^53.
72
+ return `${millis}000000`;
73
+ }
74
+ function toLogRecord(record) {
75
+ const level = typeof record.level === "string" && record.level in SEVERITY_NUMBERS
76
+ ? record.level
77
+ : "info";
78
+ return {
79
+ timeUnixNano: timeUnixNano(record.ts),
80
+ severityNumber: SEVERITY_NUMBERS[level],
81
+ severityText: level.toUpperCase(),
82
+ body: { stringValue: typeof record.msg === "string" ? record.msg : String(record.msg ?? "") },
83
+ attributes: toAttributes(record, RECORD_FIELDS_NOT_ATTRIBUTES),
84
+ };
85
+ }
86
+ export function createOtlpLogSink(options) {
87
+ const batchSize = options.batchSize && options.batchSize > 0 ? options.batchSize : DEFAULT_BATCH_SIZE;
88
+ const maxQueue = options.maxQueue && options.maxQueue > 0 ? options.maxQueue : DEFAULT_MAX_QUEUE;
89
+ const flushIntervalMs = options.flushIntervalMs === undefined
90
+ ? DEFAULT_FLUSH_INTERVAL_MS
91
+ : options.flushIntervalMs;
92
+ const doFetch = options.fetch ?? globalThis.fetch;
93
+ const reportWindowMs = flushIntervalMs > 0 ? flushIntervalMs : NO_TIMER_REPORT_WINDOW_MS;
94
+ // A null/undefined override is dropped rather than applied, so a caller that
95
+ // resolves its own environment and comes back empty-handed falls back to the
96
+ // default here instead of shipping records with no `deployment.environment`.
97
+ const resourceOverrides = Object.fromEntries(Object.entries(options.resource ?? {}).filter(([, value]) => value !== null && value !== undefined));
98
+ const resourceAttributes = toAttributes({
99
+ "service.name": options.serviceName,
100
+ "deployment.environment": process.env.OXYGEN_DEPLOY_ENV
101
+ ?? process.env.VERCEL_ENV
102
+ ?? process.env.NODE_ENV
103
+ ?? null,
104
+ ...resourceOverrides,
105
+ });
106
+ const queue = [];
107
+ let inFlight = null;
108
+ let stopped = false;
109
+ let lastReportAt = 0;
110
+ // Never through log(): this module IS the sink log() calls, so a log() here
111
+ // would enqueue its own failure and re-fail on the next flush forever. stderr
112
+ // is the one destination that cannot re-enter, and the write is guarded
113
+ // because a closed/full stderr must not throw into the flush path either.
114
+ const report = (fields) => {
115
+ const now = Date.now();
116
+ if (now - lastReportAt < reportWindowMs)
117
+ return;
118
+ lastReportAt = now;
119
+ try {
120
+ process.stderr.write(`${JSON.stringify({
121
+ level: "error",
122
+ msg: "otlp_log_sink.export_failed",
123
+ endpoint: options.endpoint,
124
+ ...fields,
125
+ })}\n`);
126
+ }
127
+ catch {
128
+ // A telemetry failure that cannot even be reported is still not a fault
129
+ // of the code being observed.
130
+ }
131
+ };
132
+ const sink = (record) => {
133
+ if (stopped)
134
+ return;
135
+ // Drop the OLDEST record and keep this one: under sustained backpressure the
136
+ // newest lines are the ones describing the incident being debugged.
137
+ while (queue.length >= maxQueue) {
138
+ queue.shift();
139
+ }
140
+ queue.push(record);
141
+ };
142
+ const flush = async () => {
143
+ if (inFlight)
144
+ return inFlight;
145
+ if (queue.length === 0)
146
+ return undefined;
147
+ const batch = queue.splice(0, batchSize);
148
+ const payload = JSON.stringify({
149
+ resourceLogs: [
150
+ {
151
+ resource: { attributes: resourceAttributes },
152
+ scopeLogs: [
153
+ {
154
+ scope: { name: "oxygen" },
155
+ logRecords: batch.map(toLogRecord),
156
+ },
157
+ ],
158
+ },
159
+ ],
160
+ });
161
+ const run = (async () => {
162
+ try {
163
+ const response = await doFetch(options.endpoint, {
164
+ method: "POST",
165
+ headers: { "Content-Type": "application/json", ...options.headers },
166
+ body: payload,
167
+ signal: AbortSignal.timeout(EXPORT_TIMEOUT_MS),
168
+ });
169
+ if (response.ok)
170
+ return;
171
+ let body = "";
172
+ try {
173
+ body = (await response.text()).slice(0, MAX_ERROR_BODY);
174
+ }
175
+ catch {
176
+ // An unreadable body is no worse than a status-only report.
177
+ }
178
+ // The batch is gone deliberately: retrying into an unhealthy collector
179
+ // is how a telemetry path turns an ingest blip into memory pressure.
180
+ report({ status: response.status, dropped_events: batch.length, response_body: body });
181
+ }
182
+ catch (error) {
183
+ report({
184
+ dropped_events: batch.length,
185
+ error_message: error instanceof Error ? error.message : String(error),
186
+ });
187
+ }
188
+ })();
189
+ inFlight = run.finally(() => {
190
+ inFlight = null;
191
+ });
192
+ return inFlight;
193
+ };
194
+ const timer = flushIntervalMs > 0
195
+ ? setInterval(() => {
196
+ void flush();
197
+ }, flushIntervalMs)
198
+ : null;
199
+ // Never hold the process open on the telemetry timer.
200
+ timer?.unref?.();
201
+ const stop = async () => {
202
+ stopped = true;
203
+ if (timer)
204
+ clearInterval(timer);
205
+ // Drain rather than flush once: a shutdown's final lines are usually the
206
+ // interesting ones, and one flush ships only the oldest `batchSize`.
207
+ while (queue.length > 0) {
208
+ await flush();
209
+ }
210
+ await inFlight;
211
+ };
212
+ return { sink, flush, stop };
213
+ }
@@ -50,6 +50,7 @@ export const PLAN_GATED_CAPABILITIES = [
50
50
  * `required_plan_tiers` in tool-access, which the resolver consults separately.
51
51
  */
52
52
  export const PLAN_GATE_QUALIFYING_TIERS = [
53
+ "oxygen",
53
54
  "starter",
54
55
  "pro",
55
56
  "team",
@@ -185,14 +185,14 @@ export declare const PLAN_LIMITS: {
185
185
  readonly maxDirectCsvBodyBytes: 10000000;
186
186
  };
187
187
  readonly agents: {
188
- readonly maxTotalCredits: 2500;
189
- readonly maxInferenceCredits: 1500;
188
+ readonly maxTotalCredits: 250;
189
+ readonly maxInferenceCredits: 150;
190
190
  readonly maxUpstreamCostUsd: 10;
191
191
  readonly maxUpstreamCostUsdByok: 10;
192
192
  };
193
193
  readonly copilot: {
194
- readonly maxSessionBudgetCredits: 5000;
195
- readonly defaultPerTurnCreditCeiling: 1500;
194
+ readonly maxSessionBudgetCredits: 500;
195
+ readonly defaultPerTurnCreditCeiling: 150;
196
196
  };
197
197
  readonly signals: {
198
198
  readonly maxEvents: 100;
@@ -295,14 +295,14 @@ export declare const PLAN_LIMITS: {
295
295
  readonly maxDirectCsvBodyBytes: 10000000;
296
296
  };
297
297
  readonly agents: {
298
- readonly maxTotalCredits: 10000;
299
- readonly maxInferenceCredits: 5000;
298
+ readonly maxTotalCredits: 1000;
299
+ readonly maxInferenceCredits: 500;
300
300
  readonly maxUpstreamCostUsd: 10;
301
301
  readonly maxUpstreamCostUsdByok: 250;
302
302
  };
303
303
  readonly copilot: {
304
- readonly maxSessionBudgetCredits: 5000;
305
- readonly defaultPerTurnCreditCeiling: 1500;
304
+ readonly maxSessionBudgetCredits: 500;
305
+ readonly defaultPerTurnCreditCeiling: 150;
306
306
  };
307
307
  readonly signals: {
308
308
  readonly maxEvents: 200;
@@ -405,14 +405,14 @@ export declare const PLAN_LIMITS: {
405
405
  readonly maxDirectCsvBodyBytes: 10000000;
406
406
  };
407
407
  readonly agents: {
408
- readonly maxTotalCredits: 25000;
409
- readonly maxInferenceCredits: 12500;
408
+ readonly maxTotalCredits: 2500;
409
+ readonly maxInferenceCredits: 1250;
410
410
  readonly maxUpstreamCostUsd: 25;
411
411
  readonly maxUpstreamCostUsdByok: 250;
412
412
  };
413
413
  readonly copilot: {
414
- readonly maxSessionBudgetCredits: 10000;
415
- readonly defaultPerTurnCreditCeiling: 2500;
414
+ readonly maxSessionBudgetCredits: 1000;
415
+ readonly defaultPerTurnCreditCeiling: 250;
416
416
  };
417
417
  readonly signals: {
418
418
  readonly maxEvents: 200;
@@ -515,14 +515,14 @@ export declare const PLAN_LIMITS: {
515
515
  readonly maxDirectCsvBodyBytes: 10000000;
516
516
  };
517
517
  readonly agents: {
518
- readonly maxTotalCredits: 50000;
519
- readonly maxInferenceCredits: 25000;
518
+ readonly maxTotalCredits: 5000;
519
+ readonly maxInferenceCredits: 2500;
520
520
  readonly maxUpstreamCostUsd: 50;
521
521
  readonly maxUpstreamCostUsdByok: 250;
522
522
  };
523
523
  readonly copilot: {
524
- readonly maxSessionBudgetCredits: 20000;
525
- readonly defaultPerTurnCreditCeiling: 5000;
524
+ readonly maxSessionBudgetCredits: 2000;
525
+ readonly defaultPerTurnCreditCeiling: 500;
526
526
  };
527
527
  readonly signals: {
528
528
  readonly maxEvents: 200;
@@ -625,14 +625,14 @@ export declare const PLAN_LIMITS: {
625
625
  readonly maxDirectCsvBodyBytes: 10000000;
626
626
  };
627
627
  readonly agents: {
628
- readonly maxTotalCredits: 100000;
629
- readonly maxInferenceCredits: 50000;
628
+ readonly maxTotalCredits: 10000;
629
+ readonly maxInferenceCredits: 5000;
630
630
  readonly maxUpstreamCostUsd: 100;
631
631
  readonly maxUpstreamCostUsdByok: 250;
632
632
  };
633
633
  readonly copilot: {
634
- readonly maxSessionBudgetCredits: 50000;
635
- readonly defaultPerTurnCreditCeiling: 10000;
634
+ readonly maxSessionBudgetCredits: 5000;
635
+ readonly defaultPerTurnCreditCeiling: 1000;
636
636
  };
637
637
  readonly signals: {
638
638
  readonly maxEvents: 200;
@@ -645,8 +645,9 @@ export declare const PLAN_LIMITS: {
645
645
  };
646
646
  /**
647
647
  * Map a raw plan-tier string (subscription `tier`, metadata tier, legacy plan
648
- * key like `growth_250`) onto its enforcement rung. Unknown/absent tiers land
649
- * on the unentitled "free" rung; enterprise shares "scale".
648
+ * key like `growth_250`, or an Oxygen rung key like `oxygen_499`) onto its
649
+ * enforcement rung. Unknown/absent tiers land on the unentitled "free" rung;
650
+ * enterprise shares "scale".
650
651
  */
651
652
  export declare function limitsTierForPlanTier(tier: string | null | undefined): LimitsTier;
652
653
  export declare function planLimitsForTier(tier: LimitsTier): PlanLimits;
@@ -70,8 +70,8 @@ export const PLAN_LIMITS = {
70
70
  // is bounded by its balance, not by these. The intent is only that no single
71
71
  // run can silently eat a first impression.
72
72
  agents: {
73
- maxTotalCredits: 2_500,
74
- maxInferenceCredits: 1_500,
73
+ maxTotalCredits: 250,
74
+ maxInferenceCredits: 150,
75
75
  maxUpstreamCostUsd: 10,
76
76
  maxUpstreamCostUsdByok: 10,
77
77
  },
@@ -80,7 +80,7 @@ export const PLAN_LIMITS = {
80
80
  // path over the CLI must be able to complete one real session on the base
81
81
  // grant alone. Lowering this would re-create the dead-on-arrival Copilot the
82
82
  // split grant exists to prevent.
83
- copilot: { maxSessionBudgetCredits: 5_000, defaultPerTurnCreditCeiling: 1_500 },
83
+ copilot: { maxSessionBudgetCredits: 500, defaultPerTurnCreditCeiling: 150 },
84
84
  signals: { maxEvents: 100, maxWindowDays: 30 },
85
85
  tableActions: { maxActionsPerRun: 10 },
86
86
  },
@@ -108,12 +108,12 @@ export const PLAN_LIMITS = {
108
108
  maxDirectCsvBodyBytes: 10_000_000,
109
109
  },
110
110
  agents: {
111
- maxTotalCredits: 10_000,
112
- maxInferenceCredits: 5_000,
111
+ maxTotalCredits: 1_000,
112
+ maxInferenceCredits: 500,
113
113
  maxUpstreamCostUsd: 10,
114
114
  maxUpstreamCostUsdByok: 250,
115
115
  },
116
- copilot: { maxSessionBudgetCredits: 5_000, defaultPerTurnCreditCeiling: 1_500 },
116
+ copilot: { maxSessionBudgetCredits: 500, defaultPerTurnCreditCeiling: 150 },
117
117
  signals: { maxEvents: 200, maxWindowDays: 90 },
118
118
  tableActions: { maxActionsPerRun: 25 },
119
119
  },
@@ -141,12 +141,12 @@ export const PLAN_LIMITS = {
141
141
  maxDirectCsvBodyBytes: 10_000_000,
142
142
  },
143
143
  agents: {
144
- maxTotalCredits: 25_000,
145
- maxInferenceCredits: 12_500,
144
+ maxTotalCredits: 2_500,
145
+ maxInferenceCredits: 1_250,
146
146
  maxUpstreamCostUsd: 25,
147
147
  maxUpstreamCostUsdByok: 250,
148
148
  },
149
- copilot: { maxSessionBudgetCredits: 10_000, defaultPerTurnCreditCeiling: 2_500 },
149
+ copilot: { maxSessionBudgetCredits: 1_000, defaultPerTurnCreditCeiling: 250 },
150
150
  signals: { maxEvents: 200, maxWindowDays: 180 },
151
151
  tableActions: { maxActionsPerRun: 25 },
152
152
  },
@@ -174,12 +174,12 @@ export const PLAN_LIMITS = {
174
174
  maxDirectCsvBodyBytes: 10_000_000,
175
175
  },
176
176
  agents: {
177
- maxTotalCredits: 50_000,
178
- maxInferenceCredits: 25_000,
177
+ maxTotalCredits: 5_000,
178
+ maxInferenceCredits: 2_500,
179
179
  maxUpstreamCostUsd: 50,
180
180
  maxUpstreamCostUsdByok: 250,
181
181
  },
182
- copilot: { maxSessionBudgetCredits: 20_000, defaultPerTurnCreditCeiling: 5_000 },
182
+ copilot: { maxSessionBudgetCredits: 2_000, defaultPerTurnCreditCeiling: 500 },
183
183
  signals: { maxEvents: 200, maxWindowDays: 365 },
184
184
  tableActions: { maxActionsPerRun: 25 },
185
185
  },
@@ -207,27 +207,54 @@ export const PLAN_LIMITS = {
207
207
  maxDirectCsvBodyBytes: 10_000_000,
208
208
  },
209
209
  agents: {
210
- maxTotalCredits: 100_000,
211
- maxInferenceCredits: 50_000,
210
+ maxTotalCredits: 10_000,
211
+ maxInferenceCredits: 5_000,
212
212
  maxUpstreamCostUsd: 100,
213
213
  maxUpstreamCostUsdByok: 250,
214
214
  },
215
- copilot: { maxSessionBudgetCredits: 50_000, defaultPerTurnCreditCeiling: 10_000 },
215
+ copilot: { maxSessionBudgetCredits: 5_000, defaultPerTurnCreditCeiling: 1_000 },
216
216
  signals: { maxEvents: 200, maxWindowDays: 365 },
217
217
  tableActions: { maxActionsPerRun: 25 },
218
218
  },
219
219
  };
220
+ /**
221
+ * Which enforcement rung a rung of the six-rung Oxygen plan lands on.
222
+ *
223
+ * The rungs share one `oxygen` tier, so the ladder has to be re-derived from
224
+ * price. Deliberately scoped to `oxygen` rather than applied to every plan: a
225
+ * blanket price band would regress the grandfathered plans, demoting `pro_149`
226
+ * ($149) to "starter" and `team_399` ($399) to "pro".
227
+ */
228
+ function oxygenRungLimitsTier(monthlyPriceCents) {
229
+ if (monthlyPriceCents === null)
230
+ return "starter";
231
+ // Thresholds track the ladder these rungs replaced, so nobody's enforcement
232
+ // rung moves for a price they were already paying: Starter was $99, Pro $249,
233
+ // Team $749. A rung sits on the rung of the old plan at or below its price —
234
+ // $499 is below the old Team, so it enforces as "pro".
235
+ if (monthlyPriceCents >= 199_900)
236
+ return "scale";
237
+ if (monthlyPriceCents >= 99_900)
238
+ return "team";
239
+ if (monthlyPriceCents >= 19_900)
240
+ return "pro";
241
+ return "starter";
242
+ }
220
243
  /**
221
244
  * Map a raw plan-tier string (subscription `tier`, metadata tier, legacy plan
222
- * key like `growth_250`) onto its enforcement rung. Unknown/absent tiers land
223
- * on the unentitled "free" rung; enterprise shares "scale".
245
+ * key like `growth_250`, or an Oxygen rung key like `oxygen_499`) onto its
246
+ * enforcement rung. Unknown/absent tiers land on the unentitled "free" rung;
247
+ * enterprise shares "scale".
224
248
  */
225
249
  export function limitsTierForPlanTier(tier) {
226
- const base = resolveBasePricingPlan(tier)?.tier;
250
+ const plan = resolveBasePricingPlan(tier);
251
+ const base = plan?.tier;
227
252
  if (!base || base === "free")
228
253
  return "free";
229
254
  if (base === "enterprise")
230
255
  return "scale";
256
+ if (base === "oxygen")
257
+ return oxygenRungLimitsTier(plan?.monthlyPriceCents ?? null);
231
258
  return base;
232
259
  }
233
260
  export function planLimitsForTier(tier) {