@oxygen-agent/cli 1.766.0 → 1.782.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.
package/README.md CHANGED
@@ -34,4 +34,4 @@ oxygen update
34
34
 
35
35
  For product documentation, visit https://oxygen-agent.com/docs. For support, visit https://oxygen-agent.com.
36
36
 
37
- Version: 1.766.0
37
+ Version: 1.782.1
package/dist/index.js CHANGED
@@ -8216,7 +8216,7 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
8216
8216
  .command("limits")
8217
8217
  .description("Plan-tier limits, spend-safety defaults, and storage capacity posture.")
8218
8218
  .addCommand(new Command("show")
8219
- .description("Show effective plan-tier limits, spend-safety defaults, and uncapped-by-plan storage posture.")
8219
+ .description("Show limits and Tables capacity usage: 3M rows/Table, 25M/workspace, and 20/30 GiB PostgreSQL warning/limit; S3 excluded. Recovery: https://oxygen-agent.com/docs/safety/billing.")
8220
8220
  .option("--json", "Print a JSON envelope.")
8221
8221
  .action(async (options) => {
8222
8222
  await handleAsyncAction("limits show", options, () => requestOxygen("/api/cli/limits"));
@@ -14645,6 +14645,7 @@ Run completion:
14645
14645
  .option("--approved", "Authorize autonomous tool calls for this revision.")
14646
14646
  .option("--max-credits <n>", "Optional explicit positive ceiling for each autonomous delivery; omit to use the plan-tier default.")
14647
14647
  .option("--draft", "Save without going live: keeps the running version serving every trigger. Publish later by applying again without --draft.")
14648
+ .option("--review", "Show what publishing this would change against the version now serving triggers — added, removed and modified steps, what leaves Oxygen, the accounts used, and whether it needs standing authority. Writes nothing.")
14648
14649
  .option("--include-bundle", "Include durable recipe bundles in JSON output.")
14649
14650
  .option("--json", "Print a JSON envelope.")
14650
14651
  .action(async (options) => {
@@ -14659,6 +14660,7 @@ Run completion:
14659
14660
  // byte-identical to what every previous CLI sent, so behaviour
14660
14661
  // against an older server is unchanged rather than merely equivalent.
14661
14662
  ...(options.draft ? { publish: false } : {}),
14663
+ ...(options.review ? { review: true } : {}),
14662
14664
  ...(options.approved ? { approved: true } : {}),
14663
14665
  ...(maxCredits !== undefined ? { max_credits: maxCredits } : {}),
14664
14666
  },
@@ -14795,6 +14797,7 @@ Run completion:
14795
14797
  .option("--approved", "Required for live calls after inspecting a dry run.")
14796
14798
  .option("--revision <n>", "Run a specific saved version instead of the live one. Dry-run and smoke-test only: publish a version to run it live.")
14797
14799
  .option("--node <node_id>", "Test one node: runs that node plus only the predecessors it needs, from the same saved graph. Dry-run and smoke-test only.")
14800
+ .option("--preview", "Show what a live run of this exact version would do — every step that leaves Oxygen, the accounts it would use, and the billable-step floor — without running anything.")
14798
14801
  .option("--include-bundle", "Include durable recipe bundles in JSON output.")
14799
14802
  .option("--json", "Print a JSON envelope.")
14800
14803
  .addHelpText("after", "\nSafety: smoke_test and dry_run share one boundary: 0 credits, no paid provider calls, no external writes. Oxygen internal reads use current workspace data, and oxygen.http_json_request may make a real outbound GET. Every mode creates an inspectable Workflow run record.\n\n--node scopes a test to one step. Oxygen plans the slice on the server from the exact saved version and records it on the run, so the run shows precisely which nodes were allowed to execute; you never submit a node list. Selecting the trigger, a disabled node, or a node unreachable from the trigger is refused with the reason. Only canonical graph workflows support it.\n")
@@ -14814,6 +14817,7 @@ Run completion:
14814
14817
  ...(options.approved ? { approved: true } : {}),
14815
14818
  ...(revisionVersion !== undefined ? { revision_version: revisionVersion } : {}),
14816
14819
  ...(readOption(options.node) ? { test_node_id: readOption(options.node) } : {}),
14820
+ ...(options.preview ? { preview: true } : {}),
14817
14821
  },
14818
14822
  }), options));
