@oxygen-agent/cli 1.162.10 → 1.177.0

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 (38) hide show
  1. package/README.md +1 -1
  2. package/dist/http-client.js +2 -78
  3. package/dist/index.js +1312 -527
  4. package/dist/run-wait.d.ts +23 -0
  5. package/dist/run-wait.js +57 -0
  6. package/node_modules/@oxygen/recipe-sdk/dist/index.d.ts +0 -5
  7. package/node_modules/@oxygen/shared/dist/cell-format.d.ts +2 -14
  8. package/node_modules/@oxygen/shared/dist/cell-format.js +3 -10
  9. package/node_modules/@oxygen/shared/dist/cli-envelope.d.ts +27 -0
  10. package/node_modules/@oxygen/shared/dist/cli-envelope.js +102 -0
  11. package/node_modules/@oxygen/shared/dist/cli-result.d.ts +39 -0
  12. package/node_modules/@oxygen/shared/dist/cli-result.js +52 -0
  13. package/node_modules/@oxygen/shared/dist/credit-guidance.d.ts +0 -1
  14. package/node_modules/@oxygen/shared/dist/credit-guidance.js +1 -1
  15. package/node_modules/@oxygen/shared/dist/file-import.js +1 -1
  16. package/node_modules/@oxygen/shared/dist/index.d.ts +3 -39
  17. package/node_modules/@oxygen/shared/dist/index.js +3 -44
  18. package/node_modules/@oxygen/shared/dist/log.d.ts +0 -1
  19. package/node_modules/@oxygen/shared/dist/log.js +8 -3
  20. package/node_modules/@oxygen/shared/dist/object-storage.d.ts +0 -3
  21. package/node_modules/@oxygen/shared/dist/object-storage.js +1 -24
  22. package/node_modules/@oxygen/shared/dist/redaction.js +45 -4
  23. package/node_modules/@oxygen/shared/dist/search-vocab.d.ts +18 -0
  24. package/node_modules/@oxygen/shared/dist/search-vocab.js +151 -0
  25. package/node_modules/@oxygen/shared/dist/select-options.d.ts +18 -0
  26. package/node_modules/@oxygen/shared/dist/select-options.js +121 -0
  27. package/node_modules/@oxygen/shared/dist/sequences.js +1 -1
  28. package/node_modules/@oxygen/shared/dist/sql-error.d.ts +0 -6
  29. package/node_modules/@oxygen/shared/dist/sql-error.js +67 -58
  30. package/node_modules/@oxygen/shared/dist/telemetry.d.ts +0 -1
  31. package/node_modules/@oxygen/shared/dist/telemetry.js +23 -18
  32. package/node_modules/@oxygen/shared/dist/version.d.ts +1 -1
  33. package/node_modules/@oxygen/shared/dist/version.js +1 -1
  34. package/node_modules/@oxygen/shared/dist/worker-failures-queue.d.ts +22 -0
  35. package/node_modules/@oxygen/shared/dist/worker-failures-queue.js +56 -0
  36. package/node_modules/@oxygen/workflows/dist/index.d.ts +12 -11
  37. package/node_modules/@oxygen/workflows/dist/index.js +58 -0
  38. package/package.json +1 -1
@@ -5,6 +5,8 @@ import { redactSqlParameters, sqlErrorTelemetryAttributes } from "./sql-error.js
5
5
  import { OXYGEN_VERSION } from "./version.js";
6
6
  const counterCache = new Map();
7
7
  const histogramCache = new Map();
