@oxygen-agent/cli 1.720.7 → 1.739.1

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 (42) hide show
  1. package/README.md +1 -1
  2. package/dist/command-manifest.js +27 -12
  3. package/dist/index.js +227 -63
  4. package/dist/skills.js +2 -2
  5. package/node_modules/@oxygen/shared/dist/cli-result.js +2 -0
  6. package/node_modules/@oxygen/shared/dist/crm-activity-events.d.ts +20 -0
  7. package/node_modules/@oxygen/shared/dist/crm-activity-events.js +5 -0
  8. package/node_modules/@oxygen/shared/dist/future-signup-events.d.ts +27 -0
  9. package/node_modules/@oxygen/shared/dist/future-signup-events.js +90 -0
  10. package/node_modules/@oxygen/shared/dist/future-signup-lifecycle-projection.d.ts +320 -0
  11. package/node_modules/@oxygen/shared/dist/future-signup-lifecycle-projection.js +1233 -0
  12. package/node_modules/@oxygen/shared/dist/index.d.ts +6 -0
  13. package/node_modules/@oxygen/shared/dist/index.js +6 -0
  14. package/node_modules/@oxygen/shared/dist/notetaker-events.d.ts +4 -0
  15. package/node_modules/@oxygen/shared/dist/notetaker-events.js +23 -0
  16. package/node_modules/@oxygen/shared/dist/plain-support-events.d.ts +40 -0
  17. package/node_modules/@oxygen/shared/dist/plain-support-events.js +43 -0
  18. package/node_modules/@oxygen/shared/dist/redaction.js +6 -1
  19. package/node_modules/@oxygen/shared/dist/user-capability-routing.d.ts +2 -0
  20. package/node_modules/@oxygen/shared/dist/user-capability-routing.js +29 -0
  21. package/node_modules/@oxygen/shared/dist/version.d.ts +1 -1
  22. package/node_modules/@oxygen/shared/dist/version.js +1 -1
  23. package/node_modules/@oxygen/shared/dist/webhook-headers.d.ts +10 -0
  24. package/node_modules/@oxygen/shared/dist/webhook-headers.js +48 -0
  25. package/node_modules/@oxygen/shared/dist/workspace-event-catalog.d.ts +31 -1
  26. package/node_modules/@oxygen/shared/dist/workspace-event-catalog.js +112 -1
  27. package/node_modules/@oxygen/workflows/dist/graph/expression.js +7 -5
  28. package/node_modules/@oxygen/workflows/dist/graph/lint.d.ts +9 -0
  29. package/node_modules/@oxygen/workflows/dist/graph/lint.js +409 -7
  30. package/node_modules/@oxygen/workflows/dist/graph/manifest-schema.d.ts +513 -0
  31. package/node_modules/@oxygen/workflows/dist/graph/manifest-schema.js +104 -1
  32. package/node_modules/@oxygen/workflows/dist/graph/params.d.ts +2 -0
  33. package/node_modules/@oxygen/workflows/dist/graph/params.js +15 -0
  34. package/node_modules/@oxygen/workflows/dist/graph/remap.js +12 -3
  35. package/node_modules/@oxygen/workflows/dist/graph/topology.d.ts +9 -0
  36. package/node_modules/@oxygen/workflows/dist/graph/topology.js +33 -1
  37. package/node_modules/@oxygen/workflows/dist/graph/types.d.ts +123 -16
  38. package/node_modules/@oxygen/workflows/dist/graph/types.js +45 -0
  39. package/node_modules/@oxygen/workflows/dist/index.d.ts +94 -2
  40. package/node_modules/@oxygen/workflows/dist/index.js +169 -26
  41. package/node_modules/@oxygen/workflows/dist/portable.js +3 -1
  42. package/package.json +1 -1
@@ -10,7 +10,7 @@
10
10
  // full copy of those rules here would drift from the linter that actually runs.
11
11
  //
12
12
  // Pure data: this module ships to the browser with the rest of the graph barrel.
13
- import { MAX_WORKFLOW_NODE_RETRY_ATTEMPTS, MAX_WORKFLOW_NODE_RETRY_WAIT_SECONDS, PORTABLE_WORKFLOW_DEFINITION_COMPILER_VERSION, PORTABLE_WORKFLOW_DEFINITION_VERSION, WORKFLOW_GRAPH_COMPILER_VERSION, WORKFLOW_GRAPH_MANIFEST_VERSION, } from "./types.js";
13
+ import { MAX_WORKFLOW_NODE_RETRY_ATTEMPTS, MAX_WORKFLOW_NODE_RETRY_WAIT_SECONDS, PORTABLE_WORKFLOW_DEFINITION_COMPILER_VERSION, PORTABLE_WORKFLOW_DEFINITION_VERSION, WORKFLOW_CODE_V1_DEFAULT_LIMITS, WORKFLOW_CODE_V1_DEFAULT_OUTPUT_SCHEMA, WORKFLOW_CODE_V1_DEFAULT_SOURCE, WORKFLOW_CODE_V1_LANGUAGE, WORKFLOW_CODE_V1_MAX_INPUT_BYTES, WORKFLOW_CODE_V1_MAX_INPUTS, WORKFLOW_CODE_V1_MAX_OUTPUT_BYTES, WORKFLOW_CODE_V1_MAX_SCHEMA_BYTES, WORKFLOW_CODE_V1_MAX_SCHEMA_DEPTH, WORKFLOW_CODE_V1_MAX_SCHEMA_PROPERTIES, WORKFLOW_CODE_V1_MAX_SOURCE_BYTES, WORKFLOW_CODE_V1_MAX_TIMEOUT_MS, WORKFLOW_CODE_V1_MIN_INPUT_BYTES, WORKFLOW_CODE_V1_MIN_OUTPUT_BYTES, WORKFLOW_CODE_V1_MIN_TIMEOUT_MS, WORKFLOW_CODE_V1_RUNTIME, WORKFLOW_CODE_V1_VERSION, WORKFLOW_GRAPH_COMPILER_VERSION, WORKFLOW_GRAPH_MANIFEST_VERSION, } from "./types.js";
14
14
  const valueRefSchema = {
15
15
  type: "object",
16
16
  description: "How a node input is bound at run time. Scope paths are 'trigger.input.x', 'steps.<node_id>.output.y', or 'loop.<loop_node_id>.item.z'.",
@@ -25,6 +25,89 @@ const valueRefSchema = {
25
25
  },
26
26
  required: ["type"],
27
27
  };