14819
14823
  }))
@@ -15094,11 +15098,16 @@ Full trigger schema: oxygen workflows schema --subject trigger --json
15094
15098
  .addCommand(new Command("cancel")
15095
15099
  .description("Cancel a queued or running workflow run.")
15096
15100
  .argument("<run_id>", "Workflow run UUID.")
15101
+ .option("--preview", "Show what cancelling would and would not undo — steps in flight, and any external write already dispatched that cancelling cannot recall. Cancels nothing.")
15097
15102
  .option("--json", "Print a JSON envelope.")
15103
+ .addHelpText("after", "\nCancelling stops further steps; it cannot recall a step already dispatched to a provider. Use --preview first when the run may be mid-write.\n")
15098
15104
  .action(async (runId, options) => {
15099
15105
  await handleAsyncAction("workflows cancel", options, () => requestOxygen("/api/cli/workflows/cancel", {
15100
15106
  method: "POST",
15101
- body: { run_id: runId },
15107
+ body: {
15108
+ run_id: runId,
15109
+ ...(options.preview ? { preview: true } : {}),
15110
+ },
15102
15111
  }));
15103
15112
  }))
15104
15113
  .addCommand(new Command("approvals")
@@ -57,6 +57,7 @@ export * from "./sequences.js";
57
57
  export * from "./suppression-entries.js";
58
58
  export * from "./dnc-identities.js";
59
59
  export * from "./table-limits.js";
60
+ export * from "./table-capacity.js";
60
61
  export * from "./log.js";
61
62
  export * from "./axiom-field-budget.js";
62
63
  export { redactSecretsInString, sanitizeLogFields } from "./redaction.js";
@@ -57,6 +57,7 @@ export * from "./sequences.js";
57
57
  export * from "./suppression-entries.js";
58
58
  export * from "./dnc-identities.js";
59
59
  export * from "./table-limits.js";
60
+ export * from "./table-capacity.js";
60
61
  export * from "./log.js";
61
62
  export * from "./axiom-field-budget.js";
62
63
  // Narrow, deliberate export (ADR 0014): lets telemetry emitters regression-test
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Plan-independent infrastructure envelope for durable Workspace Tables.
3
+ * Plans do not sell different storage entitlements; every workspace gets the
4
+ * same safety boundary and plans continue to differ through operation/rate and
5
+ * credit limits.
6
+ */
7
+ export declare const WORKSPACE_TABLE_CAPACITY: Readonly<{
8
+ tableRowLimit: 3000000;
9
+ workspaceRowLimit: 25000000;
10
+ workspaceDatabaseWarningBytes: number;
11
+ workspaceDatabaseLimitBytes: number;
12
+ }>;
13
+ export declare const WORKSPACE_TABLE_DATABASE_STORAGE_SCOPE: "postgres_workspace_tables_heap_indexes_toast";
@@ -0,0 +1,14 @@
1
+ const GIB = 1024 ** 3;
2
+ /**
3
+ * Plan-independent infrastructure envelope for durable Workspace Tables.
4
+ * Plans do not sell different storage entitlements; every workspace gets the
5
+ * same safety boundary and plans continue to differ through operation/rate and
6
+ * credit limits.
7
+ */
8
+ export const WORKSPACE_TABLE_CAPACITY = Object.freeze({
9
+ tableRowLimit: 3_000_000,
10
+ workspaceRowLimit: 25_000_000,
11
+ workspaceDatabaseWarningBytes: 20 * GIB,
12
+ workspaceDatabaseLimitBytes: 30 * GIB,
13
+ });
14
+ export const WORKSPACE_TABLE_DATABASE_STORAGE_SCOPE = "postgres_workspace_tables_heap_indexes_toast";
@@ -1,4 +1,4 @@
1
- export declare const OXYGEN_VERSION = "1.766.0";
1
+ export declare const OXYGEN_VERSION = "1.782.1";
2
2
  export declare const OXYGEN_MINIMUM_CLI_VERSION = "1.181.0";
3
3
  export declare const MANAGED_INBOX_MINIMUM_CLI_VERSION = "1.326.2";
4
4
  export declare const SUPPORT_AGENT_REPLY_MINIMUM_CLI_VERSION = "1.747.0";
@@ -1,4 +1,4 @@
1
- export const OXYGEN_VERSION = "1.766.0";
1
+ export const OXYGEN_VERSION = "1.782.1";
2
2
  // The GLOBAL CLI compatibility floor: the oldest CLI allowed to call any
3
3
  // operational route. Raising it hard-rejects every older CLI from the entire
4
4
  // product, so it obeys one law, enforced by scripts/ci/cli-min-version-gate.mjs:
@@ -0,0 +1,33 @@
1
+ import type { WorkflowGraphManifest } from "./types.js";
2
+ /**
3
+ * What changed between the revision currently serving triggers and the one about
4
+ * to replace it.
5
+ *
6
+ * Publishing is the moment a workflow stops being a draft and starts acting on
7
+ * the world, and until now the author approved it having been shown a credit cap
8
+ * and nothing else. "Approve this" is not a review if the thing being approved
9
+ * is invisible. This is the comparison that makes the review honest.
10
+ *
11
+ * Canvas position is deliberately NOT a change. `ui` carries x/y, so dragging a
12
+ * node two pixels would otherwise report the graph as modified and train the
13
+ * author to skim a diff that cries wolf — which is worse than no diff, because
14
+ * it looks like diligence.
15
+ */
16
+ export type WorkflowGraphNodeChange = {
17
+ nodeId: string;
18
+ /** The author's own step name, which is what the canvas shows. */
19
+ label: string;
20
+ change: "added" | "removed" | "modified";
21
+ /** For a modified node, the top-level fields that actually differ. */
22
+ changedFields?: string[];
23
+ };
24
+ export type WorkflowGraphDiff = {
25
+ triggerChanged: boolean;
26
+ inputSchemaChanged: boolean;
27
+ nodes: WorkflowGraphNodeChange[];
28
+ edgesAdded: number;
29
+ edgesRemoved: number;
30
+ /** True when nothing semantic differs — a re-publish of the same graph. */
31
+ unchanged: boolean;
32
+ };
33
+ export declare function diffWorkflowGraphManifests(before: WorkflowGraphManifest, after: WorkflowGraphManifest): WorkflowGraphDiff;
@@ -0,0 +1,75 @@
1
+ function label(node) {
2
+ return node.name?.trim() || node.id;
3
+ }
4
+ /** Everything about a node except where it sits on the canvas. */
5
+ function semanticNode(node) {
6
+ const { ui: _ui, ...rest } = node;
7
+ return rest;
8
+ }
9
+ function stable(value) {
10
+ if (value === undefined)
11
+ return "undefined";
12
+ if (value === null || typeof value !== "object")
13
+ return JSON.stringify(value) ?? "null";
14
+ if (Array.isArray(value))
15
+ return `[${value.map(stable).join(",")}]`;
16
+ const entries = Object.entries(value)
17
+ .filter(([, entry]) => entry !== undefined)
18
+ .sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0));
19
+ return `{${entries.map(([key, entry]) => `${JSON.stringify(key)}:${stable(entry)}`).join(",")}}`;
20
+ }
21
+ function changedFields(before, after) {
22
+ const keys = new Set([...Object.keys(before), ...Object.keys(after)]);
23
+ return [...keys]
24
+ .filter((key) => stable(before[key]) !== stable(after[key]))
25
+ .sort();
26
+ }
27
+ function edgeKey(edge) {
28
+ return `${edge.source}->${edge.target}#${edge.source_handle ?? ""}`;
29
+ }
30
+ export function diffWorkflowGraphManifests(before, after) {
31
+ const beforeNodes = new Map(before.nodes.map((node) => [node.id, node]));
32
+ const afterNodes = new Map(after.nodes.map((node) => [node.id, node]));
33
+ const changes = [];
34
+ // Reported in the AFTER graph's order so the review reads like the canvas the
35
+ // author is looking at; removals are appended, since they have no position in it.
36
+ for (const node of after.nodes) {
37
+ const previous = beforeNodes.get(node.id);
38
+ if (!previous) {
39
+ changes.push({ nodeId: node.id, label: label(node), change: "added" });
40
+ continue;
41
+ }
42
+ const fields = changedFields(semanticNode(previous), semanticNode(node));
43
+ if (fields.length > 0) {
44
+ changes.push({
45
+ nodeId: node.id,
46
+ label: label(node),
47
+ change: "modified",
48
+ changedFields: fields,
49
+ });
50
+ }
51
+ }
52
+ for (const node of before.nodes) {
53
+ if (!afterNodes.has(node.id)) {
54
+ changes.push({ nodeId: node.id, label: label(node), change: "removed" });
55
+ }
56
+ }
57
+ const beforeEdges = new Set(before.edges.map(edgeKey));
58
+ const afterEdges = new Set(after.edges.map(edgeKey));
59
+ const edgesAdded = [...afterEdges].filter((key) => !beforeEdges.has(key)).length;
60
+ const edgesRemoved = [...beforeEdges].filter((key) => !afterEdges.has(key)).length;
61
+ const triggerChanged = stable(before.trigger) !== stable(after.trigger);
62
+ const inputSchemaChanged = stable(before.input_schema) !== stable(after.input_schema);
63
+ return {
64
+ triggerChanged,
65
+ inputSchemaChanged,
66
+ nodes: changes,
67
+ edgesAdded,
68
+ edgesRemoved,
69
+ unchanged: !triggerChanged
70
+ && !inputSchemaChanged
71
+ && changes.length === 0
72
+ && edgesAdded === 0
73
+ && edgesRemoved === 0,
74
+ };
75
+ }
@@ -24,12 +24,28 @@ export class WorkflowValueCoercionError extends Error {
24
24
  this.name = "WorkflowValueCoercionError";
25
25
  }