8
+ // skipcq: JS-0116 — `async` keeps synchronous setup throws (getTracer / attribute
9
+ // normalization) as rejections so the Promise<T> contract holds for all callers.
8
10
  export async function withTelemetrySpan(tracerName, name, attributes, fn, options) {
9
11
  const tracer = trace.getTracer(tracerName, OXYGEN_VERSION);
10
12
  return tracer.startActiveSpan(name, { attributes: normalizeTelemetryAttributes(commonTelemetryAttributes(attributes)) }, async (span) => {
@@ -51,12 +53,6 @@ export function markActiveTelemetryError(message, attributes) {
51
53
  span.setAttributes(normalizeTelemetryAttributes(attributes));
52
54
  }
53
55
  }
54
- export function addTelemetryEvent(name, attributes) {
55
- const span = trace.getActiveSpan();
56
- if (!span)
57
- return;
58
- span.addEvent(name, normalizeTelemetryAttributes(attributes));
59
- }
60
56
  export function recordTelemetryCounter(name, value = 1, attributes) {
61
57
  if (!Number.isFinite(value))
62
58
  return;
@@ -71,28 +67,37 @@ export function recordTelemetryHistogram(name, value, attributes) {
71
67
  }
72
68
  export function commonTelemetryAttributes(attributes) {
73
69
  const workerProcess = isWorkerProcess();
70
+ // Each attribute reads an ordered env-var fallback chain that differs by
71
+ // surface: the worker prefers its Fly vars first, the web its Vercel vars.
72
+ // pickEnvBySurface picks the surface's list, then the first defined value.
73
+ const pick = (workerKeys, webKeys) => pickEnvBySurface(workerProcess, workerKeys, webKeys);
74
74
  return {
75
75
  "oxygen.version": OXYGEN_VERSION,
76
- "deployment.environment": workerProcess
77
- ? process.env.FLY_ENVIRONMENT ?? process.env.NODE_ENV ?? null
78
- : process.env.VERCEL_ENV ?? process.env.NODE_ENV ?? null,
76
+ "deployment.environment": pick(["FLY_ENVIRONMENT", "NODE_ENV"], ["VERCEL_ENV", "NODE_ENV"]),
79
77
  // deployment.sha stays the deploy artifact id: the Fly registry image ref on
80
78
  // the worker (release cross-reference), VERCEL_GIT_COMMIT_SHA on the web. The
81
79
  // worker's image ref is NOT a git commit, so deployment.git_sha carries the
82
80
  // resolved commit separately and is the single field that means "the commit"
83
81
  // on both surfaces (OXY-61). On the web both happen to equal the Vercel SHA.
84
- "deployment.sha": workerProcess
85
- ? process.env.FLY_IMAGE_REF ?? process.env.VERCEL_GIT_COMMIT_SHA ?? null
86
- : process.env.VERCEL_GIT_COMMIT_SHA ?? process.env.FLY_IMAGE_REF ?? null,
87
- "deployment.git_sha": workerProcess
88
- ? process.env.OXYGEN_GIT_SHA ?? process.env.VERCEL_GIT_COMMIT_SHA ?? null
89
- : process.env.VERCEL_GIT_COMMIT_SHA ?? process.env.OXYGEN_GIT_SHA ?? null,
90
- "cloud.region": workerProcess
91
- ? process.env.FLY_REGION ?? process.env.VERCEL_REGION ?? null
92
- : process.env.VERCEL_REGION ?? process.env.FLY_REGION ?? null,
82
+ "deployment.sha": pick(["FLY_IMAGE_REF", "VERCEL_GIT_COMMIT_SHA"], ["VERCEL_GIT_COMMIT_SHA", "FLY_IMAGE_REF"]),
83
+ "deployment.git_sha": pick(["OXYGEN_GIT_SHA", "VERCEL_GIT_COMMIT_SHA"], ["VERCEL_GIT_COMMIT_SHA", "OXYGEN_GIT_SHA"]),
84
+ "cloud.region": pick(["FLY_REGION", "VERCEL_REGION"], ["VERCEL_REGION", "FLY_REGION"]),
93
85
  ...attributes,
94
86
  };
95
87
  }
88
+ // Picks the env-var fallback list for the active surface (worker vs web), then
89
+ // returns the first env var in that list that is set, else null. Preserves the
90
+ // original `a ?? b ?? null` semantics exactly: only an unset (`undefined`) env
91
+ // var falls through to the next key — an explicit empty string is kept.
92
+ function pickEnvBySurface(workerProcess, workerKeys, webKeys) {
93
+ const keys = workerProcess ? workerKeys : webKeys;
94
+ for (const key of keys) {
95
+ const value = process.env[key];
96
+ if (value !== undefined)
97
+ return value;
98
+ }
99
+ return null;
100
+ }
96
101
  function isWorkerProcess() {
97
102
  if (process.env.OXYGEN_PROCESS_ROLE === "worker")
98
103
  return true;
@@ -1,2 +1,2 @@
1
- export declare const OXYGEN_VERSION = "1.162.10";
1
+ export declare const OXYGEN_VERSION = "1.177.0";
2
2
  export declare const OXYGEN_MINIMUM_CLI_VERSION = "1.154.0";
@@ -1,4 +1,4 @@
1
- export const OXYGEN_VERSION = "1.162.10";
1
+ export const OXYGEN_VERSION = "1.177.0";
2
2
  // Bump this only when deployed CLI/API contracts require a newer CLI.
3
3
  // 1.154.0: LinkedIn → Sequencer rename moved the CLI/API/MCP surface
4
4
  // (oxygen sequences|inbox|senders, /api/cli/{sequences,inbox,senders}) and
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Canonical queue selector for the worker-failures surface, shared by:
3
+ * - CLI `oxygen worker failures --queue <queue>` (free-text, passed through),
4
+ * - MCP `oxygen_worker_failures({ queue })` (enum-constrained), and
5
+ * - the `/api/cli/worker/failures` route that actually parses the value.
6
+ *
7
+ * Single-sourced so a token documented by one surface can never silently fall
8
+ * back to the `all` aggregate on another — the exact drift OXY-337 fixed, where
9
+ * the CLI documented `postgres_jobs` but the route only accepted `postgres_queue`.
10
+ */
11
+ /**
12
+ * Public, documented queue tokens in display order. `postgres_queue` is the
13
+ * canonical name for the Postgres-backed background job queue. The MCP enum is
14
+ * built from this list so the constraint can never drift from the parser.
15
+ */
16
+ export declare const WORKER_FAILURES_QUEUE_VALUES: readonly ["all", "actions", "ingestions", "postgres_queue"];
17
+ export type WorkerFailuresQueue = (typeof WORKER_FAILURES_QUEUE_VALUES)[number];
18
+ /**
19
+ * Normalize a raw `--queue` / `queue` value to its canonical token. Empty,
20
+ * missing, or unrecognized values resolve to `all` (the aggregate default).
21
+ */
22
+ export declare function normalizeWorkerFailuresQueue(value: string | null | undefined): WorkerFailuresQueue;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Canonical queue selector for the worker-failures surface, shared by:
3
+ * - CLI `oxygen worker failures --queue <queue>` (free-text, passed through),
4
+ * - MCP `oxygen_worker_failures({ queue })` (enum-constrained), and
5
+ * - the `/api/cli/worker/failures` route that actually parses the value.
6
+ *
7
+ * Single-sourced so a token documented by one surface can never silently fall
8
+ * back to the `all` aggregate on another — the exact drift OXY-337 fixed, where
9
+ * the CLI documented `postgres_jobs` but the route only accepted `postgres_queue`.
10
+ */
11
+ /**
12
+ * Public, documented queue tokens in display order. `postgres_queue` is the
13
+ * canonical name for the Postgres-backed background job queue. The MCP enum is
14
+ * built from this list so the constraint can never drift from the parser.
15
+ */
16
+ export const WORKER_FAILURES_QUEUE_VALUES = [
17
+ "all",
18
+ "actions",
19
+ "ingestions",
20
+ "postgres_queue",
21
+ ];
22
+ /**
23
+ * Every accepted token mapped to its canonical value. Covers the canonical
24
+ * tokens themselves plus legacy/compat aliases:
25
+ * - `postgres_jobs` was the CLI-documented token before this unification.
26
+ * - `bullmq` / `redis` / `jobs` predate the Postgres-queue migration.
27
+ * - `postgres` / `pg` and the singular `action` / `ingestion` are shorthands.
28
+ */
29
+ const QUEUE_TOKEN_TO_CANONICAL = {
30
+ all: "all",
31
+ action: "actions",
32
+ actions: "actions",
33
+ table_actions: "actions",
34
+ ingestion: "ingestions",
35
+ ingestions: "ingestions",
36
+ table_ingestions: "ingestions",
37
+ postgres: "postgres_queue",
38
+ postgres_queue: "postgres_queue",
39
+ postgres_jobs: "postgres_queue",
40
+ pg: "postgres_queue",
41
+ bullmq: "postgres_queue",
42
+ redis: "postgres_queue",
43
+ jobs: "postgres_queue",
44
+ };
45
+ /**
46
+ * Normalize a raw `--queue` / `queue` value to its canonical token. Empty,
47
+ * missing, or unrecognized values resolve to `all` (the aggregate default).
48
+ */
49
+ export function normalizeWorkerFailuresQueue(value) {
50
+ if (!value)
51
+ return "all";
52
+ const normalized = value.trim().toLowerCase();
53
+ if (!normalized)
54
+ return "all";
55
+ return QUEUE_TOKEN_TO_CANONICAL[normalized] ?? "all";
56
+ }
@@ -9,7 +9,6 @@ export declare const DEFAULT_WORKFLOW_CRON_TIMEZONE = "UTC";
9
9
  export type WorkflowMode = "dry_run" | "live" | "smoke_test";
10
10
  export type WorkflowTriggerType = "api" | "webhook" | "cron" | "event";
11
11
  export type WorkflowStatus = "active" | "disabled";
12
- export type WorkflowStepKind = "transform" | "tool" | "branch";
13
12
  export type WorkflowStepEffect = "none" | "external_read" | "external_write";
14
13
  export type RecipeRuntime = "durable";
15
14
  export type WorkflowEventFilterOp = "eq" | "neq" | "exists" | "not_exists";
@@ -211,16 +210,6 @@ export type Blueprint = {
211
210
  source_hash: string;
212
211
  compiler_version: typeof BLUEPRINT_COMPILER_VERSION;
213
212
  };
214
- export type WorkflowApplyInput = {
215
- manifest: WorkflowManifest;
216
- };
217
- export type WorkflowCallInput = {
218
- workflow_id?: string;
219
- workflow_name?: string;
220
- input?: Record<string, unknown>;
221
- mode: WorkflowMode;
222
- idempotency_key?: string;
223
- };
224
213
  type WorkflowFunction = (context: Record<string, unknown>) => unknown | Promise<unknown>;
225
214
  export type WorkflowDefinition = {
226
215
  readonly __oxygen_workflow_definition: true;
@@ -815,4 +804,16 @@ export declare function getWorkflowSchema(subject?: "apply" | "call" | "event" |
815
804
  };
816
805
  };
817
806
  };
807
+ export declare const WORKFLOW_MAX_CREDITS_EXCEEDED_ERROR_CODE = "max_credits_exceeded";
808
+ export declare function readWorkflowRunMaxCredits(metadata: Record<string, unknown> | null | undefined): number | null;
809
+ export declare function readManagedToolRunCredits(output: unknown): number;
810
+ export type WorkflowSpendCapDecision = {
811
+ allowed: boolean;
812
+ projectedCredits: number;
813
+ };
814
+ export declare function evaluateWorkflowRunSpendCap(input: {
815
+ maxCredits: number;
816
+ creditsUsed: number;
817
+ estimatedCredits?: number | null;
818
+ }): WorkflowSpendCapDecision;
818
819
  export {};
@@ -1393,6 +1393,64 @@ function isRecord(value) {
1393
1393
  function isNonEmptyString(value) {
1394
1394
  return typeof value === "string" && value.trim().length > 0;
1395
1395
  }
1396
+ // --- Workflow run spend cap (max_credits) -------------------------------
1397
+ // Live `workflows call` requests carry an approved max_credits spend cap in
1398
+ // the run's metadata (R-E.22(c)). These pure helpers are the single source of
1399
+ // truth for reading that cap and deciding whether the next paid tool step may
1400
+ // run; the worker's step loop and the durable-recipe runtime wrap the refusal
1401
+ // in an OxygenError with this code so the stop is visible as run/step state.
1402
+ export const WORKFLOW_MAX_CREDITS_EXCEEDED_ERROR_CODE = "max_credits_exceeded";
1403
+ // Matches the soft-ceiling epsilon used by table action runs so float drift
1404
+ // never refuses a run that is exactly at its cap.
1405
+ const WORKFLOW_SPEND_CAP_EPSILON = 0.000001;
1406
+ export function readWorkflowRunMaxCredits(metadata) {
1407
+ if (!isRecord(metadata))
1408
+ return null;
1409
+ const raw = metadata.max_credits ?? metadata.maxCredits;
1410
+ const parsed = typeof raw === "number"
1411
+ ? raw
1412
+ : typeof raw === "string" && raw.trim()
1413
+ ? Number(raw)
1414
+ : Number.NaN;
1415
+ if (!Number.isFinite(parsed) || parsed <= 0)
1416
+ return null;
1417
+ return parsed;
1418
+ }
1419
+ // Managed tool runs attach `meta.billing` with the credits the run charged
1420
+ // (`managed_credit_estimate` is reserved and captured in full). BYOK and
1421
+ // user-connection runs consume no OXYGEN credits, so they never count
1422
+ // against the cap.
1423
+ export function readManagedToolRunCredits(output) {
1424
+ if (!isRecord(output))
1425
+ return 0;
1426
+ const meta = output.meta;
1427
+ if (!isRecord(meta))
1428
+ return 0;
1429
+ const billing = meta.billing;
1430
+ if (!isRecord(billing))
1431
+ return 0;
1432
+ if (billing.credential_mode !== "managed")
1433
+ return 0;
1434
+ const credits = billing.managed_credit_estimate;
1435
+ if (typeof credits !== "number" || !Number.isFinite(credits) || credits <= 0)
1436
+ return 0;
1437
+ return credits;
1438
+ }
1439
+ // `estimatedCredits` is null when the next tool has no managed-credit price
1440
+ // (free, BYOK, or unknown catalog entry): such a step is only refused when
1441
+ // the cap is already breached, never pre-emptively.
1442
+ export function evaluateWorkflowRunSpendCap(input) {
1443
+ const estimated = typeof input.estimatedCredits === "number"
1444
+ && Number.isFinite(input.estimatedCredits)
1445
+ && input.estimatedCredits > 0
1446
+ ? input.estimatedCredits
1447
+ : 0;
1448
+ const projectedCredits = input.creditsUsed + estimated;
1449
+ return {
1450
+ allowed: projectedCredits <= input.maxCredits + WORKFLOW_SPEND_CAP_EPSILON,
1451
+ projectedCredits,
1452
+ };
1453
+ }
1396
1454
  function escapeRegExp(value) {
1397
1455
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1398
1456
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxygen-agent/cli",
3
- "version": "1.162.10",
3
+ "version": "1.177.0",
4
4
  "private": false,
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",