28
+ const workflowCodeInputValueRefSchema = {
29
+ ...valueRefSchema,
30
+ description: "One explicit oxygen-js-v1 input binding. Knowledge context refs are excluded in Code v1; bind trigger, step, loop, literal, template, or formula data explicitly.",
31
+ properties: {
32
+ ...valueRefSchema.properties,
33
+ type: { enum: ["literal", "template", "ref", "formula"] },
34
+ },
35
+ };
36
+ const workflowCodeOutputSchemaSchema = {
37
+ type: "object",
38
+ additionalProperties: false,
39
+ description: `A top-level closed object JSON Schema. Supported semantic keywords are type, const, enum, anyOf, properties, required, additionalProperties:false, and items. Every nested object schema must also be closed; maximum ${WORKFLOW_CODE_V1_MAX_SCHEMA_BYTES} UTF-8 bytes, depth ${WORKFLOW_CODE_V1_MAX_SCHEMA_DEPTH}, and ${WORKFLOW_CODE_V1_MAX_SCHEMA_PROPERTIES} named properties total.`,
40
+ properties: {
41
+ type: { const: "object" },
42
+ const: {},
43
+ enum: { type: "array", minItems: 1, items: {} },
44
+ anyOf: { type: "array", minItems: 1, items: { type: "object", additionalProperties: true } },
45
+ properties: {
46
+ type: "object",
47
+ maxProperties: WORKFLOW_CODE_V1_MAX_SCHEMA_PROPERTIES,
48
+ additionalProperties: { type: "object", additionalProperties: true },
49
+ },
50
+ required: { type: "array", items: { type: "string" } },
51
+ additionalProperties: { const: false },
52
+ items: { type: "object", additionalProperties: true },
53
+ title: { type: "string" },
54
+ description: { type: "string" },
55
+ default: {},
56
+ examples: { type: "array", items: {} },
57
+ },
58
+ required: ["type", "additionalProperties"],
59
+ default: WORKFLOW_CODE_V1_DEFAULT_OUTPUT_SCHEMA,
60
+ };
61
+ /** Published authoring schema for the nested oxygen-js-v1 Code envelope. */
62
+ export const workflowCodeV1Schema = {
63
+ type: "object",
64
+ additionalProperties: false,
65
+ description: "Typed, sandboxed, network-denied JavaScript. Source is a complete `(inputs) => output` function expression; provider access belongs in explicit Tool nodes.",
66
+ properties: {
67
+ version: { const: WORKFLOW_CODE_V1_VERSION },
68
+ language: { const: WORKFLOW_CODE_V1_LANGUAGE },
69
+ runtime: { const: WORKFLOW_CODE_V1_RUNTIME },
70
+ source: {
71
+ type: "string",
72
+ default: WORKFLOW_CODE_V1_DEFAULT_SOURCE,
73
+ description: `Complete JavaScript function expression; at most ${WORKFLOW_CODE_V1_MAX_SOURCE_BYTES} UTF-8 bytes.`,
74
+ },
75
+ inputs: {
76
+ type: "object",
77
+ maxProperties: WORKFLOW_CODE_V1_MAX_INPUTS,
78
+ additionalProperties: workflowCodeInputValueRefSchema,
79
+ default: {},
80
+ },
81
+ output_schema: workflowCodeOutputSchemaSchema,
82
+ limits: {
83
+ type: "object",
84
+ additionalProperties: false,
85
+ properties: {
86
+ timeout_ms: {
87
+ type: "integer",
88
+ minimum: WORKFLOW_CODE_V1_MIN_TIMEOUT_MS,
89
+ maximum: WORKFLOW_CODE_V1_MAX_TIMEOUT_MS,
90
+ default: WORKFLOW_CODE_V1_DEFAULT_LIMITS.timeout_ms,
91
+ },
92
+ max_input_bytes: {
93
+ type: "integer",
94
+ minimum: WORKFLOW_CODE_V1_MIN_INPUT_BYTES,
95
+ maximum: WORKFLOW_CODE_V1_MAX_INPUT_BYTES,
96
+ default: WORKFLOW_CODE_V1_DEFAULT_LIMITS.max_input_bytes,
97
+ },
98
+ max_output_bytes: {
99
+ type: "integer",
100
+ minimum: WORKFLOW_CODE_V1_MIN_OUTPUT_BYTES,
101
+ maximum: WORKFLOW_CODE_V1_MAX_OUTPUT_BYTES,
102
+ default: WORKFLOW_CODE_V1_DEFAULT_LIMITS.max_output_bytes,
103
+ },
104
+ },
105
+ required: ["timeout_ms", "max_input_bytes", "max_output_bytes"],
106
+ default: WORKFLOW_CODE_V1_DEFAULT_LIMITS,
107
+ },
108
+ },
109
+ required: ["version", "language", "runtime", "source", "inputs", "output_schema", "limits"],
110
+ };
28
111
  const conditionSchema = {
29
112
  type: "object",
30
113
  description: "A structured predicate: { type: 'group', op: 'all' | 'any', not?: boolean, children: [...] } or { type: 'compare', left, op, right? }. 'is_empty' / 'is_not_empty' take no right operand.",
@@ -174,10 +257,30 @@ const nodeSchema = {
174
257
  description: "Imported durable-recipe code nodes: configuration field -> value ref.",
175
258
  additionalProperties: valueRefSchema,
176
259
  },
260
+ code: workflowCodeV1Schema,
177
261
  workflow_id: { type: "string", description: "workflow nodes: the child workflow to run." },
178
262
  input: { type: "object", description: "workflow nodes: child input name -> value ref.", additionalProperties: valueRefSchema },
179
263
  },