26
26
  }
27
+ /**
28
+ * Build the JSON that would satisfy an unresolved `trigger.input.*` path.
29
+ *
30
+ * The product already knows the exact dotted path it could not resolve, so
31
+ * printing only the path makes the reader reconstruct by hand what the error
32
+ * could have handed them. Only `trigger.input.*` is answerable this way — a
33
+ * `steps.*` ref is produced by an earlier node, not by the caller, so no input
34
+ * would fix it and suggesting one would send the reader down a dead end.
35
+ */
36
+ function unresolvedRefRemedy(path) {
37
+ const parts = path.split(".");
38
+ if (parts.length < 3 || parts[0] !== "trigger" || parts[1] !== "input")
39
+ return "";
40
+ const skeleton = parts.slice(2).reduceRight((inner, key) => ({ [key]: inner }), "…");
41
+ return ` Supply it with --input-json '${JSON.stringify(skeleton)}'.`;
42
+ }
27
43
  /** A direct mapping named data the run does not carry; sending undefined is never intentional. */
28
44
  export class WorkflowValueRefUnresolvedError extends Error {
29
45
  path;
30
46
  code = "workflow_value_ref_unresolved";
31
47
  constructor(path) {
32
- super(`Mapped field '${path}' did not resolve in this run.`);
48
+ super(`Mapped field '${path}' did not resolve in this run.${unresolvedRefRemedy(path)}`);
33
49
  this.path = path;
34
50
  this.name = "WorkflowValueRefUnresolvedError";
35
51
  }
@@ -14,6 +14,7 @@
14
14
  */
15
15
  export * from "./types.js";
16
16
  export * from "./topology.js";
17
+ export * from "./diff.js";
17
18
  export * from "./expression.js";
18
19
  export * from "./params.js";
19
20
  export * from "./remap.js";
@@ -14,6 +14,7 @@
14
14
  */
15
15
  export * from "./types.js";
16
16
  export * from "./topology.js";
17
+ export * from "./diff.js";
17
18
  export * from "./expression.js";
18
19
  export * from "./params.js";
19
20
  export * from "./remap.js";
@@ -24,7 +24,6 @@ const NODE_KINDS = new Set([
24
24
  "wait",
25
25
  "approval",
26
26
  "code",
27
- "workflow",
28
27
  ]);
29
28
  const MERGE_STRATEGIES = new Set(["append", "first", "wait_all"]);
30
29
  const STEP_EFFECTS = new Set(["none", "external_read", "external_write"]);
@@ -334,19 +333,6 @@ node, path, add, scope, options) {
334
333
  case "code":
335
334
  lintCodeNode(node, path, add, scope, options);
336
335
  return;
337
- case "workflow":
338
- if (!isNonEmptyString(node.workflow_id)) {
339
- add(`${path}.workflow_id`, "invalid_workflow_ref", "Workflow node workflow_id is required.");
340
- }
341
- validateValueRefRecord(node.input, `${path}.input`, add, scope);
342
- // Authoring-time refusal, not just a runtime one. A sub-workflow needs its
343
- // own durable run, lease, spend ceiling and cancellation semantics, none of
344
- // which exist yet — so the worker rejects the node terminally. Without this
345
- // rule a user could save the graph, arm a cron trigger on it, and discover
346
- // the refusal at 03:00 on every delivery instead of at `workflows apply`.
347
- // Remove BOTH refusals together when child runs ship.
348
- add(`${path}.kind`, "unsupported_node_kind", "Sub-workflow nodes cannot run yet. Inline the child workflow's nodes for now.");
349
- return;
350
336
  default:
351
337
  return;
352
338
  }
