@oxygen-agent/cli 1.365.3 → 1.575.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/column-run-notices.d.ts +11 -0
- package/dist/column-run-notices.js +37 -0
- package/dist/command-manifest.js +13 -8
- package/dist/help.js +78 -16
- package/dist/index.js +3873 -514
- package/dist/skills.js +106 -1
- package/node_modules/@oxygen/formula/dist/coerce.d.ts +8 -0
- package/node_modules/@oxygen/formula/dist/coerce.js +10 -0
- package/node_modules/@oxygen/formula/dist/evaluate.d.ts +31 -0
- package/node_modules/@oxygen/formula/dist/evaluate.js +248 -0
- package/node_modules/@oxygen/formula/dist/expression.d.ts +64 -0
- package/node_modules/@oxygen/formula/dist/expression.js +428 -0
- package/node_modules/@oxygen/formula/dist/formula-functions.d.ts +71 -0
- package/node_modules/@oxygen/formula/dist/formula-functions.js +1100 -0
- package/node_modules/@oxygen/formula/dist/index.d.ts +17 -0
- package/node_modules/@oxygen/formula/dist/index.js +17 -0
- package/node_modules/@oxygen/formula/dist/value-normalizers.d.ts +30 -0
- package/node_modules/@oxygen/formula/dist/value-normalizers.js +80 -0
- package/node_modules/@oxygen/formula/package.json +26 -0
- package/node_modules/@oxygen/recipe-sdk/dist/index.d.ts +30 -0
- package/node_modules/@oxygen/recipe-sdk/dist/index.js +2 -2
- package/node_modules/@oxygen/shared/dist/billing-anchors.d.ts +60 -0
- package/node_modules/@oxygen/shared/dist/billing-anchors.js +135 -0
- package/node_modules/@oxygen/shared/dist/billing.d.ts +101 -5
- package/node_modules/@oxygen/shared/dist/billing.js +192 -8
- package/node_modules/@oxygen/shared/dist/call-outcomes.d.ts +59 -0
- package/node_modules/@oxygen/shared/dist/call-outcomes.js +73 -0
- package/node_modules/@oxygen/shared/dist/cli-result.js +1 -0
- package/node_modules/@oxygen/shared/dist/credit-guidance.js +3 -1
- package/node_modules/@oxygen/shared/dist/crm-reply-events.d.ts +35 -0
- package/node_modules/@oxygen/shared/dist/crm-reply-events.js +31 -0
- package/node_modules/@oxygen/shared/dist/dial-guardrail-overrides.d.ts +50 -0
- package/node_modules/@oxygen/shared/dist/dial-guardrail-overrides.js +65 -0
- package/node_modules/@oxygen/shared/dist/directory.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/directory.js +1 -0
- package/node_modules/@oxygen/shared/dist/hosted-ai.d.ts +17 -1
- package/node_modules/@oxygen/shared/dist/hosted-ai.js +52 -3
- package/node_modules/@oxygen/shared/dist/index.d.ts +11 -0
- package/node_modules/@oxygen/shared/dist/index.js +15 -0
- package/node_modules/@oxygen/shared/dist/langfuse.d.ts +77 -0
- package/node_modules/@oxygen/shared/dist/langfuse.js +231 -0
- package/node_modules/@oxygen/shared/dist/linkedin-quota-denial.d.ts +31 -0
- package/node_modules/@oxygen/shared/dist/linkedin-quota-denial.js +56 -0
- package/node_modules/@oxygen/shared/dist/linkedin-sequences.d.ts +5 -4
- package/node_modules/@oxygen/shared/dist/linkedin-sequences.js +5 -4
- package/node_modules/@oxygen/shared/dist/linkedin-url.d.ts +22 -0
- package/node_modules/@oxygen/shared/dist/linkedin-url.js +7 -4
- package/node_modules/@oxygen/shared/dist/log.js +56 -4
- package/node_modules/@oxygen/shared/dist/microsoft-consent-url.d.ts +7 -0
- package/node_modules/@oxygen/shared/dist/microsoft-consent-url.js +29 -0
- package/node_modules/@oxygen/shared/dist/object-storage.d.ts +31 -0
- package/node_modules/@oxygen/shared/dist/object-storage.js +61 -0
- package/node_modules/@oxygen/shared/dist/plan-limits.d.ts +636 -0
- package/node_modules/@oxygen/shared/dist/plan-limits.js +199 -0
- package/node_modules/@oxygen/shared/dist/pricing-sheet.d.ts +89 -23
- package/node_modules/@oxygen/shared/dist/pricing-sheet.js +88 -24
- package/node_modules/@oxygen/shared/dist/sequence-crm-events.d.ts +291 -0
- package/node_modules/@oxygen/shared/dist/sequence-crm-events.js +224 -0
- package/node_modules/@oxygen/shared/dist/sequence-template.d.ts +42 -1
- package/node_modules/@oxygen/shared/dist/sequence-template.js +0 -0
- package/node_modules/@oxygen/shared/dist/sequences.d.ts +287 -24
- package/node_modules/@oxygen/shared/dist/sequences.js +940 -60
- package/node_modules/@oxygen/shared/dist/spend-safety.d.ts +70 -0
- package/node_modules/@oxygen/shared/dist/spend-safety.js +106 -0
- package/node_modules/@oxygen/shared/dist/tags.d.ts +90 -1
- package/node_modules/@oxygen/shared/dist/tags.js +126 -6
- package/node_modules/@oxygen/shared/dist/version.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.js +1 -1
- package/node_modules/@oxygen/shared/dist/workflow-trigger-metadata.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/workflow-trigger-metadata.js +4 -0
- package/node_modules/@oxygen/shared/dist/workspace-agents.d.ts +8 -7
- package/node_modules/@oxygen/shared/dist/workspace-agents.js +34 -7
- package/node_modules/@oxygen/shared/package.json +95 -0
- package/node_modules/@oxygen/workflows/dist/event-dispatch.d.ts +126 -0
- package/node_modules/@oxygen/workflows/dist/event-dispatch.js +173 -0
- package/node_modules/@oxygen/workflows/dist/graph/expression.d.ts +78 -0
- package/node_modules/@oxygen/workflows/dist/graph/expression.js +700 -0
- package/node_modules/@oxygen/workflows/dist/graph/index.d.ts +20 -0
- package/node_modules/@oxygen/workflows/dist/graph/index.js +20 -0
- package/node_modules/@oxygen/workflows/dist/graph/lint.d.ts +4 -0
- package/node_modules/@oxygen/workflows/dist/graph/lint.js +812 -0
- package/node_modules/@oxygen/workflows/dist/graph/manifest-schema.d.ts +501 -0
- package/node_modules/@oxygen/workflows/dist/graph/manifest-schema.js +200 -0
- package/node_modules/@oxygen/workflows/dist/graph/params.d.ts +86 -0
- package/node_modules/@oxygen/workflows/dist/graph/params.js +173 -0
- package/node_modules/@oxygen/workflows/dist/graph/remap.d.ts +48 -0
- package/node_modules/@oxygen/workflows/dist/graph/remap.js +213 -0
- package/node_modules/@oxygen/workflows/dist/graph/topology.d.ts +46 -0
- package/node_modules/@oxygen/workflows/dist/graph/topology.js +280 -0
- package/node_modules/@oxygen/workflows/dist/graph/types.d.ts +270 -0
- package/node_modules/@oxygen/workflows/dist/graph/types.js +93 -0
- package/node_modules/@oxygen/workflows/dist/index.d.ts +113 -1
- package/node_modules/@oxygen/workflows/dist/index.js +179 -13
- package/node_modules/@oxygen/workflows/dist/tool-effects.d.ts +1 -0
- package/node_modules/@oxygen/workflows/dist/tool-effects.js +19 -0
- package/node_modules/@oxygen/workflows/dist/usage-estimate.js +135 -4
- package/node_modules/@oxygen/workflows/package.json +4 -0
- package/package.json +10 -5
|
@@ -57,9 +57,50 @@ function reserveSurfaceDimension(merged, ctx) {
|
|
|
57
57
|
surface: ctx.surface,
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
|
-
|
|
60
|
+
// The sink registration must live on globalThis, not in a module-local variable:
|
|
61
|
+
// Next.js compiles instrumentation.ts and each route handler into separate
|
|
62
|
+
// bundles, each with its OWN instance of this module. A module-local set by the
|
|
63
|
+
// web log shipper (which starts in instrumentation) is invisible to route
|
|
64
|
+
// handlers — exactly why only startup-context lines (the shipper beacon, the
|
|
65
|
+
// framework console filter) ever shipped flat while 4k+ route-emitted
|
|
66
|
+
// error/warn lines/day stayed drain-only (2026-07-21). globalThis is the one
|
|
67
|
+
// object all bundle copies share; Symbol.for gives every copy the same slot.
|
|
68
|
+
// Same trap + same fix as linkedin-quota's enforcer registration.
|
|
69
|
+
const LOG_SINK_SLOT = Symbol.for("oxygen.log.sink");
|
|
61
70
|
export function setLogSink(sink) {
|
|
62
|
-
|
|
71
|
+
globalThis[LOG_SINK_SLOT] = sink;
|
|
72
|
+
}
|
|
73
|
+
function getLogSink() {
|
|
74
|
+
return globalThis[LOG_SINK_SLOT] ?? null;
|
|
75
|
+
}
|
|
76
|
+
const LEVEL_RANK = { debug: 10, info: 20, warn: 30, error: 40 };
|
|
77
|
+
/**
|
|
78
|
+
* Minimum level that reaches the Axiom SINK. stdout is deliberately untouched —
|
|
79
|
+
* `flyctl logs`, `vercel logs`, and local runs keep every line, so nothing becomes
|
|
80
|
+
* un-debuggable; this only decides what we pay to store and query.
|
|
81
|
+
*
|
|
82
|
+
* Default `info`, i.e. debug never ships. Until 2026-07-31 `log()` had no level gate
|
|
83
|
+
* at all, so debug lines were shipped exactly like info — which contradicted the code
|
|
84
|
+
* that chose debug specifically to stay quiet (see the `work_queue.wake.failed` call
|
|
85
|
+
* in packages/work-queue/src/wake-client.ts: "Log at debug level so this doesn't
|
|
86
|
+
* generate noise during deploys" — it generated 10k rows/week regardless). Measured
|
|
87
|
+
* the day this landed: debug was 97,704 rows/day of `oxygen-logs`, 24% of all
|
|
88
|
+
* in-process rows, and 98% of that was a single `crm_person_photo_cycle_skipped`
|
|
89
|
+
* line reporting that nothing happened.
|
|
90
|
+
*
|
|
91
|
+
* Set `OXYGEN_LOG_SHIP_MIN_LEVEL=debug` to restore the old behaviour (e.g. while
|
|
92
|
+
* debugging a specific incident), or raise it to `warn` to ship only actionable lines.
|
|
93
|
+
*/
|
|
94
|
+
function shipMinRank() {
|
|
95
|
+
const configured = process.env.OXYGEN_LOG_SHIP_MIN_LEVEL?.trim().toLowerCase();
|
|
96
|
+
if (configured
|
|
97
|
+
&& Object.prototype.hasOwnProperty.call(LEVEL_RANK, configured)) {
|
|
98
|
+
return LEVEL_RANK[configured];
|
|
99
|
+
}
|
|
100
|
+
return LEVEL_RANK.info;
|
|
101
|
+
}
|
|
102
|
+
function shouldShip(level) {
|
|
103
|
+
return LEVEL_RANK[level] >= shipMinRank();
|
|
63
104
|
}
|
|
64
105
|
export function log(level, msg, fields) {
|
|
65
106
|
const context = getLogContext();
|
|
@@ -89,6 +130,9 @@ export function log(level, msg, fields) {
|
|
|
89
130
|
else {
|
|
90
131
|
console.log(line);
|
|
91
132
|
}
|
|
133
|
+
if (!shouldShip(level))
|
|
134
|
+
return;
|
|
135
|
+
const logSink = getLogSink();
|
|
92
136
|
if (!logSink)
|
|
93
137
|
return;
|
|
94
138
|
try {
|
|
@@ -118,8 +162,16 @@ function hashErrorKey(input) {
|
|
|
118
162
|
export function errorId(err) {
|
|
119
163
|
if (!(err instanceof Error))
|
|
120
164
|
return "non_error";
|
|
121
|
-
|
|
122
|
-
|
|
165
|
+
// A drizzle DrizzleQueryError carries the bound parameter values inside its
|
|
166
|
+
// own message (`Failed query: <sql>\nparams: <values>`), which also makes
|
|
167
|
+
// stack line 1 the `params:` continuation instead of a frame. Hashing either
|
|
168
|
+
// raw gave every repetition of one DB failure its own id — 101 identical
|
|
169
|
+
// oxygen_lead_db_cache.write_failed events grouped as 101 buckets of one, and
|
|
170
|
+
// the hash input was derived from customer row values (OXY-46). Redact the
|
|
171
|
+
// params tail and hash the first real frame so DB errors group again.
|
|
172
|
+
const message = redactSqlParameters(err.message);
|
|
173
|
+
const firstFrame = (err.stack ?? "").split("\n").find((line) => /^\s+at\s/.test(line)) ?? "";
|
|
174
|
+
return hashErrorKey(`${err.name}:${message}:${firstFrame}`);
|
|
123
175
|
}
|
|
124
176
|
export function errorFields(err) {
|
|
125
177
|
if (err instanceof Error) {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize a Microsoft Entra admin-consent URL for an operator handoff.
|
|
3
|
+
*
|
|
4
|
+
* Provider responses are external input. Only Microsoft's documented HTTPS
|
|
5
|
+
* login host and either provider-supported admin-consent path may become clickable.
|
|
6
|
+
*/
|
|
7
|
+
export declare function normalizeMicrosoftAdminConsentUrl(value: unknown): string | null;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const MICROSOFT_LOGIN_HOST = "login.microsoftonline.com";
|
|
2
|
+
const ADMIN_CONSENT_PATH = /^\/(?:[^/]+\/)?adminconsent\/?$/i;
|
|
3
|
+
/**
|
|
4
|
+
* Normalize a Microsoft Entra admin-consent URL for an operator handoff.
|
|
5
|
+
*
|
|
6
|
+
* Provider responses are external input. Only Microsoft's documented HTTPS
|
|
7
|
+
* login host and either provider-supported admin-consent path may become clickable.
|
|
8
|
+
*/
|
|
9
|
+
export function normalizeMicrosoftAdminConsentUrl(value) {
|
|
10
|
+
if (typeof value !== "string" || value.trim() === "")
|
|
11
|
+
return null;
|
|
12
|
+
let url;
|
|
13
|
+
try {
|
|
14
|
+
url = new URL(value.trim());
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
if (url.protocol !== "https:" ||
|
|
20
|
+
url.hostname.toLowerCase() !== MICROSOFT_LOGIN_HOST ||
|
|
21
|
+
url.username !== "" ||
|
|
22
|
+
url.password !== "" ||
|
|
23
|
+
url.port !== "" ||
|
|
24
|
+
url.hash !== "" ||
|
|
25
|
+
!ADMIN_CONSENT_PATH.test(url.pathname)) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
return url.toString();
|
|
29
|
+
}
|
|
@@ -7,7 +7,12 @@ export declare function buildPublishingMediaObjectKey(input: {
|
|
|
7
7
|
organizationId: string;
|
|
8
8
|
fileName?: string | null;
|
|
9
9
|
}): string;
|
|
10
|
+
export declare function buildCopilotAttachmentObjectKey(input: {
|
|
11
|
+
organizationId: string;
|
|
12
|
+
fileName?: string | null;
|
|
13
|
+
}): string;
|
|
10
14
|
export declare function isImportObjectKeyForOrganization(key: string, organizationId: string): boolean;
|
|
15
|
+
export declare function isCopilotAttachmentObjectKeyForOrganization(key: string, organizationId: string): boolean;
|
|
11
16
|
export declare function isPublishingMediaObjectKeyForOrganization(key: string, organizationId: string): boolean;
|
|
12
17
|
export type PresignedImportUpload = {
|
|
13
18
|
uploadUrl: string;
|
|
@@ -30,6 +35,32 @@ export declare function presignPublishingMediaUpload(input: {
|
|
|
30
35
|
contentType?: string | null;
|
|
31
36
|
contentLength: number;
|
|
32
37
|
}): Promise<PresignedPublishingMediaUpload>;
|
|
38
|
+
export declare function presignCopilotAttachmentUpload(input: {
|
|
39
|
+
organizationId: string;
|
|
40
|
+
fileName?: string | null;
|
|
41
|
+
contentType?: string | null;
|
|
42
|
+
contentLength: number;
|
|
43
|
+
}): Promise<PresignedImportUpload>;
|
|
44
|
+
/**
|
|
45
|
+
* Read an uploaded attachment back into memory. Bounded by `maxBytes` because
|
|
46
|
+
* the callers are a CSV header sniff and a text extraction, neither of which
|
|
47
|
+
* needs the whole object — and a 400MB upload must not become a 400MB buffer in
|
|
48
|
+
* a Vercel handler.
|
|
49
|
+
*/
|
|
50
|
+
export declare function readCopilotAttachmentObject(input: {
|
|
51
|
+
storageKey: string;
|
|
52
|
+
maxBytes: number;
|
|
53
|
+
}): Promise<{
|
|
54
|
+
body: Uint8Array;
|
|
55
|
+
contentType: string | null;
|
|
56
|
+
truncated: boolean;
|
|
57
|
+
}>;
|
|
58
|
+
export declare function getCopilotAttachmentObjectMetadata(input: {
|
|
59
|
+
storageKey: string;
|
|
60
|
+
}): Promise<{
|
|
61
|
+
contentLength: number | null;
|
|
62
|
+
contentType: string | null;
|
|
63
|
+
}>;
|
|
33
64
|
export declare function getImportObjectMetadata(input: {
|
|
34
65
|
storageKey: string;
|
|
35
66
|
}): Promise<{
|
|
@@ -65,9 +65,16 @@ export function buildPublishingMediaObjectKey(input) {
|
|
|
65
65
|
const safeName = sanitizeFileName(input.fileName) || "media";
|
|
66
66
|
return `publishing-media/${input.organizationId}/${randomUUID()}/${safeName}`;
|
|
67
67
|
}
|
|
68
|
+
export function buildCopilotAttachmentObjectKey(input) {
|
|
69
|
+
const safeName = sanitizeFileName(input.fileName) || "attachment";
|
|
70
|
+
return `copilot/${input.organizationId}/${randomUUID()}/${safeName}`;
|
|
71
|
+
}
|
|
68
72
|
export function isImportObjectKeyForOrganization(key, organizationId) {
|
|
69
73
|
return key.startsWith(`imports/${organizationId}/`);
|
|
70
74
|
}
|
|
75
|
+
export function isCopilotAttachmentObjectKeyForOrganization(key, organizationId) {
|
|
76
|
+
return key.startsWith(`copilot/${organizationId}/`);
|
|
77
|
+
}
|
|
71
78
|
export function isPublishingMediaObjectKeyForOrganization(key, organizationId) {
|
|
72
79
|
return key.startsWith(`publishing-media/${organizationId}/`);
|
|
73
80
|
}
|
|
@@ -115,6 +122,60 @@ export async function presignPublishingMediaUpload(input) {
|
|
|
115
122
|
expiresInSeconds: PRESIGN_EXPIRY_SECONDS,
|
|
116
123
|
};
|
|
117
124
|
}
|
|
125
|
+
export async function presignCopilotAttachmentUpload(input) {
|
|
126
|
+
const { client, config } = resolveClient();
|
|
127
|
+
const storageKey = buildCopilotAttachmentObjectKey({
|
|
128
|
+
organizationId: input.organizationId,
|
|
129
|
+
fileName: input.fileName ?? null,
|
|
130
|
+
});
|
|
131
|
+
const command = new PutObjectCommand({
|
|
132
|
+
Bucket: config.bucket,
|
|
133
|
+
Key: storageKey,
|
|
134
|
+
ContentLength: input.contentLength,
|
|
135
|
+
...(input.contentType ? { ContentType: input.contentType } : {}),
|
|
136
|
+
});
|
|
137
|
+
const uploadUrl = await getSignedUrl(client, command, { expiresIn: PRESIGN_EXPIRY_SECONDS });
|
|
138
|
+
return {
|
|
139
|
+
uploadUrl,
|
|
140
|
+
bucket: config.bucket,
|
|
141
|
+
storageKey,
|
|
142
|
+
contentLength: input.contentLength,
|
|
143
|
+
provider: "s3",
|
|
144
|
+
expiresInSeconds: PRESIGN_EXPIRY_SECONDS,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Read an uploaded attachment back into memory. Bounded by `maxBytes` because
|
|
149
|
+
* the callers are a CSV header sniff and a text extraction, neither of which
|
|
150
|
+
* needs the whole object — and a 400MB upload must not become a 400MB buffer in
|
|
151
|
+
* a Vercel handler.
|
|
152
|
+
*/
|
|
153
|
+
export async function readCopilotAttachmentObject(input) {
|
|
154
|
+
const { client, config } = resolveClient();
|
|
155
|
+
const result = await client.send(new GetObjectCommand({
|
|
156
|
+
Bucket: config.bucket,
|
|
157
|
+
Key: input.storageKey,
|
|
158
|
+
// Ask for one byte past the cap so a full read is distinguishable from a
|
|
159
|
+
// file that happens to be exactly maxBytes long.
|
|
160
|
+
Range: `bytes=0-${input.maxBytes}`,
|
|
161
|
+
}));
|
|
162
|
+
const raw = await result.Body?.transformToByteArray();
|
|
163
|
+
const body = raw ?? new Uint8Array();
|
|
164
|
+
const truncated = body.byteLength > input.maxBytes;
|
|
165
|
+
return {
|
|
166
|
+
body: truncated ? body.subarray(0, input.maxBytes) : body,
|
|
167
|
+
contentType: typeof result.ContentType === "string" ? result.ContentType : null,
|
|
168
|
+
truncated,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
export async function getCopilotAttachmentObjectMetadata(input) {
|
|
172
|
+
const { client, config } = resolveClient();
|
|
173
|
+
const result = await client.send(new HeadObjectCommand({ Bucket: config.bucket, Key: input.storageKey }));
|
|
174
|
+
return {
|
|
175
|
+
contentLength: typeof result.ContentLength === "number" ? result.ContentLength : null,
|
|
176
|
+
contentType: typeof result.ContentType === "string" ? result.ContentType : null,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
118
179
|
export async function getImportObjectMetadata(input) {
|
|
119
180
|
const { client, config } = resolveClient();
|
|
120
181
|
const result = await client.send(new HeadObjectCommand({ Bucket: config.bucket, Key: input.storageKey }));
|