180
264
  required: ["id", "name", "kind", "ui"],
265
+ allOf: [{
266
+ if: { properties: { kind: { const: "code" } }, required: ["kind"] },
267
+ then: {
268
+ oneOf: [
269
+ {
270
+ required: ["run_source"],
271
+ not: { anyOf: [{ required: ["recipe"] }, { required: ["code"] }] },
272
+ },
273
+ {
274
+ required: ["recipe"],
275
+ not: { anyOf: [{ required: ["run_source"] }, { required: ["code"] }] },
276
+ },
277
+ {
278
+ required: ["code"],
279
+ not: { anyOf: [{ required: ["run_source"] }, { required: ["recipe"] }] },
280
+ },
281
+ ],
282
+ },
283
+ }],
181
284
  };
182
285
  const edgeSchema = {
183
286
  type: "object",
@@ -40,6 +40,8 @@ export type ToolParamSpec = {
40
40
  * author cannot clear.
41
41
  */
42
42
  export declare const ONE_OF_REQUIREMENTS: Record<string, readonly string[][]>;
43
+ /** Capabilities needing at least one field from a group; selecting both is valid. */
44
+ export declare const ANY_OF_REQUIREMENTS: Record<string, readonly string[][]>;
43
45
  /**
44
46
  * What is still missing before this node can run, in words an author can act on.
45
47
  * Empty means ready.
@@ -16,6 +16,13 @@ export const ONE_OF_REQUIREMENTS = {
16
16
  // Declares no `required` array at all and needs one of the two payload forms.
17
17
  oxygen_tables_import_csv: [["csv_text", "csv_base64"]],
18
18
  };
19
+ /** Capabilities needing at least one field from a group; selecting both is valid. */
20
+ export const ANY_OF_REQUIREMENTS = {
21
+ // The schema's root anyOf is lost when flattened into workflow fields. A
22
+ // global provider may use external alone, while account-local providers need
23
+ // identity + external together, so this must not use ONE_OF_REQUIREMENTS.
24
+ "oxygen.crm_record_assert": [["identity", "external"]],
25
+ };
19
26
  /**
20
27
  * What is still missing before this node can run, in words an author can act on.
21
28
  * Empty means ready.
@@ -57,6 +64,14 @@ export function missingToolParams(input) {
57
64
  const cap = fields.find((field) => field.name === "max_credits");
58
65
  if (cap && !isPositiveNumberRef(params.max_credits))
59
66
  return [label("max_credits")];
67
+ for (const group of ANY_OF_REQUIREMENTS[toolId] ?? []) {
68
+ const known = group.filter((name) => fields.some((field) => field.name === name));
69
+ if (known.length !== group.length)
70
+ continue;
71
+ if (!group.some((name) => isOneOfOptionSelected(params[name]))) {
72
+ return [group.map(label).join(" or ")];
73
+ }
74
+ }
60
75
  for (const group of ONE_OF_REQUIREMENTS[toolId] ?? []) {
61
76
  const known = group.filter((name) => fields.some((field) => field.name === name));
62
77
  if (known.length !== group.length)
@@ -205,6 +205,17 @@ export function remapNodeRefs(node, ids) {
205
205
  }),
206
206
  };
207
207
  case "code":
208
+ // Typed code names every upstream dependency as a value ref, so a copied
209
+ // node can be rewired safely without ever editing arbitrary source text.
210
+ if (node.code !== undefined) {
211
+ return {
212
+ ...node,
213
+ code: {
214
+ ...node.code,
215
+ inputs: Object.fromEntries(Object.entries(node.code.inputs).map(([key, ref]) => [key, remapValueRef(ref, ids)])),
216
+ },
217
+ };
218
+ }
208
219
  return node.configuration === undefined
209
220
  ? node
210
221
  : {
@@ -212,9 +223,7 @@ export function remapNodeRefs(node, ids) {
212
223
  configuration: Object.fromEntries(Object.entries(node.configuration).map(([key, ref]) => [key, remapValueRef(ref, ids)])),
213
224
  };
214
225
  default:
215
- // trigger, merge — no bindings. A source code node's JavaScript
216
- // reading `ctx.steps`, deliberately NOT rewritten: it is arbitrary code, and
217
- // string-substituting inside it would be a guess.
226
+ // trigger, merge — no bindings.
218
227
  return node;
219
228
  }
220
229
  }
@@ -1,4 +1,13 @@
1
1
  import { type WorkflowGraphEdge, type WorkflowGraphManifest } from "./types.js";
2
+ /**
3
+ * Worst-case fresh microVM count for one graph run.
4
+ *
5
+ * A typed Code node runs once per enclosing loop iteration. A retry replays
6
+ * completed steps and only re-executes the failed sandbox, so retry settings do
7
+ * not multiply every node; one global headroom covers the tenant schema's five
8
+ * run attempts. The result saturates at cap+1 to bound hostile arithmetic.
9
+ */
10
+ export declare function workflowCodeSandboxInvocationUpperBound(graph: WorkflowGraphManifest): number;
2
11
  export type GraphAdjacency = {
3
12
  /** node id -> edges leaving it, in manifest declaration order. */
4
13
  outgoing: Map<string, WorkflowGraphEdge[]>;
@@ -8,7 +8,39 @@
8
8
  // meaningful on a half-drawn canvas.
9
9
  // - `disabled` nodes still participate. Disabling is a run-time skip, not a
10
10
  // structural edit, so the editor keeps showing the same shape.
11
- import { LOOP_BODY_HANDLE, isLoopNode, isTriggerNode, } from "./types.js";
11
+ import { LOOP_BODY_HANDLE, MAX_WORKFLOW_LOOP_ITERATIONS, WORKFLOW_CODE_V1_MAX_SANDBOX_INVOCATIONS_PER_RUN, WORKFLOW_CODE_V1_RUN_RETRY_HEADROOM, isLoopNode, isTriggerNode, } from "./types.js";
12
+ /**
13
+ * Worst-case fresh microVM count for one graph run.
14
+ *
15
+ * A typed Code node runs once per enclosing loop iteration. A retry replays
16
+ * completed steps and only re-executes the failed sandbox, so retry settings do
17
+ * not multiply every node; one global headroom covers the tenant schema's five
18
+ * run attempts. The result saturates at cap+1 to bound hostile arithmetic.
19
+ */
20
+ export function workflowCodeSandboxInvocationUpperBound(graph) {
21
+ const saturation = WORKFLOW_CODE_V1_MAX_SANDBOX_INVOCATIONS_PER_RUN + 1;
22
+ const loops = graph.nodes
23
+ .filter(isLoopNode)
24
+ .map((node) => ({
25
+ body: loopBodyNodeIds(graph, node.id),
26
+ iterations: Number.isInteger(node.max_iterations) && (node.max_iterations ?? 0) > 0
27
+ ? Math.min(node.max_iterations, MAX_WORKFLOW_LOOP_ITERATIONS)
28
+ : MAX_WORKFLOW_LOOP_ITERATIONS,
29
+ }));
30
+ let total = graph.nodes.some((node) => node.kind === "code" && node.code !== undefined && node.disabled !== true) ? WORKFLOW_CODE_V1_RUN_RETRY_HEADROOM : 0;
31
+ for (const node of graph.nodes) {
32
+ if (node.kind !== "code" || !node.code || node.disabled === true)
33
+ continue;
34
+ let invocations = 1;
35
+ for (const loop of loops) {
36
+ if (!loop.body.has(node.id))
37
+ continue;
38
+ invocations = Math.min(saturation, invocations * loop.iterations);
39
+ }
40
+ total = Math.min(saturation, total + invocations);
41
+ }
42
+ return total;
43
+ }
12
44
  function nodeIndex(graph) {
13
45
  const index = new Map();
14
46
  for (const node of graph.nodes) {
@@ -4,6 +4,15 @@ export declare const WORKFLOW_GRAPH_COMPILER_VERSION = "oxygen-workflows-v2";
4
4
  export declare const PORTABLE_WORKFLOW_DEFINITION_VERSION = 1;
5
5
  export declare const PORTABLE_WORKFLOW_DEFINITION_COMPILER_VERSION = "oxygen-workflow-definition-v1";
6
6
  export declare const RESERVED_NODE_IDS: ReadonlySet<string>;
7
+ /**
8
+ * Named Code inputs and declared output fields become dotted scope paths and
9
+ * JavaScript property reads (`inputs.company_domain`). V1 deliberately accepts
10
+ * only one unescaped path segment; bracket-quoted keys can be added later as a
11
+ * versioned path-grammar change without making today's picker offer a path the
12
+ * runtime resolves differently.
13
+ */
14
+ export declare const WORKFLOW_CODE_FIELD_NAME_PATTERN: RegExp;
15
+ export declare function isWorkflowCodeFieldName(value: string): boolean;
7
16
  export declare const LOOP_BODY_HANDLE = "loop_body";
8
17
  export declare const LOOP_DONE_HANDLE = "loop_done";
9
18
  export declare const ERROR_HANDLE = "error";
@@ -79,6 +88,87 @@ export type WorkflowValueRef =
79
88
  path?: string;
80
89
  };
81
90
  export type WorkflowValueRefType = WorkflowValueRef["type"];
91
+ /** The only JSON value kinds the oxygen-js-v1 output validator enforces. */
92
+ export type WorkflowCodeJsonType = "array" | "boolean" | "integer" | "null" | "number" | "object" | "string";
93
+ /**
94
+ * Closed JSON Schema subset enforced for typed Code v1 outputs.
95
+ *
96
+ * Deliberately no index signature: adding a keyword here is a runtime contract
97
+ * change, because the worker must enforce every semantic keyword authors can
98
+ * store. Object-shaped members still need `additionalProperties: false`; lint
99
+ * enforces that recursive invariant because TypeScript cannot express it
100
+ * conditionally for every union of `type` values.
101
+ */
102
+ export type WorkflowCodeJsonSchema = {
103
+ type?: WorkflowCodeJsonType | WorkflowCodeJsonType[];
104
+ const?: unknown;
105
+ enum?: unknown[];
106
+ anyOf?: WorkflowCodeJsonSchema[];
107
+ properties?: Record<string, WorkflowCodeJsonSchema>;
108
+ required?: string[];
109
+ additionalProperties?: false;
110
+ items?: WorkflowCodeJsonSchema;
111
+ /** Annotation-only keywords; the runtime never treats these as constraints. */
112
+ title?: string;
113
+ description?: string;
114
+ default?: unknown;
115
+ examples?: unknown[];
116
+ };
117
+ /** A Code v1 node always returns a closed object at its top level. */
118
+ export type WorkflowCodeOutputSchema = Omit<WorkflowCodeJsonSchema, "type" | "additionalProperties"> & {
119
+ type: "object";
120
+ additionalProperties: false;
121
+ };
122
+ export declare const WORKFLOW_CODE_V1_VERSION = 1;
123
+ export declare const WORKFLOW_CODE_V1_LANGUAGE = "javascript";
124
+ export declare const WORKFLOW_CODE_V1_RUNTIME = "oxygen-js-v1";
125
+ export declare const WORKFLOW_CODE_V1_MAX_SOURCE_BYTES: number;
126
+ export declare const WORKFLOW_CODE_V1_MAX_INPUTS = 100;
127
+ export declare const WORKFLOW_CODE_V1_MIN_TIMEOUT_MS = 10;
128
+ export declare const WORKFLOW_CODE_V1_MAX_TIMEOUT_MS = 10000;
129
+ export declare const WORKFLOW_CODE_V1_DEFAULT_TIMEOUT_MS = 1000;
130
+ export declare const WORKFLOW_CODE_V1_MIN_INPUT_BYTES = 1000;
131
+ export declare const WORKFLOW_CODE_V1_MAX_INPUT_BYTES = 2000000;
132
+ export declare const WORKFLOW_CODE_V1_DEFAULT_INPUT_BYTES = 250000;
133
+ export declare const WORKFLOW_CODE_V1_MIN_OUTPUT_BYTES = 1000;
134
+ export declare const WORKFLOW_CODE_V1_MAX_OUTPUT_BYTES = 2000000;
135
+ export declare const WORKFLOW_CODE_V1_DEFAULT_OUTPUT_BYTES = 250000;
136
+ export declare const WORKFLOW_CODE_V1_MAX_SCHEMA_BYTES: number;
137
+ export declare const WORKFLOW_CODE_V1_MAX_SCHEMA_DEPTH = 8;
138
+ export declare const WORKFLOW_CODE_V1_MAX_SCHEMA_PROPERTIES = 200;
139
+ export declare const WORKFLOW_CODE_V1_MAX_SCHEMA_NODES = 500;
140
+ export declare const WORKFLOW_CODE_V1_MAX_VALIDATION_VISITS = 100000;
141
+ /** Hard per-run COGS boundary for fresh Workflow Code microVMs, including retries and loops. */
142
+ export declare const WORKFLOW_CODE_V1_MAX_SANDBOX_INVOCATIONS_PER_RUN = 25;
143
+ /** Four extra executions cover the tenant schema's five-attempt run ceiling. */
144
+ export declare const WORKFLOW_CODE_V1_RUN_RETRY_HEADROOM = 4;
145
+ export declare const WORKFLOW_CODE_V1_DEFAULT_SOURCE = "(inputs) => ({})";
146
+ export declare const WORKFLOW_CODE_V1_DEFAULT_OUTPUT_SCHEMA: {
147
+ type: "object";
148
+ properties: {};
149
+ additionalProperties: false;
150
+ };
151
+ export declare const WORKFLOW_CODE_V1_DEFAULT_LIMITS: {
152
+ readonly timeout_ms: 1000;
153
+ readonly max_input_bytes: 250000;
154
+ readonly max_output_bytes: 250000;
155
+ };
156
+ /** Typed, deterministic, network-denied JavaScript Code node contract. */
157
+ export type WorkflowCodeV1 = {
158
+ version: typeof WORKFLOW_CODE_V1_VERSION;
159
+ language: typeof WORKFLOW_CODE_V1_LANGUAGE;
160
+ runtime: typeof WORKFLOW_CODE_V1_RUNTIME;
161
+ /** A complete function expression, conventionally `(inputs) => ({ ... })`. */
162
+ source: string;
163
+ /** Explicit graph bindings resolved before the sandbox is entered. */
164
+ inputs: Record<string, WorkflowValueRef>;
165
+ output_schema: WorkflowCodeOutputSchema;
166
+ limits: {
167
+ timeout_ms: number;
168
+ max_input_bytes: number;
169
+ max_output_bytes: number;
170
+ };
171
+ };
82
172
  export type WorkflowCompareOp = "eq" | "neq" | "gt" | "gte" | "lt" | "lte" | "contains" | "not_contains" | "starts_with" | "ends_with" | "is_empty" | "is_not_empty" | "in" | "not_in" | "matches";
83
173
  /**
84
174
  * A structured (never source-code) predicate, so the editor can render it and the
@@ -132,6 +222,37 @@ type WorkflowGraphNodeBase = {
132
222
  /** Bounds on a node's own retry budget. */
133
223
  export declare const MAX_WORKFLOW_NODE_RETRY_ATTEMPTS = 5;
134
224
  export declare const MAX_WORKFLOW_NODE_RETRY_WAIT_SECONDS = 3600;
225
+ /**
226
+ * The three executable shapes a graph Code node may carry.
227
+ *
228
+ * `run_source` and `recipe` remain first-class compatibility variants so old,
229
+ * immutable revisions keep their exact stored contract. New authored code uses
230
+ * the nested typed `code` envelope. The `never` siblings make "exactly one"
231
+ * compile-time visible instead of leaving it solely to the manifest linter.
232
+ */
233
+ export type WorkflowGraphCodeNode = (WorkflowGraphNodeBase & {
234
+ kind: "code";
235
+ run_source: string;
236
+ recipe?: never;
237
+ code?: never;
238
+ configuration?: never;
239
+ /** Imported v1 source reads raw step outputs rather than v2 output envelopes. */
240
+ legacy_context?: "v1";
241
+ }) | (WorkflowGraphNodeBase & {
242
+ kind: "code";
243
+ run_source?: never;
244
+ recipe: RecipeManifest;
245
+ code?: never;
246
+ configuration?: Record<string, WorkflowValueRef>;
247
+ legacy_context?: never;
248
+ }) | (WorkflowGraphNodeBase & {
249
+ kind: "code";
250
+ run_source?: never;
251
+ recipe?: never;
252
+ code: WorkflowCodeV1;
253
+ configuration?: never;
254
+ legacy_context?: never;
255
+ });
135
256
  export type WorkflowGraphNode =
136
257
  /** The run's entry point. Its output is the trigger payload. */
137
258
  (WorkflowGraphNodeBase & {
@@ -223,22 +344,8 @@ export type WorkflowGraphNode =
223
344
  reason?: WorkflowValueRef;
224
345
  request?: Record<string, WorkflowValueRef>;
225
346
  })
226
- /**
227
- * Sandboxed transform source, or an imported durable recipe bundle.
228
- *
229
- * A recipe is deliberately ONE executable node: its bundle is the runtime
230
- * authority, while visual_plan remains inspectable metadata. Turning those
231
- * descriptive checkpoints into editable graph nodes would make the canvas lie
232
- * about what deletion or rewiring actually changes.
233
- */
234
- | (WorkflowGraphNodeBase & {
235
- kind: "code";
236
- run_source?: string;
237
- recipe?: RecipeManifest;
238
- configuration?: Record<string, WorkflowValueRef>;
239
- /** Imported v1 source reads raw step outputs rather than v2 output envelopes. */
240
- legacy_context?: "v1";
241
- })
347
+ /** Legacy source, imported durable recipe, or typed oxygen-js-v1 code. */
348
+ | WorkflowGraphCodeNode
242
349
  /** Calls another workflow as a child run. */
243
350
  | (WorkflowGraphNodeBase & {
244
351
  kind: "workflow";
@@ -10,6 +10,17 @@ export const RESERVED_NODE_IDS = new Set([
10
10
  "constructor",
11
11
  "prototype",
12
12
  ]);
13
+ /**
14
+ * Named Code inputs and declared output fields become dotted scope paths and
15
+ * JavaScript property reads (`inputs.company_domain`). V1 deliberately accepts
16
+ * only one unescaped path segment; bracket-quoted keys can be added later as a
17
+ * versioned path-grammar change without making today's picker offer a path the
18
+ * runtime resolves differently.
19
+ */
20
+ export const WORKFLOW_CODE_FIELD_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
21
+ export function isWorkflowCodeFieldName(value) {
22
+ return WORKFLOW_CODE_FIELD_NAME_PATTERN.test(value) && !RESERVED_NODE_IDS.has(value);
23
+ }
13
24
  // Reserved edge source handles. Anything else in `source_handle` must name a
14
25
  // case id on a `switch` node.
15
26
  export const LOOP_BODY_HANDLE = "loop_body";
@@ -58,6 +69,40 @@ export const BILLABLE_WORKFLOW_GRAPH_NODE_KINDS = new Set([
58
69
  export function workflowGraphNodeKindBills(kind) {
59
70
  return BILLABLE_WORKFLOW_GRAPH_NODE_KINDS.has(kind);
60
71
  }
72
+ export const WORKFLOW_CODE_V1_VERSION = 1;
73
+ export const WORKFLOW_CODE_V1_LANGUAGE = "javascript";
74
+ export const WORKFLOW_CODE_V1_RUNTIME = "oxygen-js-v1";
75
+ export const WORKFLOW_CODE_V1_MAX_SOURCE_BYTES = 64 * 1_024;
76
+ export const WORKFLOW_CODE_V1_MAX_INPUTS = 100;
77
+ export const WORKFLOW_CODE_V1_MIN_TIMEOUT_MS = 10;
78
+ export const WORKFLOW_CODE_V1_MAX_TIMEOUT_MS = 10_000;
79
+ export const WORKFLOW_CODE_V1_DEFAULT_TIMEOUT_MS = 1_000;
80
+ export const WORKFLOW_CODE_V1_MIN_INPUT_BYTES = 1_000;
81
+ export const WORKFLOW_CODE_V1_MAX_INPUT_BYTES = 2_000_000;
82
+ export const WORKFLOW_CODE_V1_DEFAULT_INPUT_BYTES = 250_000;
83
+ export const WORKFLOW_CODE_V1_MIN_OUTPUT_BYTES = 1_000;
84
+ export const WORKFLOW_CODE_V1_MAX_OUTPUT_BYTES = 2_000_000;
85
+ export const WORKFLOW_CODE_V1_DEFAULT_OUTPUT_BYTES = 250_000;
86
+ export const WORKFLOW_CODE_V1_MAX_SCHEMA_BYTES = 64 * 1_024;
87
+ export const WORKFLOW_CODE_V1_MAX_SCHEMA_DEPTH = 8;
88
+ export const WORKFLOW_CODE_V1_MAX_SCHEMA_PROPERTIES = 200;
89
+ export const WORKFLOW_CODE_V1_MAX_SCHEMA_NODES = 500;
90
+ export const WORKFLOW_CODE_V1_MAX_VALIDATION_VISITS = 100_000;
91
+ /** Hard per-run COGS boundary for fresh Workflow Code microVMs, including retries and loops. */
92
+ export const WORKFLOW_CODE_V1_MAX_SANDBOX_INVOCATIONS_PER_RUN = 25;
93
+ /** Four extra executions cover the tenant schema's five-attempt run ceiling. */
94
+ export const WORKFLOW_CODE_V1_RUN_RETRY_HEADROOM = 4;
95
+ export const WORKFLOW_CODE_V1_DEFAULT_SOURCE = "(inputs) => ({})";
96
+ export const WORKFLOW_CODE_V1_DEFAULT_OUTPUT_SCHEMA = {
97
+ type: "object",
98
+ properties: {},
99
+ additionalProperties: false,
100
+ };
101
+ export const WORKFLOW_CODE_V1_DEFAULT_LIMITS = {
102
+ timeout_ms: WORKFLOW_CODE_V1_DEFAULT_TIMEOUT_MS,
103
+ max_input_bytes: WORKFLOW_CODE_V1_DEFAULT_INPUT_BYTES,
104
+ max_output_bytes: WORKFLOW_CODE_V1_DEFAULT_OUTPUT_BYTES,
105
+ };
61
106
  /** Bounds on a node's own retry budget. */
62
107
  export const MAX_WORKFLOW_NODE_RETRY_ATTEMPTS = 5;
63
108
  export const MAX_WORKFLOW_NODE_RETRY_WAIT_SECONDS = 3_600;
@@ -32,7 +32,7 @@ export type JsonSchema = {
32
32
  properties?: Record<string, JsonSchema>;
33
33
  required?: string[];
34
34
  items?: JsonSchema;
35
- enum?: string[];
35
+ enum?: unknown[];
36
36
  const?: unknown;
37
37
  anyOf?: JsonSchema[];
38
38
  additionalProperties?: boolean | JsonSchema;
@@ -444,9 +444,11 @@ export declare function buildBlueprint(input: {
444
444
  exportedAt?: Date;
445
445
  sourceHash?: string;
446
446
  }): Blueprint;
447
- export declare function validateJsonSchemaValue(value: unknown, schema: JsonSchema | undefined, path?: string): JsonSchemaValidationIssue[];
447
+ export declare function validateJsonSchemaValue(value: unknown, schema: JsonSchema | undefined, path?: string, maxIssues?: number, maxVisits?: number): JsonSchemaValidationIssue[];
448
448
  export declare function assertRecipeBundleSafe(bundle: string): void;
449
449
  export declare function lintRecipeBundleSafety(bundle: string, path?: string): WorkflowLintIssue[];
450
+ /** Comment/string-aware author feedback for the stricter oxygen-js-v1 runtime. */
451
+ export declare function lintWorkflowCodeSourceSafety(source: string, path?: string): WorkflowLintIssue[];
450
452
  export declare function runPureWorkflowFunction(input: {
451
453
  source: string;
452
454
  context: Record<string, unknown>;
@@ -471,6 +473,10 @@ export declare const workflowApplySchema: {
471
473
  readonly exclusiveMinimum: 0;
472
474
  readonly description: "Hard credit ceiling for each autonomous delivery.";
473
475
  };
476
+ readonly publish: {
477
+ readonly type: "boolean";
478
+ readonly description: "False saves an editable draft revision without publishing it. Defaults to true.";
479
+ };
474
480
  };
475
481
  readonly required: readonly ["manifest"];
476
482
  readonly $defs: {
@@ -486,6 +492,9 @@ export declare const workflowCallSchema: {
486
492
  readonly type: "object";
487
493
  readonly additionalProperties: false;
488
494
  readonly properties: {
495
+ readonly workflow: {
496
+ readonly type: "string";
497
+ };
489
498
  readonly workflow_id: {
490
499
  readonly type: "string";
491
500
  };
@@ -502,6 +511,20 @@ export declare const workflowCallSchema: {
502
511
  readonly idempotency_key: {
503
512
  readonly type: "string";
504
513
  };
514
+ readonly revision_version: {
515
+ readonly type: "integer";
516
+ readonly minimum: 1;
517
+ readonly description: "Exact saved revision to run. Historical revisions are non-live only.";
518
+ };
519
+ readonly approved: {
520
+ readonly type: "boolean";
521
+ readonly description: "Explicit approval required for live calls.";
522
+ };
523
+ readonly max_credits: {
524
+ readonly type: "number";
525
+ readonly exclusiveMinimum: 0;
526
+ readonly description: "Positive credit ceiling required for live calls.";
527
+ };
505
528
  };
506
529
  readonly required: readonly ["mode"];
507
530
  };
@@ -538,6 +561,15 @@ export declare const workflowEventEmitSchema: {
538
561
  readonly mode: {
539
562
  readonly enum: readonly ["dry_run", "live", "smoke_test"];
540
563
  };
564
+ readonly approved: {
565
+ readonly type: "boolean";
566
+ readonly description: "Explicit approval required when event deliveries run live.";
567
+ };
568
+ readonly max_credits: {
569
+ readonly type: "number";
570
+ readonly exclusiveMinimum: 0;
571
+ readonly description: "Positive per-delivery credit ceiling required for live event emission.";
572
+ };
541
573
  };
542
574
  readonly required: readonly ["source", "event", "payload", "mode"];
543
575
  };
@@ -622,6 +654,10 @@ export declare function getWorkflowSchema(subject?: "apply" | "call" | "event" |
622
654
  readonly exclusiveMinimum: 0;
623
655
  readonly description: "Hard credit ceiling for each autonomous delivery.";
624
656
  };
657
+ readonly publish: {
658
+ readonly type: "boolean";
659
+ readonly description: "False saves an editable draft revision without publishing it. Defaults to true.";
660
+ };
625
661
  };
626
662
  readonly required: readonly ["manifest"];
627
663
  readonly $defs: {
@@ -636,6 +672,9 @@ export declare function getWorkflowSchema(subject?: "apply" | "call" | "event" |
636
672
  readonly type: "object";
637
673
  readonly additionalProperties: false;
638
674
  readonly properties: {
675
+ readonly workflow: {
676
+ readonly type: "string";
677
+ };
639
678
  readonly workflow_id: {
640
679
  readonly type: "string";
641
680
  };
@@ -652,6 +691,20 @@ export declare function getWorkflowSchema(subject?: "apply" | "call" | "event" |
652
691
  readonly idempotency_key: {
653
692
  readonly type: "string";
654
693
  };
694
+ readonly revision_version: {
695
+ readonly type: "integer";
696
+ readonly minimum: 1;
697
+ readonly description: "Exact saved revision to run. Historical revisions are non-live only.";
698
+ };
699
+ readonly approved: {
700
+ readonly type: "boolean";
701
+ readonly description: "Explicit approval required for live calls.";
702
+ };
703
+ readonly max_credits: {
704
+ readonly type: "number";
705
+ readonly exclusiveMinimum: 0;
706
+ readonly description: "Positive credit ceiling required for live calls.";
707
+ };
655
708
  };
656
709
  readonly required: readonly ["mode"];
657
710
  } | {
@@ -687,6 +740,15 @@ export declare function getWorkflowSchema(subject?: "apply" | "call" | "event" |
687
740
  readonly mode: {
688
741
  readonly enum: readonly ["dry_run", "live", "smoke_test"];
689
742
  };
743
+ readonly approved: {
744
+ readonly type: "boolean";
745
+ readonly description: "Explicit approval required when event deliveries run live.";
746
+ };
747
+ readonly max_credits: {
748
+ readonly type: "number";
749
+ readonly exclusiveMinimum: 0;
750
+ readonly description: "Positive per-delivery credit ceiling required for live event emission.";
751
+ };
690
752
  };
691
753
  readonly required: readonly ["source", "event", "payload", "mode"];
692
754
  } | {
@@ -770,6 +832,10 @@ export declare function getWorkflowSchema(subject?: "apply" | "call" | "event" |
770
832
  readonly exclusiveMinimum: 0;
771
833
  readonly description: "Hard credit ceiling for each autonomous delivery.";
772
834
  };
835
+ readonly publish: {
836
+ readonly type: "boolean";
837
+ readonly description: "False saves an editable draft revision without publishing it. Defaults to true.";
838
+ };
773
839
  };
774
840
  readonly required: readonly ["manifest"];
775
841
  readonly $defs: {
@@ -785,6 +851,9 @@ export declare function getWorkflowSchema(subject?: "apply" | "call" | "event" |
785
851
  readonly type: "object";
786
852
  readonly additionalProperties: false;
787
853
  readonly properties: {
854
+ readonly workflow: {
855
+ readonly type: "string";
856
+ };
788
857
  readonly workflow_id: {
789
858
  readonly type: "string";
790
859
  };
@@ -801,6 +870,20 @@ export declare function getWorkflowSchema(subject?: "apply" | "call" | "event" |
801
870
  readonly idempotency_key: {
802
871
  readonly type: "string";
803
872
  };
873
+ readonly revision_version: {
874
+ readonly type: "integer";
875
+ readonly minimum: 1;
876
+ readonly description: "Exact saved revision to run. Historical revisions are non-live only.";
877
+ };
878
+ readonly approved: {
879
+ readonly type: "boolean";
880
+ readonly description: "Explicit approval required for live calls.";
881
+ };
882
+ readonly max_credits: {
883
+ readonly type: "number";
884
+ readonly exclusiveMinimum: 0;
885
+ readonly description: "Positive credit ceiling required for live calls.";
886
+ };
804
887
  };
805
888
  readonly required: readonly ["mode"];
806
889
  };
@@ -837,6 +920,15 @@ export declare function getWorkflowSchema(subject?: "apply" | "call" | "event" |
837
920
  readonly mode: {
838
921
  readonly enum: readonly ["dry_run", "live", "smoke_test"];
839
922
  };
923
+ readonly approved: {
924
+ readonly type: "boolean";
925
+ readonly description: "Explicit approval required when event deliveries run live.";
926
+ };
927
+ readonly max_credits: {
928
+ readonly type: "number";
929
+ readonly exclusiveMinimum: 0;
930
+ readonly description: "Positive per-delivery credit ceiling required for live event emission.";
931
+ };
840
932
  };
841
933
  readonly required: readonly ["source", "event", "payload", "mode"];
842
934
  };