@@ -886,6 +872,15 @@ function lintLoopNode(node, path, add, scope) {
886
872
  if (node.concurrency !== undefined && !isPositiveInteger(node.concurrency)) {
887
873
  add(`${path}.concurrency`, "invalid_loop_target", "Loop concurrency must be a positive integer.");
888
874
  }
875
+ else if (typeof node.concurrency === "number" && node.concurrency > 1) {
876
+ // First-release runtime cut (founder-approved 2026-08-17). The worker runs
877
+ // loop bodies sequentially and always has; `concurrency` was accepted and
878
+ // then ignored, which is the worst of both — the author reads a promise of
879
+ // parallelism, the run delivers none, and nothing says so. Refusing is the
880
+ // honest contract until parallel bodies can interleave lease renewal, spend
881
+ // accounting and per-iteration rows without getting the money wrong.
882
+ add(`${path}.concurrency`, "invalid_loop_target", "Loop bodies run one item at a time. Remove concurrency, or set it to 1.");
883
+ }
889
884
  }
890
885
  function lintSetNode(node, path, add, scope) {
891
886
  const fields = asArray(node.fields);
@@ -932,19 +927,31 @@ function lintApprovalNode(node, path, add, scope) {
932
927
  function lintWaitNode(node, path, add, scope) {
933
928
  const hasDuration = node.duration_seconds !== undefined;
934
929
  const hasUntil = node.until !== undefined;
935
- if (hasDuration === hasUntil) {
936
- add(path, "invalid_wait", "Wait node requires exactly one of duration_seconds or until.");
930
+ // First-release runtime cut (founder-approved 2026-08-17): duration waits only.
931
+ //
932
+ // `until` never did what its name promises. The worker re-evaluates it on a
933
+ // poll against the run's IMMUTABLE input, so it can only ever become true if it
934
+ // was already true — it cannot observe an event, a table write, or anything
935
+ // that happens after the run started. Offering it advertises a transition
936
+ // source that does not exist. Event-driven waits come back when there is a
937
+ // subscription behind them; until then a graph that needs one uses an approval
938
+ // node or a trigger.
939
+ //
940
+ // Existing revisions carrying `until` keep executing unchanged — this refuses
941
+ // the SAVE, not the historical run.
942
+ if (hasUntil) {
943
+ add(`${path}.until`, "invalid_wait", "Wait until is not available. Use a duration wait, or an approval node to pause for a decision.");
937
944
  return;
938
945
  }
939
- if (hasDuration) {
940
- if (typeof node.duration_seconds !== "number"
941
- || !Number.isFinite(node.duration_seconds)
942
- || node.duration_seconds <= 0) {
943
- add(`${path}.duration_seconds`, "invalid_wait", "Wait duration_seconds must be a positive number.");
944
- }
946
+ if (!hasDuration) {
947
+ add(path, "invalid_wait", "Wait node requires duration_seconds.");
945
948
  return;
946
949
  }
947
- validateValueRef(node.until, `${path}.until`, add, scope);
950
+ if (typeof node.duration_seconds !== "number"
951
+ || !Number.isFinite(node.duration_seconds)
952
+ || node.duration_seconds <= 0) {
953
+ add(`${path}.duration_seconds`, "invalid_wait", "Wait duration_seconds must be a positive number.");
954
+ }
948
955
  }
949
956
  function isPositiveInteger(value) {
950
957
  return typeof value === "number" && Number.isInteger(value) && value > 0;
@@ -1092,11 +1099,6 @@ function graphNodeValueRefs(node, path) {
1092
1099
  ref,
1093
1100
  path: `${path}.configuration.${name}`,
1094
1101
  }));
1095
- case "workflow":
1096
- return Object.entries(node.input).map(([name, ref]) => ({
1097
- ref,
1098
- path: `${path}.input.${name}`,
1099
- }));
1100
1102
  default:
1101
1103
  return [];
1102
1104
  }
@@ -204,7 +204,7 @@ export declare const workflowGraphManifestSchema: {
204
204
  readonly type: "string";
205
205
  };
206
206
  readonly kind: {
207
- readonly enum: readonly ["trigger", "tool", "filter", "switch", "loop", "merge", "set", "wait", "approval", "code", "workflow"];
207
+ readonly enum: readonly ["trigger", "tool", "filter", "switch", "loop", "merge", "set", "wait", "approval", "code"];
208
208
  };
209
209
  readonly ui: {
210
210
  readonly type: "object";
@@ -994,7 +994,7 @@ export declare const portableWorkflowDefinitionSchema: {
994
994
  readonly type: "string";
995
995
  };
996
996
  readonly kind: {
997
- readonly enum: readonly ["trigger", "tool", "filter", "switch", "loop", "merge", "set", "wait", "approval", "code", "workflow"];
997
+ readonly enum: readonly ["trigger", "tool", "filter", "switch", "loop", "merge", "set", "wait", "approval", "code"];
998
998
  };
999
999
  readonly ui: {
1000
1000
  readonly type: "object";
@@ -156,7 +156,7 @@ const nodeSchema = {
156
156
  name: { type: "string" },
157
157
  description: { type: "string" },
158
158
  kind: {
159
- enum: ["trigger", "tool", "filter", "switch", "loop", "merge", "set", "wait", "approval", "code", "workflow"],
159
+ enum: ["trigger", "tool", "filter", "switch", "loop", "merge", "set", "wait", "approval", "code"],
160
160
  },
161
161
  ui: {
162
162
  type: "object",
@@ -185,11 +185,6 @@ export function remapNodeRefs(node, ids) {
185
185
  return node.until === undefined
186
186
  ? node
187
187
  : { ...node, until: remapValueRef(node.until, ids) };
188
- case "workflow":
189
- return {
190
- ...node,
191
- input: Object.fromEntries(Object.entries(node.input).map(([key, ref]) => [key, remapValueRef(ref, ids)])),
192
- };
193
188
  // Missed when this module was first written, because the kind list was typed
194
189
  // from memory instead of read off the WorkflowGraphNode union — so a
195
190
  // duplicated approval node kept pointing at the original, silently, which is
@@ -22,6 +22,12 @@ export type WorkflowTestScope = {
22
22
  export type WorkflowTestScopeReceipt = WorkflowTestScope & {
23
23
  source_hash: string;
24
24
  planned_node_ids: string[];
25
+ /**
26
+ * The nodes this scope deliberately excludes. Recorded so "the downstream step
27
+ * did not run" is something the product states, not something a reader infers
28
+ * from a missing row in the step list.
29
+ */
30
+ excluded_node_ids?: string[];
25
31
  };
26
32
  export declare const RESERVED_NODE_IDS: ReadonlySet<string>;
27
33
  /**
@@ -376,13 +382,7 @@ export type WorkflowGraphNode =
376
382
  request?: Record<string, WorkflowValueRef>;
377
383
  })
378
384
  /** Legacy source, imported durable recipe, or typed oxygen-js-v1 code. */
379
- | WorkflowGraphCodeNode
380
- /** Calls another workflow as a child run. */
381
- | (WorkflowGraphNodeBase & {
382
- kind: "workflow";
383
- workflow_id: string;
384
- input: Record<string, WorkflowValueRef>;
385
- });
385
+ | WorkflowGraphCodeNode;
386
386
  export type WorkflowGraphNodeKind = WorkflowGraphNode["kind"];
387
387
  /** Narrow a node union member by its `kind` discriminant. */
388
388
  export type WorkflowGraphNodeOfKind<K extends WorkflowGraphNodeKind> = Extract<WorkflowGraphNode, {
@@ -475,5 +475,4 @@ export declare function isTriggerNode(node: WorkflowGraphNode): node is Workflow
475
475
  export declare function isToolNode(node: WorkflowGraphNode): node is WorkflowGraphNodeOfKind<"tool">;
476
476
  export declare function isSwitchNode(node: WorkflowGraphNode): node is WorkflowGraphNodeOfKind<"switch">;
477
477
  export declare function isLoopNode(node: WorkflowGraphNode): node is WorkflowGraphNodeOfKind<"loop">;
478
- export declare function isWorkflowSubgraphNode(node: WorkflowGraphNode): node is WorkflowGraphNodeOfKind<"workflow">;
479
478
  export {};
@@ -65,7 +65,6 @@ export const MAX_WORKFLOW_LOOP_ITERATIONS = 1_000;
65
65
  */
66
66
  export const BILLABLE_WORKFLOW_GRAPH_NODE_KINDS = new Set([
67
67
  "tool",
68
- "workflow",
69
68
  ]);
70
69
  /** Whether a v2 node of this kind draws an automation action when it executes. */
71
70
  export function workflowGraphNodeKindBills(kind) {
@@ -161,6 +160,3 @@ export function isSwitchNode(node) {
161
160
  export function isLoopNode(node) {
162
161
  return isNodeOfKind(node, "loop");
163
162
  }
164
- export function isWorkflowSubgraphNode(node) {
165
- return isNodeOfKind(node, "workflow");
166
- }
@@ -1938,7 +1938,6 @@ export function workflowManifestCanInvokeTool(manifest) {
1938
1938
  && manifest.tools_used.some((toolId) => isNonEmptyString(toolId));
1939
1939
  const viaGraph = Array.isArray(manifest.nodes)
1940
1940
  && manifest.nodes.some((node) => isRecord(node) && (node.kind === "tool"
1941
- || node.kind === "workflow"
1942
1941
  || (isRecord(node.recipe)
1943
1942
  && Array.isArray(node.recipe.tools_used)
1944
1943
  && node.recipe.tools_used.some((toolId) => isNonEmptyString(toolId)))));
@@ -69,13 +69,6 @@ function portableNodeIssue(node, index) {
69
69
  message: "Legacy execution context cannot be represented as a portable editable graph.",
70
70
  };
71
71
  }
72
- if (node.kind === "workflow") {
73
- return {
74
- path: `$.graph.nodes.${index}`,
75
- code: "workflow_node_not_portable",
76
- message: "Child-workflow nodes are not executable yet and cannot be exported as a portable definition.",
77
- };
78
- }
79
72
  return null;
80
73
  }
81
74
  function valueRefUsesWorkspaceAsset(ref) {
@@ -114,8 +107,6 @@ function nodeUsesWorkspaceAsset(node) {
114
107
  return node.code !== undefined
115
108
  ? Object.values(node.code.inputs).some(valueRefUsesWorkspaceAsset)
116
109
  : Object.values(node.configuration ?? {}).some(valueRefUsesWorkspaceAsset);
117
- case "workflow":
118
- return Object.values(node.input).some(valueRefUsesWorkspaceAsset);
119
110
  case "trigger":
120
111
  case "merge":
121
112
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxygen-agent/cli",
3
- "version": "1.766.0",
3
+ "version": "1.782.1",
4
4
  "private": false,
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",