windmill-cli 1.670.0 → 1.672.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 (2) hide show
  1. package/esm/main.js +328 -33
  2. package/package.json +1 -1
package/esm/main.js CHANGED
@@ -11812,7 +11812,7 @@ var init_OpenAPI = __esm(() => {
11812
11812
  PASSWORD: undefined,
11813
11813
  TOKEN: getEnv2("WM_TOKEN"),
11814
11814
  USERNAME: undefined,
11815
- VERSION: "1.670.0",
11815
+ VERSION: "1.672.0",
11816
11816
  WITH_CREDENTIALS: true,
11817
11817
  interceptors: {
11818
11818
  request: new Interceptors,
@@ -31437,6 +31437,11 @@ public class Main {
31437
31437
  def main a, b, c
31438
31438
  puts a, b, c
31439
31439
  end
31440
+ `,
31441
+ rlang: `
31442
+ main <- function(x, name = "default") {
31443
+ return(list(result = x, name = name))
31444
+ }
31440
31445
  `
31441
31446
  };
31442
31447
  });
@@ -31492,6 +31497,8 @@ function inferContentTypeFromFilePath(contentPath, defaultTs) {
31492
31497
  return "java";
31493
31498
  } else if (contentPath.endsWith(".rb")) {
31494
31499
  return "ruby";
31500
+ } else if (contentPath.endsWith(".r")) {
31501
+ return "rlang";
31495
31502
  } else {
31496
31503
  throw new Error("Invalid language: " + contentPath.substring(contentPath.lastIndexOf(".")));
31497
31504
  }
@@ -59592,6 +59599,8 @@ function filePathExtensionFromContentType(language, defaultTs) {
59592
59599
  return ".java";
59593
59600
  } else if (language === "ruby") {
59594
59601
  return ".rb";
59602
+ } else if (language === "rlang") {
59603
+ return ".r";
59595
59604
  } else {
59596
59605
  throw new Error("Invalid language: " + language);
59597
59606
  }
@@ -60166,7 +60175,8 @@ var init_script = __esm(async () => {
60166
60175
  ".nu",
60167
60176
  ".playbook.yml",
60168
60177
  ".java",
60169
- ".rb"
60178
+ ".rb",
60179
+ ".r"
60170
60180
  ];
60171
60181
  languageAliases = {
60172
60182
  python: "python3"
@@ -60998,7 +61008,8 @@ var init_path_assigner = __esm(() => {
60998
61008
  java: "java",
60999
61009
  duckdb: "duckdb.sql",
61000
61010
  bunnative: "ts",
61001
- ruby: "rb"
61011
+ ruby: "rb",
61012
+ rlang: "r"
61002
61013
  };
61003
61014
  EXTENSION_TO_LANGUAGE = {
61004
61015
  py: "python3",
@@ -62439,7 +62450,8 @@ async function elementsToMap(els, ignore, json, skips, specificItems, branchOver
62439
62450
  "yml",
62440
62451
  "nu",
62441
62452
  "java",
62442
- "rb"
62453
+ "rb",
62454
+ "r"
62443
62455
  ].includes(path9.split(".").pop() ?? "")) {
62444
62456
  continue;
62445
62457
  }
@@ -64586,6 +64598,9 @@ async function inferSchema(language, content, currentSchema, path10) {
64586
64598
  } else if (language === "ruby") {
64587
64599
  const { parse_ruby } = await loadParser("windmill-parser-wasm-ruby");
64588
64600
  inferedSchema = JSON.parse(parse_ruby(content));
64601
+ } else if (language === "rlang") {
64602
+ const { parse_r } = await loadParser("windmill-parser-wasm-r");
64603
+ inferedSchema = JSON.parse(parse_r(content));
64589
64604
  } else {
64590
64605
  throw new Error("Invalid language: " + language);
64591
64606
  }
@@ -70733,7 +70748,7 @@ function getTypeStrFromPath(p) {
70733
70748
  return "workspace_dependencies";
70734
70749
  }
70735
70750
  const parsed = path18.parse(p);
70736
- if (parsed.ext == ".go" || parsed.ext == ".ts" || parsed.ext == ".sh" || parsed.ext == ".py" || parsed.ext == ".sql" || parsed.ext == ".gql" || parsed.ext == ".ps1" || parsed.ext == ".js" || parsed.ext == ".php" || parsed.ext == ".rs" || parsed.ext == ".cs" || parsed.ext == ".nu" || parsed.ext == ".java" || parsed.ext == ".rb" || parsed.ext == ".yml" && parsed.name.split(".").pop() == "playbook") {
70751
+ if (parsed.ext == ".go" || parsed.ext == ".ts" || parsed.ext == ".sh" || parsed.ext == ".py" || parsed.ext == ".sql" || parsed.ext == ".gql" || parsed.ext == ".ps1" || parsed.ext == ".js" || parsed.ext == ".php" || parsed.ext == ".rs" || parsed.ext == ".cs" || parsed.ext == ".nu" || parsed.ext == ".java" || parsed.ext == ".rb" || parsed.ext == ".r" || parsed.ext == ".yml" && parsed.name.split(".").pop() == "playbook") {
70737
70752
  return "script";
70738
70753
  }
70739
70754
  if (parsed.name === "folder.meta" || /^folder\.[^.]+\.meta$/.test(parsed.name)) {
@@ -71178,28 +71193,89 @@ async function run3(opts, path19) {
71178
71193
  path: path19,
71179
71194
  requestBody: input
71180
71195
  });
71196
+ const stepLabels = new Map;
71197
+ try {
71198
+ const initialJob = await getJob({
71199
+ workspace: workspace.workspaceId,
71200
+ id
71201
+ });
71202
+ const rawFlow = initialJob.raw_flow;
71203
+ if (rawFlow?.modules) {
71204
+ for (const mod of rawFlow.modules) {
71205
+ if (mod.id) {
71206
+ const label = mod.summary ? `${mod.id}: ${mod.summary}` : mod.id;
71207
+ stepLabels.set(mod.id, label);
71208
+ }
71209
+ }
71210
+ }
71211
+ } catch {}
71181
71212
  let i = 0;
71213
+ let lastStatus = "";
71182
71214
  while (true) {
71183
71215
  const jobInfo = await getJob({
71184
71216
  workspace: workspace.workspaceId,
71185
71217
  id
71186
71218
  });
71187
- if (jobInfo.flow_status.modules.length <= i) {
71219
+ const isCompleted = jobInfo.type === "CompletedJob";
71220
+ const flowStatus = jobInfo.flow_status;
71221
+ if (flowStatus.modules.length <= i) {
71188
71222
  break;
71189
71223
  }
71190
- const module = jobInfo.flow_status.modules[i];
71191
- if (module.job) {
71192
- if (!opts.silent) {
71193
- info("====== Job " + (i + 1) + " ======");
71224
+ const module = flowStatus.modules[i];
71225
+ if (module.type === "Failure") {
71226
+ if (module.job && !opts.silent) {
71227
+ const label = stepLabels.get(module.id) ?? `Step ${i + 1}`;
71228
+ info("====== " + label + " ======");
71194
71229
  await track_job(workspace.workspaceId, module.job);
71195
71230
  }
71231
+ break;
71232
+ }
71233
+ if (module.job) {
71234
+ const label = stepLabels.get(module.id) ?? `Step ${i + 1}`;
71235
+ const isForLoop = module.flow_jobs !== undefined;
71236
+ if (isForLoop) {
71237
+ let trackedIterations = 0;
71238
+ let forLoopFailed = false;
71239
+ while (true) {
71240
+ const refreshed = await getJob({
71241
+ workspace: workspace.workspaceId,
71242
+ id
71243
+ });
71244
+ const refreshedModule = refreshed.flow_status.modules[i];
71245
+ const flowJobs = refreshedModule.flow_jobs ?? [];
71246
+ while (trackedIterations < flowJobs.length) {
71247
+ if (!opts.silent) {
71248
+ info(`====== ${label} (iteration ${trackedIterations}) ======`);
71249
+ await track_job(workspace.workspaceId, flowJobs[trackedIterations]);
71250
+ }
71251
+ trackedIterations++;
71252
+ }
71253
+ if (refreshedModule.type === "Success" || refreshedModule.type === "Failure") {
71254
+ forLoopFailed = refreshedModule.type === "Failure";
71255
+ break;
71256
+ }
71257
+ await new Promise((resolve8) => setTimeout(resolve8, 200));
71258
+ }
71259
+ if (forLoopFailed)
71260
+ break;
71261
+ } else {
71262
+ if (!opts.silent) {
71263
+ info("====== " + label + " ======");
71264
+ await track_job(workspace.workspaceId, module.job);
71265
+ }
71266
+ }
71196
71267
  } else {
71197
- if (!opts.silent) {
71198
- info(module.type);
71268
+ const status = String(module.type);
71269
+ if (!opts.silent && status !== lastStatus) {
71270
+ info(colors.dim(status));
71271
+ lastStatus = status;
71199
71272
  }
71200
71273
  await new Promise((resolve8, _) => setTimeout(() => resolve8(undefined), 100));
71274
+ if (isCompleted)
71275
+ break;
71201
71276
  continue;
71202
71277
  }
71278
+ lastStatus = "";
71203
71279
  i++;
71204
71280
  }
71205
71281
  const MAX_RETRIES = 600;
@@ -71211,7 +71287,11 @@ async function run3(opts, path19) {
71211
71287
  id
71212
71288
  });
71213
71289
  if (!opts.silent) {
71214
- info(colors.green.underline.bold("Flow ran to completion"));
71290
+ if (jobInfo.success === false) {
71291
+ info(colors.red.underline.bold("Flow failed"));
71292
+ } else {
71293
+ info(colors.green.underline.bold("Flow ran to completion"));
71294
+ }
71215
71295
  info(`
71216
71296
  `);
71217
71297
  }
@@ -74385,6 +74465,7 @@ var SKILLS = [
74385
74465
  { name: "write-script-postgresql", description: "MUST use when writing PostgreSQL queries.", languageKey: "postgresql" },
74386
74466
  { name: "write-script-powershell", description: "MUST use when writing PowerShell scripts.", languageKey: "powershell" },
74387
74467
  { name: "write-script-python3", description: "MUST use when writing Python scripts.", languageKey: "python3" },
74468
+ { name: "write-script-rlang", description: "MUST use when writing R scripts.", languageKey: "rlang" },
74388
74469
  { name: "write-script-rust", description: "MUST use when writing Rust scripts.", languageKey: "rust" },
74389
74470
  { name: "write-script-snowflake", description: "MUST use when writing Snowflake queries.", languageKey: "snowflake" },
74390
74471
  { name: "write-flow", description: "MUST use when creating flows." },
@@ -78472,6 +78553,107 @@ async def parallel(items, fn, concurrency: Optional[int] = None)
78472
78553
  # offset: Message offset to commit (from event['offset'])
78473
78554
  def commit_kafka_offsets(trigger_path: str, topic: str, partition: int, offset: int) -> None
78474
78555
 
78556
+ `,
78557
+ "write-script-rlang": `---
78558
+ name: write-script-rlang
78559
+ description: MUST use when writing R scripts.
78560
+ ---
78561
+
78562
+ ## CLI Commands
78563
+
78564
+ Place scripts in a folder. After writing, tell the user they can run:
78565
+ - \`wmill script generate-metadata\` - Generate .script.yaml and .lock files
78566
+ - \`wmill sync push\` - Deploy to Windmill
78567
+
78568
+ Do NOT run these commands yourself. Instead, inform the user that they should run them.
78569
+
78570
+ Use \`wmill resource-type list --schema\` to discover available resource types.
78571
+
78572
+ # R
78573
+
78574
+ ## Structure
78575
+
78576
+ Define a \`main\` function using \`<-\` or \`=\` assignment. Parameters become the script inputs:
78577
+
78578
+ \`\`\`r
78579
+ library(dplyr)
78580
+ library(jsonlite)
78581
+
78582
+ main <- function(x, name = "default", flag = TRUE) {
78583
+ df <- tibble(x = x, name = name)
78584
+ result <- df %>% mutate(greeting = paste("Hello", name))
78585
+ return(toJSON(result, auto_unbox = TRUE))
78586
+ }
78587
+ \`\`\`
78588
+
78589
+ **Important:**
78590
+ - The \`main\` function is required
78591
+ - Use \`library()\` to load packages — they are resolved and installed automatically
78592
+ - \`jsonlite\` is always available (used internally for argument parsing)
78593
+ - Return values must be JSON-serializable
78594
+
78595
+ ## Parameters
78596
+
78597
+ R types map to Windmill types:
78598
+ - \`numeric\` → float/int
78599
+ - \`character\` → string
78600
+ - \`logical\` → bool (use \`TRUE\`/\`FALSE\`)
78601
+ - \`list\` → object/dict
78602
+ - \`NULL\` → null
78603
+
78604
+ Default values are inferred from the function signature:
78605
+
78606
+ \`\`\`r
78607
+ main <- function(
78608
+ name, # required string
78609
+ count = 10, # optional int, default 10
78610
+ verbose = FALSE # optional bool, default FALSE
78611
+ ) {
78612
+ # ...
78613
+ }
78614
+ \`\`\`
78615
+
78616
+ ## Resources and Variables
78617
+
78618
+ Use the built-in Windmill helpers (no import needed):
78619
+
78620
+ \`\`\`r
78621
+ main <- function() {
78622
+ # Get a variable
78623
+ api_key <- get_variable("f/my_folder/api_key")
78624
+
78625
+ # Get a resource (returns a list)
78626
+ db <- get_resource("f/my_folder/postgres_config")
78627
+ host <- db$host
78628
+ port <- db$port
78629
+
78630
+ return(list(host = host, port = port))
78631
+ }
78632
+ \`\`\`
78633
+
78634
+ ## Output
78635
+
78636
+ Return any JSON-serializable value from \`main\`. The return value becomes the step result:
78637
+
78638
+ \`\`\`r
78639
+ main <- function(x) {
78640
+ # Return a scalar
78641
+ return(x + 1)
78642
+
78643
+ # Or a list (becomes JSON object)
78644
+ return(list(result = x + 1, status = "ok"))
78645
+ }
78646
+ \`\`\`
78647
+
78648
+ ## Annotations
78649
+
78650
+ Control execution behavior with comment annotations:
78651
+
78652
+ \`\`\`r
78653
+ #renv_verbose = true # Show verbose renv output during resolution
78654
+ #renv_install_verbose = true # Show verbose output during package installation
78655
+ #sandbox = true # Run in nsjail sandbox (requires nsjail)
78656
+ \`\`\`
78475
78657
  `,
78476
78658
  "write-script-rust": `---
78477
78659
  name: write-script-rust
@@ -78718,7 +78900,7 @@ Reference a specific resource using \`$res:\` prefix:
78718
78900
 
78719
78901
  ## OpenFlow Schema
78720
78902
 
78721
- {"OpenFlow":{"type":"object","description":"Top-level flow definition containing metadata, configuration, and the flow structure","properties":{"summary":{"type":"string","description":"Short description of what this flow does"},"description":{"type":"string","description":"Detailed documentation for this flow"},"value":{"$ref":"#/components/schemas/FlowValue"},"schema":{"type":"object","description":"JSON Schema for flow inputs. Use this to define input parameters, their types, defaults, and validation. For resource inputs, set type to 'object' and format to 'resource-<type>' (e.g., 'resource-stripe')"},"on_behalf_of_email":{"type":"string","description":"The flow will be run with the permissions of the user with this email."}},"required":["summary","value"]},"FlowValue":{"type":"object","description":"The flow structure containing modules and optional preprocessor/failure handlers","properties":{"modules":{"type":"array","description":"Array of steps that execute in sequence. Each step can be a script, subflow, loop, or branch","items":{"$ref":"#/components/schemas/FlowModule"}},"failure_module":{"description":"Special module that executes when the flow fails. Receives error object with message, name, stack, and step_id. Must have id 'failure'. Only supports script/rawscript types","$ref":"#/components/schemas/FlowModule"},"preprocessor_module":{"description":"Special module that runs before the first step on external triggers. Must have id 'preprocessor'. Only supports script/rawscript types. Cannot reference other step results","$ref":"#/components/schemas/FlowModule"},"same_worker":{"type":"boolean","description":"If true, all steps run on the same worker for better performance"},"concurrent_limit":{"type":"number","description":"Maximum number of concurrent executions of this flow"},"concurrency_key":{"type":"string","description":"Expression to group concurrent executions (e.g., by user ID)"},"concurrency_time_window_s":{"type":"number","description":"Time window in seconds for concurrent_limit"},"debounce_delay_s":{"type":"integer","description":"Delay in seconds to debounce flow executions"},"debounce_key":{"type":"string","description":"Expression to group debounced executions"},"debounce_args_to_accumulate":{"type":"array","description":"Arguments to accumulate across debounced executions","items":{"type":"string"}},"max_total_debouncing_time":{"type":"integer","description":"Maximum total time in seconds that a job can be debounced"},"max_total_debounces_amount":{"type":"integer","description":"Maximum number of times a job can be debounced"},"skip_expr":{"type":"string","description":"JavaScript expression to conditionally skip the entire flow"},"cache_ttl":{"type":"number","description":"Cache duration in seconds for flow results"},"cache_ignore_s3_path":{"type":"boolean"},"flow_env":{"type":"object","description":"Environment variables available to all steps. Values can be strings, JSON values, or special references: '$var:path' (workspace variable) or '$res:path' (resource).","additionalProperties":{}},"priority":{"type":"number","description":"Execution priority (higher numbers run first)"},"early_return":{"type":"string","description":"JavaScript expression to return early from the flow"},"chat_input_enabled":{"type":"boolean","description":"Whether this flow accepts chat-style input"},"notes":{"type":"array","description":"Sticky notes attached to the flow","items":{"$ref":"#/components/schemas/FlowNote"}},"groups":{"type":"array","description":"Semantic groups of modules for organizational purposes","items":{"$ref":"#/components/schemas/FlowGroup"}}},"required":["modules"]},"Retry":{"type":"object","description":"Retry configuration for failed module executions","properties":{"constant":{"type":"object","description":"Retry with constant delay between attempts","properties":{"attempts":{"type":"integer","description":"Number of retry attempts"},"seconds":{"type":"integer","description":"Seconds to wait between retries"}}},"exponential":{"type":"object","description":"Retry with exponential backoff (delay doubles each time)","properties":{"attempts":{"type":"integer","description":"Number of retry attempts"},"multiplier":{"type":"integer","description":"Multiplier for exponential backoff"},"seconds":{"type":"integer","minimum":1,"description":"Initial delay in seconds"},"random_factor":{"type":"integer","minimum":0,"maximum":100,"description":"Random jitter percentage (0-100) to avoid thundering herd"}}},"retry_if":{"$ref":"#/components/schemas/RetryIf"}}},"FlowNote":{"type":"object","description":"A sticky note attached to a flow for documentation and annotation","properties":{"id":{"type":"string","description":"Unique identifier for the note"},"text":{"type":"string","description":"Content of the note"},"position":{"type":"object","description":"Position of the note in the flow editor","properties":{"x":{"type":"number","description":"X coordinate"},"y":{"type":"number","description":"Y coordinate"}},"required":["x","y"]},"size":{"type":"object","description":"Size of the note in the flow editor","properties":{"width":{"type":"number","description":"Width in pixels"},"height":{"type":"number","description":"Height in pixels"}},"required":["width","height"]},"color":{"type":"string","description":"Color of the note (e.g., \\"yellow\\", \\"#ffff00\\")"},"type":{"type":"string","enum":["free","group"],"description":"Type of note - 'free' for standalone notes, 'group' for notes that group other nodes"},"locked":{"type":"boolean","default":false,"description":"Whether the note is locked and cannot be edited or moved"},"contained_node_ids":{"type":"array","items":{"type":"string"},"description":"For group notes, the IDs of nodes contained within this group"}},"required":["id","text","color","type"]},"FlowGroup":{"type":"object","description":"A semantic group of flow modules for organizational purposes. Does not affect execution \\u2014 modules remain in their original position in the flow. Groups provide naming and collapsibility in the editor. Members are computed dynamically from all nodes on paths between start_id and end_id.","properties":{"summary":{"type":"string","description":"Display name for this group"},"note":{"type":"string","description":"Markdown note shown below the group header"},"autocollapse":{"type":"boolean","default":false,"description":"If true, this group is collapsed by default in the flow editor. UI hint only."},"start_id":{"type":"string","description":"ID of the first flow module in this group (topological entry point)"},"end_id":{"type":"string","description":"ID of the last flow module in this group (topological exit point)"},"color":{"type":"string","description":"Color for the group in the flow editor"}},"required":["start_id","end_id"]},"RetryIf":{"type":"object","description":"Conditional retry based on error or result","properties":{"expr":{"type":"string","description":"JavaScript expression that returns true to retry. Has access to 'result' and 'error' variables"}},"required":["expr"]},"StopAfterIf":{"type":"object","description":"Early termination condition for a module","properties":{"skip_if_stopped":{"type":"boolean","description":"If true, following steps are skipped when this condition triggers"},"expr":{"type":"string","description":"JavaScript expression evaluated after the module runs. Can use 'result' (step's result) or 'flow_input'. Return true to stop"},"error_message":{"type":"string","nullable":true,"description":"Custom error message when stopping with an error. Mutually exclusive with skip_if_stopped. If set to a non-empty string, the flow stops with this error. If empty string, a default error message is used. If null or omitted, no error is raised."}},"required":["expr"]},"FlowModule":{"type":"object","description":"A single step in a flow. Can be a script, subflow, loop, or branch","properties":{"id":{"type":"string","description":"Unique identifier for this step. Used to reference results via 'results.step_id'. Must be a valid identifier (alphanumeric, underscore, hyphen)"},"value":{"$ref":"#/components/schemas/FlowModuleValue"},"stop_after_if":{"description":"Early termination condition evaluated after this step completes","$ref":"#/components/schemas/StopAfterIf"},"stop_after_all_iters_if":{"description":"For loops only - early termination condition evaluated after all iterations complete","$ref":"#/components/schemas/StopAfterIf"},"skip_if":{"type":"object","description":"Conditionally skip this step based on previous results or flow inputs","properties":{"expr":{"type":"string","description":"JavaScript expression that returns true to skip. Can use 'flow_input' or 'results.<step_id>'"}},"required":["expr"]},"sleep":{"description":"Delay before executing this step (in seconds or as expression)","$ref":"#/components/schemas/InputTransform"},"cache_ttl":{"type":"number","description":"Cache duration in seconds for this step's results"},"cache_ignore_s3_path":{"type":"boolean"},"timeout":{"description":"Maximum execution time in seconds (static value or expression)","$ref":"#/components/schemas/InputTransform"},"delete_after_use":{"type":"boolean","description":"If true, this step's result is deleted after use to save memory"},"summary":{"type":"string","description":"Short description of what this step does"},"mock":{"type":"object","description":"Mock configuration for testing without executing the actual step","properties":{"enabled":{"type":"boolean","description":"If true, return mock value instead of executing"},"return_value":{"description":"Value to return when mocked"}}},"suspend":{"type":"object","description":"Configuration for approval/resume steps that wait for user input","properties":{"required_events":{"type":"integer","description":"Number of approvals required before continuing"},"timeout":{"type":"integer","description":"Timeout in seconds before auto-continuing or canceling"},"resume_form":{"type":"object","description":"Form schema for collecting input when resuming","properties":{"schema":{"type":"object","description":"JSON Schema for the resume form"}}},"user_auth_required":{"type":"boolean","description":"If true, only authenticated users can approve"},"user_groups_required":{"description":"Expression or list of groups that can approve","$ref":"#/components/schemas/InputTransform"},"self_approval_disabled":{"type":"boolean","description":"If true, the user who started the flow cannot approve"},"hide_cancel":{"type":"boolean","description":"If true, hide the cancel button on the approval form"},"continue_on_disapprove_timeout":{"type":"boolean","description":"If true, continue flow on timeout instead of canceling"}}},"priority":{"type":"number","description":"Execution priority for this step (higher numbers run first)"},"continue_on_error":{"type":"boolean","description":"If true, flow continues even if this step fails"},"retry":{"description":"Retry configuration if this step fails","$ref":"#/components/schemas/Retry"},"debouncing":{"description":"Debounce configuration for this step (EE only)","type":"object","properties":{"debounce_delay_s":{"type":"integer","description":"Delay in seconds to debounce this step's executions across flow runs"},"debounce_key":{"type":"string","description":"Expression to group debounced executions. Supports $workspace and $args[name]. Default: $workspace/flow/<flow_path>-<step_id>"},"debounce_args_to_accumulate":{"type":"array","description":"Array-type arguments to accumulate across debounced executions","items":{"type":"string"}},"max_total_debouncing_time":{"type":"integer","description":"Maximum total time in seconds before forced execution"},"max_total_debounces_amount":{"type":"integer","description":"Maximum number of debounces before forced execution"}}}},"required":["value","id"]},"InputTransform":{"description":"Maps input parameters for a step. Can be a static value or a JavaScript expression that references previous results or flow inputs","oneOf":[{"$ref":"#/components/schemas/StaticTransform"},{"$ref":"#/components/schemas/JavascriptTransform"},{"$ref":"#/components/schemas/AiTransform"}],"discriminator":{"propertyName":"type","mapping":{"static":"#/components/schemas/StaticTransform","javascript":"#/components/schemas/JavascriptTransform","ai":"#/components/schemas/AiTransform"}}},"StaticTransform":{"type":"object","description":"Static value passed directly to the step. Use for hardcoded values or resource references like '$res:path/to/resource'","properties":{"value":{"description":"The static value. For resources, use format '$res:path/to/resource'"},"type":{"type":"string","enum":["static"]}},"required":["type"]},"JavascriptTransform":{"type":"object","description":"JavaScript expression evaluated at runtime. Can reference previous step results via 'results.step_id' or flow inputs via 'flow_input.property'. Inside loops, use 'flow_input.iter.value' for the current iteration value","properties":{"expr":{"type":"string","description":"JavaScript expression returning the value. Available variables - results (object with all previous step results), flow_input (flow inputs), flow_input.iter (in loops)"},"type":{"type":"string","enum":["javascript"]}},"required":["expr","type"]},"AiTransform":{"type":"object","description":"Value resolved by the AI runtime for this input. The AI engine decides how to satisfy the parameter.","properties":{"type":{"type":"string","enum":["ai"]}},"required":["type"]},"AIProviderKind":{"type":"string","description":"Supported AI provider types","enum":["openai","azure_openai","anthropic","mistral","deepseek","googleai","groq","openrouter","togetherai","customai","aws_bedrock"]},"ProviderConfig":{"type":"object","description":"Complete AI provider configuration with resource reference and model selection","properties":{"kind":{"$ref":"#/components/schemas/AIProviderKind"},"resource":{"type":"string","description":"Resource reference in format '$res:{resource_path}' pointing to provider credentials"},"model":{"type":"string","description":"Model identifier (e.g., 'gpt-4', 'claude-3-opus-20240229', 'gemini-pro')"}},"required":["kind","resource","model"]},"StaticProviderTransform":{"type":"object","description":"Static provider configuration passed directly to the AI agent","properties":{"value":{"$ref":"#/components/schemas/ProviderConfig"},"type":{"type":"string","enum":["static"]}},"required":["type","value"]},"ProviderTransform":{"description":"Provider configuration - can be static (ProviderConfig), JavaScript expression, or AI-determined","oneOf":[{"$ref":"#/components/schemas/StaticProviderTransform"},{"$ref":"#/components/schemas/JavascriptTransform"},{"$ref":"#/components/schemas/AiTransform"}],"discriminator":{"propertyName":"type","mapping":{"static":"#/components/schemas/StaticProviderTransform","javascript":"#/components/schemas/JavascriptTransform","ai":"#/components/schemas/AiTransform"}}},"MemoryOff":{"type":"object","description":"No conversation memory/context","properties":{"kind":{"type":"string","enum":["off"]}},"required":["kind"]},"MemoryAuto":{"type":"object","description":"Automatic context management","properties":{"kind":{"type":"string","enum":["auto"]},"context_length":{"type":"integer","description":"Maximum number of messages to retain in context"},"memory_id":{"type":"string","description":"Identifier for persistent memory across agent invocations"}},"required":["kind"]},"MemoryMessage":{"type":"object","description":"A single message in conversation history","properties":{"role":{"type":"string","enum":["user","assistant","system"]},"content":{"type":"string"}},"required":["role","content"]},"MemoryManual":{"type":"object","description":"Explicit message history","properties":{"kind":{"type":"string","enum":["manual"]},"messages":{"type":"array","items":{"$ref":"#/components/schemas/MemoryMessage"}}},"required":["kind","messages"]},"MemoryConfig":{"description":"Conversation memory configuration","oneOf":[{"$ref":"#/components/schemas/MemoryOff"},{"$ref":"#/components/schemas/MemoryAuto"},{"$ref":"#/components/schemas/MemoryManual"}],"discriminator":{"propertyName":"kind","mapping":{"off":"#/components/schemas/MemoryOff","auto":"#/components/schemas/MemoryAuto","manual":"#/components/schemas/MemoryManual"}}},"StaticMemoryTransform":{"type":"object","description":"Static memory configuration passed directly to the AI agent","properties":{"value":{"$ref":"#/components/schemas/MemoryConfig"},"type":{"type":"string","enum":["static"]}},"required":["type","value"]},"MemoryTransform":{"description":"Memory configuration - can be static (MemoryConfig), JavaScript expression, or AI-determined","oneOf":[{"$ref":"#/components/schemas/StaticMemoryTransform"},{"$ref":"#/components/schemas/JavascriptTransform"},{"$ref":"#/components/schemas/AiTransform"}],"discriminator":{"propertyName":"type","mapping":{"static":"#/components/schemas/StaticMemoryTransform","javascript":"#/components/schemas/JavascriptTransform","ai":"#/components/schemas/AiTransform"}}},"FlowModuleValue":{"description":"The actual implementation of a flow step. Can be a script (inline or referenced), subflow, loop, branch, or special module type","oneOf":[{"$ref":"#/components/schemas/RawScript"},{"$ref":"#/components/schemas/PathScript"},{"$ref":"#/components/schemas/PathFlow"},{"$ref":"#/components/schemas/ForloopFlow"},{"$ref":"#/components/schemas/WhileloopFlow"},{"$ref":"#/components/schemas/BranchOne"},{"$ref":"#/components/schemas/BranchAll"},{"$ref":"#/components/schemas/Identity"},{"$ref":"#/components/schemas/AiAgent"}],"discriminator":{"propertyName":"type","mapping":{"rawscript":"#/components/schemas/RawScript","script":"#/components/schemas/PathScript","flow":"#/components/schemas/PathFlow","forloopflow":"#/components/schemas/ForloopFlow","whileloopflow":"#/components/schemas/WhileloopFlow","branchone":"#/components/schemas/BranchOne","branchall":"#/components/schemas/BranchAll","identity":"#/components/schemas/Identity","aiagent":"#/components/schemas/AiAgent"}}},"RawScript":{"type":"object","description":"Inline script with code defined directly in the flow. Use 'bun' as default language if unspecified. The script receives arguments from input_transforms","properties":{"input_transforms":{"type":"object","description":"Map of parameter names to their values (static or JavaScript expressions). These become the script's input arguments","additionalProperties":{"$ref":"#/components/schemas/InputTransform"}},"content":{"type":"string","description":"The script source code. Should export a 'main' function"},"language":{"type":"string","description":"Programming language for this script","enum":["deno","bun","python3","go","bash","powershell","postgresql","mysql","bigquery","snowflake","mssql","oracledb","graphql","nativets","php","rust","ansible","csharp","nu","java","ruby","duckdb"]},"path":{"type":"string","description":"Optional path for saving this script"},"lock":{"type":"string","description":"Lock file content for dependencies"},"type":{"type":"string","enum":["rawscript"]},"tag":{"type":"string","description":"Worker group tag for execution routing"},"concurrent_limit":{"type":"number","description":"Maximum concurrent executions of this script"},"concurrency_time_window_s":{"type":"number","description":"Time window for concurrent_limit"},"custom_concurrency_key":{"type":"string","description":"Custom key for grouping concurrent executions"},"is_trigger":{"type":"boolean","description":"If true, this script is a trigger that can start the flow"},"assets":{"type":"array","description":"External resources this script accesses (S3 objects, resources, etc.)","items":{"type":"object","required":["path","kind"],"properties":{"path":{"type":"string","description":"Path to the asset"},"kind":{"type":"string","description":"Type of asset","enum":["s3object","resource","ducklake","datatable","volume"]},"access_type":{"type":"string","nullable":true,"description":"Access level for this asset","enum":["r","w","rw"]},"alt_access_type":{"type":"string","nullable":true,"description":"Alternative access level","enum":["r","w","rw"]}}}}},"required":["type","content","language","input_transforms"]},"PathScript":{"type":"object","description":"Reference to an existing script by path. Use this when calling a previously saved script instead of writing inline code","properties":{"input_transforms":{"type":"object","description":"Map of parameter names to their values (static or JavaScript expressions). These become the script's input arguments","additionalProperties":{"$ref":"#/components/schemas/InputTransform"}},"path":{"type":"string","description":"Path to the script in the workspace (e.g., 'f/scripts/send_email')"},"hash":{"type":"string","description":"Optional specific version hash of the script to use"},"type":{"type":"string","enum":["script"]},"tag_override":{"type":"string","description":"Override the script's default worker group tag"},"is_trigger":{"type":"boolean","description":"If true, this script is a trigger that can start the flow"}},"required":["type","path","input_transforms"]},"PathFlow":{"type":"object","description":"Reference to an existing flow by path. Use this to call another flow as a subflow","properties":{"input_transforms":{"type":"object","description":"Map of parameter names to their values (static or JavaScript expressions). These become the subflow's input arguments","additionalProperties":{"$ref":"#/components/schemas/InputTransform"}},"path":{"type":"string","description":"Path to the flow in the workspace (e.g., 'f/flows/process_user')"},"type":{"type":"string","enum":["flow"]}},"required":["type","path","input_transforms"]},"ForloopFlow":{"type":"object","description":"Executes nested modules in a loop over an iterator. Inside the loop, use 'flow_input.iter.value' to access the current iteration value, and 'flow_input.iter.index' for the index. Supports parallel execution for better performance on I/O-bound operations","properties":{"modules":{"type":"array","description":"Steps to execute for each iteration. These can reference the iteration value via 'flow_input.iter.value'","items":{"$ref":"#/components/schemas/FlowModule"}},"iterator":{"description":"JavaScript expression that returns an array to iterate over. Can reference 'results.step_id' or 'flow_input'","$ref":"#/components/schemas/InputTransform"},"skip_failures":{"type":"boolean","description":"If true, iteration failures don't stop the loop. Failed iterations return null"},"type":{"type":"string","enum":["forloopflow"]},"parallel":{"type":"boolean","description":"If true, iterations run concurrently (faster for I/O-bound operations). Use with parallelism to control concurrency"},"parallelism":{"description":"Maximum number of concurrent iterations when parallel=true. Limits resource usage. Can be static number or expression","$ref":"#/components/schemas/InputTransform"},"squash":{"type":"boolean"}},"required":["modules","iterator","skip_failures","type"]},"WhileloopFlow":{"type":"object","description":"Executes nested modules repeatedly while a condition is true. The loop checks the condition after each iteration. Use stop_after_if on modules to control loop termination","properties":{"modules":{"type":"array","description":"Steps to execute in each iteration. Use stop_after_if to control when the loop ends","items":{"$ref":"#/components/schemas/FlowModule"}},"skip_failures":{"type":"boolean","description":"If true, iteration failures don't stop the loop. Failed iterations return null"},"type":{"type":"string","enum":["whileloopflow"]},"parallel":{"type":"boolean","description":"If true, iterations run concurrently (use with caution in while loops)"},"parallelism":{"description":"Maximum number of concurrent iterations when parallel=true","$ref":"#/components/schemas/InputTransform"},"squash":{"type":"boolean"}},"required":["modules","skip_failures","type"]},"BranchOne":{"type":"object","description":"Conditional branching where only the first matching branch executes. Branches are evaluated in order, and the first one with a true expression runs. If no branches match, the default branch executes","properties":{"branches":{"type":"array","description":"Array of branches to evaluate in order. The first branch with expr evaluating to true executes","items":{"type":"object","properties":{"summary":{"type":"string","description":"Short description of this branch condition"},"expr":{"type":"string","description":"JavaScript expression that returns boolean. Can use 'results.step_id' or 'flow_input'. First true expr wins"},"modules":{"type":"array","description":"Steps to execute if this branch's expr is true","items":{"$ref":"#/components/schemas/FlowModule"}}},"required":["modules","expr"]}},"default":{"type":"array","description":"Steps to execute if no branch expressions match","items":{"$ref":"#/components/schemas/FlowModule"}},"type":{"type":"string","enum":["branchone"]}},"required":["branches","default","type"]},"BranchAll":{"type":"object","description":"Parallel branching where all branches execute simultaneously. Unlike BranchOne, all branches run regardless of conditions. Useful for executing independent tasks concurrently","properties":{"branches":{"type":"array","description":"Array of branches that all execute (either in parallel or sequentially)","items":{"type":"object","properties":{"summary":{"type":"string","description":"Short description of this branch's purpose"},"skip_failure":{"type":"boolean","description":"If true, failure in this branch doesn't fail the entire flow"},"modules":{"type":"array","description":"Steps to execute in this branch","items":{"$ref":"#/components/schemas/FlowModule"}}},"required":["modules"]}},"type":{"type":"string","enum":["branchall"]},"parallel":{"type":"boolean","description":"If true, all branches execute concurrently. If false, they execute sequentially"}},"required":["branches","type"]},"AgentTool":{"type":"object","description":"A tool available to an AI agent. Can be a flow module or an external MCP (Model Context Protocol) tool","properties":{"id":{"type":"string","description":"Unique identifier for this tool. Cannot contain spaces - use underscores instead (e.g., 'get_user_data' not 'get user data')"},"summary":{"type":"string","description":"Short description of what this tool does (shown to the AI)"},"value":{"$ref":"#/components/schemas/ToolValue"}},"required":["id","value"]},"ToolValue":{"description":"The implementation of a tool. Can be a flow module (script/flow) or an MCP tool reference","oneOf":[{"$ref":"#/components/schemas/FlowModuleTool"},{"$ref":"#/components/schemas/McpToolValue"},{"$ref":"#/components/schemas/WebsearchToolValue"}],"discriminator":{"propertyName":"tool_type","mapping":{"flowmodule":"#/components/schemas/FlowModuleTool","mcp":"#/components/schemas/McpToolValue","websearch":"#/components/schemas/WebsearchToolValue"}}},"FlowModuleTool":{"description":"A tool implemented as a flow module (script, flow, etc.). The AI can call this like any other flow module","allOf":[{"type":"object","properties":{"tool_type":{"type":"string","enum":["flowmodule"]}},"required":["tool_type"]},{"$ref":"#/components/schemas/FlowModuleValue"}]},"WebsearchToolValue":{"type":"object","description":"A tool implemented as a websearch tool. The AI can call this like any other websearch tool","properties":{"tool_type":{"type":"string","enum":["websearch"]}},"required":["tool_type"]},"McpToolValue":{"type":"object","description":"Reference to an external MCP (Model Context Protocol) tool. The AI can call tools from MCP servers","properties":{"tool_type":{"type":"string","enum":["mcp"]},"resource_path":{"type":"string","description":"Path to the MCP resource/server configuration"},"include_tools":{"type":"array","description":"Whitelist of specific tools to include from this MCP server","items":{"type":"string"}},"exclude_tools":{"type":"array","description":"Blacklist of tools to exclude from this MCP server","items":{"type":"string"}}},"required":["tool_type","resource_path"]},"AiAgent":{"type":"object","description":"AI agent step that can use tools to accomplish tasks. The agent receives inputs and can call any of its configured tools to complete the task","properties":{"input_transforms":{"type":"object","description":"Input parameters for the AI agent mapped to their values","properties":{"provider":{"$ref":"#/components/schemas/ProviderTransform"},"output_type":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"Output format type.\\nValid values: 'text' (default) - plain text response, 'image' - image generation\\n"},"user_message":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"The user's prompt/message to the AI agent. Supports variable interpolation with flow.input syntax."},"system_prompt":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"System instructions that guide the AI's behavior, persona, and response style. Optional."},"streaming":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"Boolean. If true, stream the AI response incrementally.\\nStreaming events include: token_delta, tool_call, tool_call_arguments, tool_execution, tool_result\\n"},"memory":{"$ref":"#/components/schemas/MemoryTransform"},"output_schema":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"JSON Schema object defining structured output format. Used when you need the AI to return data in a specific shape.\\nSupports standard JSON Schema properties: type, properties, required, items, enum, pattern, minLength, maxLength, minimum, maximum, etc.\\nExample: { type: 'object', properties: { name: { type: 'string' }, age: { type: 'integer' } }, required: ['name'] }\\n"},"user_attachments":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"Array of file references (images or PDFs) for the AI agent.\\nFormat: Array<{ bucket: string, key: string }> - S3 object references\\nExample: [{ bucket: 'my-bucket', key: 'documents/report.pdf' }]\\n"},"max_completion_tokens":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"Integer. Maximum number of tokens the AI will generate in its response.\\nRange: 1 to 4,294,967,295. Typical values: 256-4096 for most use cases.\\n"},"temperature":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"Float. Controls randomness/creativity of responses.\\nRange: 0.0 to 2.0 (provider-dependent)\\n- 0.0 = deterministic, focused responses\\n- 0.7 = balanced (common default)\\n- 1.0+ = more creative/random\\n"}},"required":["provider","user_message","output_type"]},"tools":{"type":"array","description":"Array of tools the agent can use. The agent decides which tools to call based on the task","items":{"$ref":"#/components/schemas/AgentTool"}},"type":{"type":"string","enum":["aiagent"]},"parallel":{"type":"boolean","description":"If true, the agent can execute multiple tool calls in parallel"}},"required":["tools","type","input_transforms"]},"Identity":{"type":"object","description":"Pass-through module that returns its input unchanged. Useful for flow structure or as a placeholder","properties":{"type":{"type":"string","enum":["identity"]},"flow":{"type":"boolean","description":"If true, marks this as a flow identity (special handling)"}},"required":["type"]},"FlowStatus":{"type":"object","properties":{"step":{"type":"integer"},"modules":{"type":"array","items":{"$ref":"#/components/schemas/FlowStatusModule"}},"user_states":{"additionalProperties":true},"preprocessor_module":{"allOf":[{"$ref":"#/components/schemas/FlowStatusModule"}]},"failure_module":{"allOf":[{"$ref":"#/components/schemas/FlowStatusModule"},{"type":"object","properties":{"parent_module":{"type":"string"}}}]},"retry":{"type":"object","properties":{"fail_count":{"type":"integer"},"failed_jobs":{"type":"array","items":{"type":"string","format":"uuid"}}}}},"required":["step","modules","failure_module"]},"FlowStatusModule":{"type":"object","properties":{"type":{"type":"string","enum":["WaitingForPriorSteps","WaitingForEvents","WaitingForExecutor","InProgress","Success","Failure"]},"id":{"type":"string"},"job":{"type":"string","format":"uuid"},"count":{"type":"integer"},"progress":{"type":"integer"},"iterator":{"type":"object","properties":{"index":{"type":"integer"},"itered":{"type":"array","items":{}},"itered_len":{"type":"integer"},"args":{}}},"flow_jobs":{"type":"array","items":{"type":"string"}},"flow_jobs_success":{"type":"array","items":{"type":"boolean"}},"flow_jobs_duration":{"type":"object","properties":{"started_at":{"type":"array","items":{"type":"string"}},"duration_ms":{"type":"array","items":{"type":"integer"}}}},"branch_chosen":{"type":"object","properties":{"type":{"type":"string","enum":["branch","default"]},"branch":{"type":"integer"}},"required":["type"]},"branchall":{"type":"object","properties":{"branch":{"type":"integer"},"len":{"type":"integer"}},"required":["branch","len"]},"approvers":{"type":"array","items":{"type":"object","properties":{"resume_id":{"type":"integer"},"approver":{"type":"string"}},"required":["resume_id","approver"]}},"failed_retries":{"type":"array","items":{"type":"string","format":"uuid"}},"skipped":{"type":"boolean"},"agent_actions":{"type":"array","items":{"type":"object","oneOf":[{"type":"object","properties":{"job_id":{"type":"string","format":"uuid"},"function_name":{"type":"string"},"type":{"type":"string","enum":["tool_call"]},"module_id":{"type":"string"}},"required":["job_id","function_name","type","module_id"]},{"type":"object","properties":{"call_id":{"type":"string","format":"uuid"},"function_name":{"type":"string"},"resource_path":{"type":"string"},"type":{"type":"string","enum":["mcp_tool_call"]},"arguments":{"type":"object"}},"required":["call_id","function_name","resource_path","type"]},{"type":"object","properties":{"type":{"type":"string","enum":["web_search"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["message"]}},"required":["content","type"]}]}},"agent_actions_success":{"type":"array","items":{"type":"boolean"}}},"required":["type"]}}`,
78903
+ {"OpenFlow":{"type":"object","description":"Top-level flow definition containing metadata, configuration, and the flow structure","properties":{"summary":{"type":"string","description":"Short description of what this flow does"},"description":{"type":"string","description":"Detailed documentation for this flow"},"value":{"$ref":"#/components/schemas/FlowValue"},"schema":{"type":"object","description":"JSON Schema for flow inputs. Use this to define input parameters, their types, defaults, and validation. For resource inputs, set type to 'object' and format to 'resource-<type>' (e.g., 'resource-stripe')"},"on_behalf_of_email":{"type":"string","description":"The flow will be run with the permissions of the user with this email."}},"required":["summary","value"]},"FlowValue":{"type":"object","description":"The flow structure containing modules and optional preprocessor/failure handlers","properties":{"modules":{"type":"array","description":"Array of steps that execute in sequence. Each step can be a script, subflow, loop, or branch","items":{"$ref":"#/components/schemas/FlowModule"}},"failure_module":{"description":"Special module that executes when the flow fails. Receives error object with message, name, stack, and step_id. Must have id 'failure'. Only supports script/rawscript types","$ref":"#/components/schemas/FlowModule"},"preprocessor_module":{"description":"Special module that runs before the first step on external triggers. Must have id 'preprocessor'. Only supports script/rawscript types. Cannot reference other step results","$ref":"#/components/schemas/FlowModule"},"same_worker":{"type":"boolean","description":"If true, all steps run on the same worker for better performance"},"concurrent_limit":{"type":"number","description":"Maximum number of concurrent executions of this flow"},"concurrency_key":{"type":"string","description":"Expression to group concurrent executions (e.g., by user ID)"},"concurrency_time_window_s":{"type":"number","description":"Time window in seconds for concurrent_limit"},"debounce_delay_s":{"type":"integer","description":"Delay in seconds to debounce flow executions"},"debounce_key":{"type":"string","description":"Expression to group debounced executions"},"debounce_args_to_accumulate":{"type":"array","description":"Arguments to accumulate across debounced executions","items":{"type":"string"}},"max_total_debouncing_time":{"type":"integer","description":"Maximum total time in seconds that a job can be debounced"},"max_total_debounces_amount":{"type":"integer","description":"Maximum number of times a job can be debounced"},"skip_expr":{"type":"string","description":"JavaScript expression to conditionally skip the entire flow"},"cache_ttl":{"type":"number","description":"Cache duration in seconds for flow results"},"cache_ignore_s3_path":{"type":"boolean"},"flow_env":{"type":"object","description":"Environment variables available to all steps. Values can be strings, JSON values, or special references: '$var:path' (workspace variable) or '$res:path' (resource).","additionalProperties":{}},"priority":{"type":"number","description":"Execution priority (higher numbers run first)"},"early_return":{"type":"string","description":"JavaScript expression to return early from the flow"},"chat_input_enabled":{"type":"boolean","description":"Whether this flow accepts chat-style input"},"notes":{"type":"array","description":"Sticky notes attached to the flow","items":{"$ref":"#/components/schemas/FlowNote"}},"groups":{"type":"array","description":"Semantic groups of modules for organizational purposes","items":{"$ref":"#/components/schemas/FlowGroup"}}},"required":["modules"]},"Retry":{"type":"object","description":"Retry configuration for failed module executions","properties":{"constant":{"type":"object","description":"Retry with constant delay between attempts","properties":{"attempts":{"type":"integer","description":"Number of retry attempts"},"seconds":{"type":"integer","description":"Seconds to wait between retries"}}},"exponential":{"type":"object","description":"Retry with exponential backoff (delay doubles each time)","properties":{"attempts":{"type":"integer","description":"Number of retry attempts"},"multiplier":{"type":"integer","description":"Multiplier for exponential backoff"},"seconds":{"type":"integer","minimum":1,"description":"Initial delay in seconds"},"random_factor":{"type":"integer","minimum":0,"maximum":100,"description":"Random jitter percentage (0-100) to avoid thundering herd"}}},"retry_if":{"$ref":"#/components/schemas/RetryIf"}}},"FlowNote":{"type":"object","description":"A sticky note attached to a flow for documentation and annotation","properties":{"id":{"type":"string","description":"Unique identifier for the note"},"text":{"type":"string","description":"Content of the note"},"position":{"type":"object","description":"Position of the note in the flow editor","properties":{"x":{"type":"number","description":"X coordinate"},"y":{"type":"number","description":"Y coordinate"}},"required":["x","y"]},"size":{"type":"object","description":"Size of the note in the flow editor","properties":{"width":{"type":"number","description":"Width in pixels"},"height":{"type":"number","description":"Height in pixels"}},"required":["width","height"]},"color":{"type":"string","description":"Color of the note (e.g., \\"yellow\\", \\"#ffff00\\")"},"type":{"type":"string","enum":["free","group"],"description":"Type of note - 'free' for standalone notes, 'group' for notes that group other nodes"},"locked":{"type":"boolean","default":false,"description":"Whether the note is locked and cannot be edited or moved"},"contained_node_ids":{"type":"array","items":{"type":"string"},"description":"For group notes, the IDs of nodes contained within this group"}},"required":["id","text","color","type"]},"FlowGroup":{"type":"object","description":"A semantic group of flow modules for organizational purposes. Does not affect execution \\u2014 modules remain in their original position in the flow. Groups provide naming and collapsibility in the editor. Members are computed dynamically from all nodes on paths between start_id and end_id.","properties":{"summary":{"type":"string","description":"Display name for this group"},"note":{"type":"string","description":"Markdown note shown below the group header"},"autocollapse":{"type":"boolean","default":false,"description":"If true, this group is collapsed by default in the flow editor. UI hint only."},"start_id":{"type":"string","description":"ID of the first flow module in this group (topological entry point)"},"end_id":{"type":"string","description":"ID of the last flow module in this group (topological exit point)"},"color":{"type":"string","description":"Color for the group in the flow editor"}},"required":["start_id","end_id"]},"RetryIf":{"type":"object","description":"Conditional retry based on error or result","properties":{"expr":{"type":"string","description":"JavaScript expression that returns true to retry. Has access to 'result' and 'error' variables"}},"required":["expr"]},"StopAfterIf":{"type":"object","description":"Early termination condition for a module","properties":{"skip_if_stopped":{"type":"boolean","description":"If true, following steps are skipped when this condition triggers"},"expr":{"type":"string","description":"JavaScript expression evaluated after the module runs. Can use 'result' (step's result) or 'flow_input'. Return true to stop"},"error_message":{"type":"string","nullable":true,"description":"Custom error message when stopping with an error. Mutually exclusive with skip_if_stopped. If set to a non-empty string, the flow stops with this error. If empty string, a default error message is used. If null or omitted, no error is raised."}},"required":["expr"]},"FlowModule":{"type":"object","description":"A single step in a flow. Can be a script, subflow, loop, or branch","properties":{"id":{"type":"string","description":"Unique identifier for this step. Used to reference results via 'results.step_id'. Must be a valid identifier (alphanumeric, underscore, hyphen)"},"value":{"$ref":"#/components/schemas/FlowModuleValue"},"stop_after_if":{"description":"Early termination condition evaluated after this step completes","$ref":"#/components/schemas/StopAfterIf"},"stop_after_all_iters_if":{"description":"For loops only - early termination condition evaluated after all iterations complete","$ref":"#/components/schemas/StopAfterIf"},"skip_if":{"type":"object","description":"Conditionally skip this step based on previous results or flow inputs","properties":{"expr":{"type":"string","description":"JavaScript expression that returns true to skip. Can use 'flow_input' or 'results.<step_id>'"}},"required":["expr"]},"sleep":{"description":"Delay before executing this step (in seconds or as expression)","$ref":"#/components/schemas/InputTransform"},"cache_ttl":{"type":"number","description":"Cache duration in seconds for this step's results"},"cache_ignore_s3_path":{"type":"boolean"},"timeout":{"description":"Maximum execution time in seconds (static value or expression)","$ref":"#/components/schemas/InputTransform"},"delete_after_use":{"type":"boolean","description":"If true, this step's result is deleted after use to save memory"},"summary":{"type":"string","description":"Short description of what this step does"},"mock":{"type":"object","description":"Mock configuration for testing without executing the actual step","properties":{"enabled":{"type":"boolean","description":"If true, return mock value instead of executing"},"return_value":{"description":"Value to return when mocked"}}},"suspend":{"type":"object","description":"Configuration for approval/resume steps that wait for user input","properties":{"required_events":{"type":"integer","description":"Number of approvals required before continuing"},"timeout":{"type":"integer","description":"Timeout in seconds before auto-continuing or canceling"},"resume_form":{"type":"object","description":"Form schema for collecting input when resuming","properties":{"schema":{"type":"object","description":"JSON Schema for the resume form"}}},"user_auth_required":{"type":"boolean","description":"If true, only authenticated users can approve"},"user_groups_required":{"description":"Expression or list of groups that can approve","$ref":"#/components/schemas/InputTransform"},"self_approval_disabled":{"type":"boolean","description":"If true, the user who started the flow cannot approve"},"hide_cancel":{"type":"boolean","description":"If true, hide the cancel button on the approval form"},"continue_on_disapprove_timeout":{"type":"boolean","description":"If true, continue flow on timeout instead of canceling"}}},"priority":{"type":"number","description":"Execution priority for this step (higher numbers run first)"},"continue_on_error":{"type":"boolean","description":"If true, flow continues even if this step fails"},"retry":{"description":"Retry configuration if this step fails","$ref":"#/components/schemas/Retry"},"debouncing":{"description":"Debounce configuration for this step (EE only)","type":"object","properties":{"debounce_delay_s":{"type":"integer","description":"Delay in seconds to debounce this step's executions across flow runs"},"debounce_key":{"type":"string","description":"Expression to group debounced executions. Supports $workspace and $args[name]. Default: $workspace/flow/<flow_path>-<step_id>"},"debounce_args_to_accumulate":{"type":"array","description":"Array-type arguments to accumulate across debounced executions","items":{"type":"string"}},"max_total_debouncing_time":{"type":"integer","description":"Maximum total time in seconds before forced execution"},"max_total_debounces_amount":{"type":"integer","description":"Maximum number of debounces before forced execution"}}}},"required":["value","id"]},"InputTransform":{"description":"Maps input parameters for a step. Can be a static value or a JavaScript expression that references previous results or flow inputs","oneOf":[{"$ref":"#/components/schemas/StaticTransform"},{"$ref":"#/components/schemas/JavascriptTransform"},{"$ref":"#/components/schemas/AiTransform"}],"discriminator":{"propertyName":"type","mapping":{"static":"#/components/schemas/StaticTransform","javascript":"#/components/schemas/JavascriptTransform","ai":"#/components/schemas/AiTransform"}}},"StaticTransform":{"type":"object","description":"Static value passed directly to the step. Use for hardcoded values or resource references like '$res:path/to/resource'","properties":{"value":{"description":"The static value. For resources, use format '$res:path/to/resource'"},"type":{"type":"string","enum":["static"]}},"required":["type"]},"JavascriptTransform":{"type":"object","description":"JavaScript expression evaluated at runtime. Can reference previous step results via 'results.step_id' or flow inputs via 'flow_input.property'. Inside loops, use 'flow_input.iter.value' for the current iteration value","properties":{"expr":{"type":"string","description":"JavaScript expression returning the value. Available variables - results (object with all previous step results), flow_input (flow inputs), flow_input.iter (in loops)"},"type":{"type":"string","enum":["javascript"]}},"required":["expr","type"]},"AiTransform":{"type":"object","description":"Value resolved by the AI runtime for this input. The AI engine decides how to satisfy the parameter.","properties":{"type":{"type":"string","enum":["ai"]}},"required":["type"]},"AIProviderKind":{"type":"string","description":"Supported AI provider types","enum":["openai","azure_openai","anthropic","mistral","deepseek","googleai","groq","openrouter","togetherai","customai","aws_bedrock"]},"ProviderConfig":{"type":"object","description":"Complete AI provider configuration with resource reference and model selection","properties":{"kind":{"$ref":"#/components/schemas/AIProviderKind"},"resource":{"type":"string","description":"Resource reference in format '$res:{resource_path}' pointing to provider credentials"},"model":{"type":"string","description":"Model identifier (e.g., 'gpt-4', 'claude-3-opus-20240229', 'gemini-pro')"}},"required":["kind","resource","model"]},"StaticProviderTransform":{"type":"object","description":"Static provider configuration passed directly to the AI agent","properties":{"value":{"$ref":"#/components/schemas/ProviderConfig"},"type":{"type":"string","enum":["static"]}},"required":["type","value"]},"ProviderTransform":{"description":"Provider configuration - can be static (ProviderConfig), JavaScript expression, or AI-determined","oneOf":[{"$ref":"#/components/schemas/StaticProviderTransform"},{"$ref":"#/components/schemas/JavascriptTransform"},{"$ref":"#/components/schemas/AiTransform"}],"discriminator":{"propertyName":"type","mapping":{"static":"#/components/schemas/StaticProviderTransform","javascript":"#/components/schemas/JavascriptTransform","ai":"#/components/schemas/AiTransform"}}},"MemoryOff":{"type":"object","description":"No conversation memory/context","properties":{"kind":{"type":"string","enum":["off"]}},"required":["kind"]},"MemoryAuto":{"type":"object","description":"Automatic context management","properties":{"kind":{"type":"string","enum":["auto"]},"context_length":{"type":"integer","description":"Maximum number of messages to retain in context"},"memory_id":{"type":"string","description":"Identifier for persistent memory across agent invocations"}},"required":["kind"]},"MemoryMessage":{"type":"object","description":"A single message in conversation history","properties":{"role":{"type":"string","enum":["user","assistant","system"]},"content":{"type":"string"}},"required":["role","content"]},"MemoryManual":{"type":"object","description":"Explicit message history","properties":{"kind":{"type":"string","enum":["manual"]},"messages":{"type":"array","items":{"$ref":"#/components/schemas/MemoryMessage"}}},"required":["kind","messages"]},"MemoryConfig":{"description":"Conversation memory configuration","oneOf":[{"$ref":"#/components/schemas/MemoryOff"},{"$ref":"#/components/schemas/MemoryAuto"},{"$ref":"#/components/schemas/MemoryManual"}],"discriminator":{"propertyName":"kind","mapping":{"off":"#/components/schemas/MemoryOff","auto":"#/components/schemas/MemoryAuto","manual":"#/components/schemas/MemoryManual"}}},"StaticMemoryTransform":{"type":"object","description":"Static memory configuration passed directly to the AI agent","properties":{"value":{"$ref":"#/components/schemas/MemoryConfig"},"type":{"type":"string","enum":["static"]}},"required":["type","value"]},"MemoryTransform":{"description":"Memory configuration - can be static (MemoryConfig), JavaScript expression, or AI-determined","oneOf":[{"$ref":"#/components/schemas/StaticMemoryTransform"},{"$ref":"#/components/schemas/JavascriptTransform"},{"$ref":"#/components/schemas/AiTransform"}],"discriminator":{"propertyName":"type","mapping":{"static":"#/components/schemas/StaticMemoryTransform","javascript":"#/components/schemas/JavascriptTransform","ai":"#/components/schemas/AiTransform"}}},"FlowModuleValue":{"description":"The actual implementation of a flow step. Can be a script (inline or referenced), subflow, loop, branch, or special module type","oneOf":[{"$ref":"#/components/schemas/RawScript"},{"$ref":"#/components/schemas/PathScript"},{"$ref":"#/components/schemas/PathFlow"},{"$ref":"#/components/schemas/ForloopFlow"},{"$ref":"#/components/schemas/WhileloopFlow"},{"$ref":"#/components/schemas/BranchOne"},{"$ref":"#/components/schemas/BranchAll"},{"$ref":"#/components/schemas/Identity"},{"$ref":"#/components/schemas/AiAgent"}],"discriminator":{"propertyName":"type","mapping":{"rawscript":"#/components/schemas/RawScript","script":"#/components/schemas/PathScript","flow":"#/components/schemas/PathFlow","forloopflow":"#/components/schemas/ForloopFlow","whileloopflow":"#/components/schemas/WhileloopFlow","branchone":"#/components/schemas/BranchOne","branchall":"#/components/schemas/BranchAll","identity":"#/components/schemas/Identity","aiagent":"#/components/schemas/AiAgent"}}},"RawScript":{"type":"object","description":"Inline script with code defined directly in the flow. Use 'bun' as default language if unspecified. The script receives arguments from input_transforms","properties":{"input_transforms":{"type":"object","description":"Map of parameter names to their values (static or JavaScript expressions). These become the script's input arguments","additionalProperties":{"$ref":"#/components/schemas/InputTransform"}},"content":{"type":"string","description":"The script source code. Should export a 'main' function"},"language":{"type":"string","description":"Programming language for this script","enum":["deno","bun","python3","go","bash","powershell","postgresql","mysql","bigquery","snowflake","mssql","oracledb","graphql","nativets","php","rust","ansible","csharp","nu","java","ruby","rlang","duckdb"]},"path":{"type":"string","description":"Optional path for saving this script"},"lock":{"type":"string","description":"Lock file content for dependencies"},"type":{"type":"string","enum":["rawscript"]},"tag":{"type":"string","description":"Worker group tag for execution routing"},"concurrent_limit":{"type":"number","description":"Maximum concurrent executions of this script"},"concurrency_time_window_s":{"type":"number","description":"Time window for concurrent_limit"},"custom_concurrency_key":{"type":"string","description":"Custom key for grouping concurrent executions"},"is_trigger":{"type":"boolean","description":"If true, this script is a trigger that can start the flow"},"assets":{"type":"array","description":"External resources this script accesses (S3 objects, resources, etc.)","items":{"type":"object","required":["path","kind"],"properties":{"path":{"type":"string","description":"Path to the asset"},"kind":{"type":"string","description":"Type of asset","enum":["s3object","resource","ducklake","datatable","volume"]},"access_type":{"type":"string","nullable":true,"description":"Access level for this asset","enum":["r","w","rw"]},"alt_access_type":{"type":"string","nullable":true,"description":"Alternative access level","enum":["r","w","rw"]}}}}},"required":["type","content","language","input_transforms"]},"PathScript":{"type":"object","description":"Reference to an existing script by path. Use this when calling a previously saved script instead of writing inline code","properties":{"input_transforms":{"type":"object","description":"Map of parameter names to their values (static or JavaScript expressions). These become the script's input arguments","additionalProperties":{"$ref":"#/components/schemas/InputTransform"}},"path":{"type":"string","description":"Path to the script in the workspace (e.g., 'f/scripts/send_email')"},"hash":{"type":"string","description":"Optional specific version hash of the script to use"},"type":{"type":"string","enum":["script"]},"tag_override":{"type":"string","description":"Override the script's default worker group tag"},"is_trigger":{"type":"boolean","description":"If true, this script is a trigger that can start the flow"}},"required":["type","path","input_transforms"]},"PathFlow":{"type":"object","description":"Reference to an existing flow by path. Use this to call another flow as a subflow","properties":{"input_transforms":{"type":"object","description":"Map of parameter names to their values (static or JavaScript expressions). These become the subflow's input arguments","additionalProperties":{"$ref":"#/components/schemas/InputTransform"}},"path":{"type":"string","description":"Path to the flow in the workspace (e.g., 'f/flows/process_user')"},"type":{"type":"string","enum":["flow"]}},"required":["type","path","input_transforms"]},"ForloopFlow":{"type":"object","description":"Executes nested modules in a loop over an iterator. Inside the loop, use 'flow_input.iter.value' to access the current iteration value, and 'flow_input.iter.index' for the index. Supports parallel execution for better performance on I/O-bound operations","properties":{"modules":{"type":"array","description":"Steps to execute for each iteration. These can reference the iteration value via 'flow_input.iter.value'","items":{"$ref":"#/components/schemas/FlowModule"}},"iterator":{"description":"JavaScript expression that returns an array to iterate over. Can reference 'results.step_id' or 'flow_input'","$ref":"#/components/schemas/InputTransform"},"skip_failures":{"type":"boolean","description":"If true, iteration failures don't stop the loop. Failed iterations return null"},"type":{"type":"string","enum":["forloopflow"]},"parallel":{"type":"boolean","description":"If true, iterations run concurrently (faster for I/O-bound operations). Use with parallelism to control concurrency"},"parallelism":{"description":"Maximum number of concurrent iterations when parallel=true. Limits resource usage. Can be static number or expression","$ref":"#/components/schemas/InputTransform"},"squash":{"type":"boolean"}},"required":["modules","iterator","skip_failures","type"]},"WhileloopFlow":{"type":"object","description":"Executes nested modules repeatedly while a condition is true. The loop checks the condition after each iteration. Use stop_after_if on modules to control loop termination","properties":{"modules":{"type":"array","description":"Steps to execute in each iteration. Use stop_after_if to control when the loop ends","items":{"$ref":"#/components/schemas/FlowModule"}},"skip_failures":{"type":"boolean","description":"If true, iteration failures don't stop the loop. Failed iterations return null"},"type":{"type":"string","enum":["whileloopflow"]},"parallel":{"type":"boolean","description":"If true, iterations run concurrently (use with caution in while loops)"},"parallelism":{"description":"Maximum number of concurrent iterations when parallel=true","$ref":"#/components/schemas/InputTransform"},"squash":{"type":"boolean"}},"required":["modules","skip_failures","type"]},"BranchOne":{"type":"object","description":"Conditional branching where only the first matching branch executes. Branches are evaluated in order, and the first one with a true expression runs. If no branches match, the default branch executes","properties":{"branches":{"type":"array","description":"Array of branches to evaluate in order. The first branch with expr evaluating to true executes","items":{"type":"object","properties":{"summary":{"type":"string","description":"Short description of this branch condition"},"expr":{"type":"string","description":"JavaScript expression that returns boolean. Can use 'results.step_id' or 'flow_input'. First true expr wins"},"modules":{"type":"array","description":"Steps to execute if this branch's expr is true","items":{"$ref":"#/components/schemas/FlowModule"}}},"required":["modules","expr"]}},"default":{"type":"array","description":"Steps to execute if no branch expressions match","items":{"$ref":"#/components/schemas/FlowModule"}},"type":{"type":"string","enum":["branchone"]}},"required":["branches","default","type"]},"BranchAll":{"type":"object","description":"Parallel branching where all branches execute simultaneously. Unlike BranchOne, all branches run regardless of conditions. Useful for executing independent tasks concurrently","properties":{"branches":{"type":"array","description":"Array of branches that all execute (either in parallel or sequentially)","items":{"type":"object","properties":{"summary":{"type":"string","description":"Short description of this branch's purpose"},"skip_failure":{"type":"boolean","description":"If true, failure in this branch doesn't fail the entire flow"},"modules":{"type":"array","description":"Steps to execute in this branch","items":{"$ref":"#/components/schemas/FlowModule"}}},"required":["modules"]}},"type":{"type":"string","enum":["branchall"]},"parallel":{"type":"boolean","description":"If true, all branches execute concurrently. If false, they execute sequentially"}},"required":["branches","type"]},"AgentTool":{"type":"object","description":"A tool available to an AI agent. Can be a flow module or an external MCP (Model Context Protocol) tool","properties":{"id":{"type":"string","description":"Unique identifier for this tool. Cannot contain spaces - use underscores instead (e.g., 'get_user_data' not 'get user data')"},"summary":{"type":"string","description":"Short description of what this tool does (shown to the AI)"},"value":{"$ref":"#/components/schemas/ToolValue"}},"required":["id","value"]},"ToolValue":{"description":"The implementation of a tool. Can be a flow module (script/flow) or an MCP tool reference","oneOf":[{"$ref":"#/components/schemas/FlowModuleTool"},{"$ref":"#/components/schemas/McpToolValue"},{"$ref":"#/components/schemas/WebsearchToolValue"}],"discriminator":{"propertyName":"tool_type","mapping":{"flowmodule":"#/components/schemas/FlowModuleTool","mcp":"#/components/schemas/McpToolValue","websearch":"#/components/schemas/WebsearchToolValue"}}},"FlowModuleTool":{"description":"A tool implemented as a flow module (script, flow, etc.). The AI can call this like any other flow module","allOf":[{"type":"object","properties":{"tool_type":{"type":"string","enum":["flowmodule"]}},"required":["tool_type"]},{"$ref":"#/components/schemas/FlowModuleValue"}]},"WebsearchToolValue":{"type":"object","description":"A tool implemented as a websearch tool. The AI can call this like any other websearch tool","properties":{"tool_type":{"type":"string","enum":["websearch"]}},"required":["tool_type"]},"McpToolValue":{"type":"object","description":"Reference to an external MCP (Model Context Protocol) tool. The AI can call tools from MCP servers","properties":{"tool_type":{"type":"string","enum":["mcp"]},"resource_path":{"type":"string","description":"Path to the MCP resource/server configuration"},"include_tools":{"type":"array","description":"Whitelist of specific tools to include from this MCP server","items":{"type":"string"}},"exclude_tools":{"type":"array","description":"Blacklist of tools to exclude from this MCP server","items":{"type":"string"}}},"required":["tool_type","resource_path"]},"AiAgent":{"type":"object","description":"AI agent step that can use tools to accomplish tasks. The agent receives inputs and can call any of its configured tools to complete the task","properties":{"input_transforms":{"type":"object","description":"Input parameters for the AI agent mapped to their values","properties":{"provider":{"$ref":"#/components/schemas/ProviderTransform"},"output_type":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"Output format type.\\nValid values: 'text' (default) - plain text response, 'image' - image generation\\n"},"user_message":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"The user's prompt/message to the AI agent. Supports variable interpolation with flow.input syntax."},"system_prompt":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"System instructions that guide the AI's behavior, persona, and response style. Optional."},"streaming":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"Boolean. If true, stream the AI response incrementally.\\nStreaming events include: token_delta, tool_call, tool_call_arguments, tool_execution, tool_result\\n"},"memory":{"$ref":"#/components/schemas/MemoryTransform"},"output_schema":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"JSON Schema object defining structured output format. Used when you need the AI to return data in a specific shape.\\nSupports standard JSON Schema properties: type, properties, required, items, enum, pattern, minLength, maxLength, minimum, maximum, etc.\\nExample: { type: 'object', properties: { name: { type: 'string' }, age: { type: 'integer' } }, required: ['name'] }\\n"},"user_attachments":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"Array of file references (images or PDFs) for the AI agent.\\nFormat: Array<{ bucket: string, key: string }> - S3 object references\\nExample: [{ bucket: 'my-bucket', key: 'documents/report.pdf' }]\\n"},"max_completion_tokens":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"Integer. Maximum number of tokens the AI will generate in its response.\\nRange: 1 to 4,294,967,295. Typical values: 256-4096 for most use cases.\\n"},"temperature":{"allOf":[{"$ref":"#/components/schemas/InputTransform"}],"description":"Float. Controls randomness/creativity of responses.\\nRange: 0.0 to 2.0 (provider-dependent)\\n- 0.0 = deterministic, focused responses\\n- 0.7 = balanced (common default)\\n- 1.0+ = more creative/random\\n"}},"required":["provider","user_message","output_type"]},"tools":{"type":"array","description":"Array of tools the agent can use. The agent decides which tools to call based on the task","items":{"$ref":"#/components/schemas/AgentTool"}},"type":{"type":"string","enum":["aiagent"]},"parallel":{"type":"boolean","description":"If true, the agent can execute multiple tool calls in parallel"}},"required":["tools","type","input_transforms"]},"Identity":{"type":"object","description":"Pass-through module that returns its input unchanged. Useful for flow structure or as a placeholder","properties":{"type":{"type":"string","enum":["identity"]},"flow":{"type":"boolean","description":"If true, marks this as a flow identity (special handling)"}},"required":["type"]},"FlowStatus":{"type":"object","properties":{"step":{"type":"integer"},"modules":{"type":"array","items":{"$ref":"#/components/schemas/FlowStatusModule"}},"user_states":{"additionalProperties":true},"preprocessor_module":{"allOf":[{"$ref":"#/components/schemas/FlowStatusModule"}]},"failure_module":{"allOf":[{"$ref":"#/components/schemas/FlowStatusModule"},{"type":"object","properties":{"parent_module":{"type":"string"}}}]},"retry":{"type":"object","properties":{"fail_count":{"type":"integer"},"failed_jobs":{"type":"array","items":{"type":"string","format":"uuid"}}}}},"required":["step","modules","failure_module"]},"FlowStatusModule":{"type":"object","properties":{"type":{"type":"string","enum":["WaitingForPriorSteps","WaitingForEvents","WaitingForExecutor","InProgress","Success","Failure"]},"id":{"type":"string"},"job":{"type":"string","format":"uuid"},"count":{"type":"integer"},"progress":{"type":"integer"},"iterator":{"type":"object","properties":{"index":{"type":"integer"},"itered":{"type":"array","items":{}},"itered_len":{"type":"integer"},"args":{}}},"flow_jobs":{"type":"array","items":{"type":"string"}},"flow_jobs_success":{"type":"array","items":{"type":"boolean"}},"flow_jobs_duration":{"type":"object","properties":{"started_at":{"type":"array","items":{"type":"string"}},"duration_ms":{"type":"array","items":{"type":"integer"}}}},"branch_chosen":{"type":"object","properties":{"type":{"type":"string","enum":["branch","default"]},"branch":{"type":"integer"}},"required":["type"]},"branchall":{"type":"object","properties":{"branch":{"type":"integer"},"len":{"type":"integer"}},"required":["branch","len"]},"approvers":{"type":"array","items":{"type":"object","properties":{"resume_id":{"type":"integer"},"approver":{"type":"string"}},"required":["resume_id","approver"]}},"failed_retries":{"type":"array","items":{"type":"string","format":"uuid"}},"skipped":{"type":"boolean"},"agent_actions":{"type":"array","items":{"type":"object","oneOf":[{"type":"object","properties":{"job_id":{"type":"string","format":"uuid"},"function_name":{"type":"string"},"type":{"type":"string","enum":["tool_call"]},"module_id":{"type":"string"}},"required":["job_id","function_name","type","module_id"]},{"type":"object","properties":{"call_id":{"type":"string","format":"uuid"},"function_name":{"type":"string"},"resource_path":{"type":"string"},"type":{"type":"string","enum":["mcp_tool_call"]},"arguments":{"type":"object"}},"required":["call_id","function_name","resource_path","type"]},{"type":"object","properties":{"type":{"type":"string","enum":["web_search"]}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["message"]}},"required":["content","type"]}]}},"agent_actions_success":{"type":"array","items":{"type":"boolean"}}},"required":["type"]}}`,
78722
78904
  "raw-app": `---
78723
78905
  name: raw-app
78724
78906
  description: MUST use when creating raw apps.
@@ -79433,7 +79615,7 @@ flow related commands
79433
79615
  - \`flow new <flow_path:string>\` - create a new empty flow
79434
79616
  - \`--summary <summary:string>\` - flow summary
79435
79617
  - \`--description <description:string>\` - flow description
79436
- - \`flow bootstrap <flow_path:string>\` - create a new empty flow (alias for new
79618
+ - \`flow bootstrap <flow_path:string>\` - create a new empty flow (alias for new)
79437
79619
  - \`--summary <summary:string>\` - flow summary
79438
79620
  - \`--description <description:string>\` - flow description
79439
79621
  - \`flow history <path:string>\` - Show version history for a flow
@@ -79586,10 +79768,10 @@ Manage jobs (list, inspect, cancel)
79586
79768
  **Subcommands:**
79587
79769
 
79588
79770
  - \`job list\` - List recent jobs
79589
- - \`job get <id:string>\` - Get job details and result
79771
+ - \`job get <id:string>\` - Get job details. For flows: shows step tree with sub-job IDs
79590
79772
  - \`--json\` - Output as JSON (for piping to jq)
79591
- - \`job result <id:string>\` - Get the result of a completed job (machine-friendly
79592
- - \`job logs <id:string>\` - Get job logs
79773
+ - \`job result <id:string>\` - Get the result of a completed job (machine-friendly)
79774
+ - \`job logs <id:string>\` - Get job logs. For flows: aggregates all step logs
79593
79775
  - \`job cancel <id:string>\` - Cancel a running or queued job
79594
79776
  - \`--reason <reason:string>\` - Reason for cancellation
79595
79777
 
@@ -79696,11 +79878,11 @@ script related commands
79696
79878
  - \`script list\` - list all scripts
79697
79879
  - \`--show-archived\` - Show archived scripts instead of active ones
79698
79880
  - \`--json\` - Output as JSON (for piping to jq)
79699
- - \`script push <path:file>\` - push a local script spec. This overrides any remote versions. Use the script file (.ts, .js, .py, .sh
79881
+ - \`script push <path:file>\` - push a local script spec. This overrides any remote versions. Use the script file (.ts, .js, .py, .sh)
79700
79882
  - \`--message <message:string>\` - Deployment message
79701
79883
  - \`script get <path:file>\` - get a script's details
79702
79884
  - \`--json\` - Output as JSON (for piping to jq)
79703
- - \`script show <path:file>\` - show a script's content (alias for get
79885
+ - \`script show <path:file>\` - show a script's content (alias for get)
79704
79886
  - \`script run <path:file>\` - run a script by path
79705
79887
  - \`-d --data <data:file>\` - Inputs specified as a JSON string or a file using @<filename> or stdin using @-.
79706
79888
  - \`-s --silent\` - Do not output anything other then the final output. Useful for scripting.
@@ -79710,10 +79892,10 @@ script related commands
79710
79892
  - \`script new <path:file> <language:string>\` - create a new script
79711
79893
  - \`--summary <summary:string>\` - script summary
79712
79894
  - \`--description <description:string>\` - script description
79713
- - \`script bootstrap <path:file> <language:string>\` - create a new script (alias for new
79895
+ - \`script bootstrap <path:file> <language:string>\` - create a new script (alias for new)
79714
79896
  - \`--summary <summary:string>\` - script summary
79715
79897
  - \`--description <description:string>\` - script description
79716
- - \`script generate-metadata [script:file]\` - re-generate the metadata file updating the lock and the script schema (for flows, use \`wmill flow generate-locks\`
79898
+ - \`script generate-metadata [script:file]\` - re-generate the metadata file updating the lock and the script schema (for flows, use \`wmill flow generate-locks\`)
79717
79899
  - \`--yes\` - Skip confirmation prompt
79718
79900
  - \`--dry-run\` - Perform a dry run without making changes
79719
79901
  - \`--lock-only\` - re-generate only the lock
@@ -81675,6 +81857,8 @@ async function list13(opts) {
81675
81857
  let successFilter = opts.success;
81676
81858
  if (opts.failed)
81677
81859
  successFilter = false;
81860
+ const showSubJobs = opts.all || opts.parent;
81861
+ const defaultJobKinds = showSubJobs ? "script,flow,singlestepflow,flowscript,flowdependencies" : "script,flow,singlestepflow";
81678
81862
  const limit = Math.min(opts.limit ?? 30, 100);
81679
81863
  const allJobs = await listJobs({
81680
81864
  workspace: workspace.workspaceId,
@@ -81683,9 +81867,11 @@ async function list13(opts) {
81683
81867
  running: opts.running,
81684
81868
  success: successFilter,
81685
81869
  perPage: limit,
81686
- jobKinds: opts.jobKinds ?? "script,flow,singlestepflow",
81870
+ jobKinds: opts.jobKinds ?? defaultJobKinds,
81687
81871
  label: opts.label,
81688
- hasNullParent: opts.all ? undefined : true
81872
+ hasNullParent: showSubJobs ? undefined : true,
81873
+ parentJob: opts.parent,
81874
+ isFlowStep: opts.isFlowStep
81689
81875
  });
81690
81876
  const jobs = allJobs.slice(0, limit);
81691
81877
  if (opts.json) {
@@ -81707,6 +81893,64 @@ async function list13(opts) {
81707
81893
  Showing ${jobs.length} job(s). Use --limit to show more.`);
81708
81894
  }
81709
81895
  }
81896
+ function getModuleStatusIcon(type, success) {
81897
+ switch (type) {
81898
+ case "Success":
81899
+ return colors.green("✓");
81900
+ case "Failure":
81901
+ return colors.red("✗");
81902
+ case "InProgress":
81903
+ return colors.blue("▶");
81904
+ case "WaitingForPriorSteps":
81905
+ return colors.dim("○");
81906
+ case "WaitingForEvents":
81907
+ return colors.yellow("⏳");
81908
+ default:
81909
+ return colors.dim("·");
81910
+ }
81911
+ }
81912
+ function formatFlowSteps(flowStatus, rawFlow) {
81913
+ const modules = flowStatus?.modules ?? [];
81914
+ const rawModules = rawFlow?.modules ?? [];
81915
+ const summaryMap = new Map;
81916
+ for (const mod of rawModules) {
81917
+ if (mod.id && mod.summary) {
81918
+ summaryMap.set(mod.id, mod.summary);
81919
+ }
81920
+ }
81921
+ console.log(colors.bold(`
81922
+ Steps:`));
81923
+ for (const mod of modules) {
81924
+ const icon = getModuleStatusIcon(mod.type);
81925
+ const summary = summaryMap.get(mod.id) ?? "";
81926
+ const label = summary ? `${mod.id}: ${summary}` : mod.id;
81927
+ const jobId = mod.job ? colors.dim(mod.job) : "";
81928
+ const flowJobsDuration = mod.flow_jobs_duration;
81929
+ const flowJobs = mod.flow_jobs;
81930
+ if (flowJobs && flowJobs.length > 0) {
81931
+ const totalMs = flowJobsDuration?.duration_ms ? flowJobsDuration.duration_ms.reduce((a, b) => a + b, 0) : undefined;
81932
+ const durationStr = totalMs != null ? colors.dim(formatDuration(totalMs)) : "";
81933
+ console.log(` ${icon} ${label} ${durationStr}`);
81934
+ const flowJobsSuccess = mod.flow_jobs_success ?? [];
81935
+ const durationMs = flowJobsDuration?.duration_ms ?? [];
81936
+ for (let iter = 0;iter < flowJobs.length; iter++) {
81937
+ const iterSuccess = flowJobsSuccess[iter];
81938
+ const iterIcon = iterSuccess === true ? colors.green("✓") : iterSuccess === false ? colors.red("✗") : colors.dim("·");
81939
+ const iterDur = durationMs[iter] != null ? colors.dim(formatDuration(durationMs[iter])) : "";
81940
+ const iterJobId = colors.dim(flowJobs[iter]);
81941
+ console.log(` ${iterIcon} iteration ${iter} ${iterJobId} ${iterDur}`);
81942
+ }
81943
+ } else {
81944
+ const durationStr = mod.duration_ms != null ? colors.dim(formatDuration(mod.duration_ms)) : "";
81945
+ console.log(` ${icon} ${label} ${jobId} ${durationStr}`);
81946
+ }
81947
+ }
81948
+ const hasJobs = modules.some((m) => m.job);
81949
+ if (hasJobs) {
81950
+ console.log(colors.dim(`
81951
+ Use 'wmill job logs <job-id>' for step logs`));
81952
+ }
81953
+ }
81710
81954
  async function get10(opts, id) {
81711
81955
  if (opts.json)
81712
81956
  setSilent(true);
@@ -81736,8 +81980,13 @@ async function get10(opts, id) {
81736
81980
  if (j.schedule_path) {
81737
81981
  console.log(colors.bold("Schedule:") + " " + j.schedule_path);
81738
81982
  }
81983
+ const isFlow = j.job_kind === "flow" || j.job_kind === "flowpreview";
81984
+ if (isFlow && j.flow_status) {
81985
+ formatFlowSteps(j.flow_status, j.raw_flow);
81986
+ }
81739
81987
  if (j.result !== undefined) {
81740
- console.log(colors.bold("Result:"));
81988
+ console.log(colors.bold(`
81989
+ Result:`));
81741
81990
  console.log(JSON.stringify(j.result, null, 2));
81742
81991
  }
81743
81992
  }
@@ -81762,10 +82011,55 @@ async function logs(opts, id) {
81762
82011
  workspace: workspace.workspaceId,
81763
82012
  id
81764
82013
  });
81765
- const jobKind = job.job_kind;
81766
- if (jobKind === "flow" || jobKind === "flowpreview") {
81767
- info(colors.yellow(`Flow jobs don't have direct logs. Each step runs as a separate job.
81768
- ` + "Use 'wmill job list --all' to see sub-jobs, then 'wmill job logs <sub-job-id>' for individual step logs."));
82014
+ const j = job;
82015
+ const jobKind = j.job_kind;
82016
+ if ((jobKind === "flow" || jobKind === "flowpreview") && j.flow_status?.modules) {
82017
+ const modules = j.flow_status.modules;
82018
+ const rawModules = j.raw_flow?.modules ?? [];
82019
+ const summaryMap = new Map;
82020
+ for (const mod of rawModules) {
82021
+ if (mod.id && mod.summary)
82022
+ summaryMap.set(mod.id, mod.summary);
82023
+ }
82024
+ const stripHint = (text) => text.replace(/^to remove ansi colors.*\n?/gm, "");
82025
+ let hasLogs = false;
82026
+ for (const mod of modules) {
82027
+ const summary = summaryMap.get(mod.id) ?? "";
82028
+ const label = summary ? `${mod.id}: ${summary}` : mod.id;
82029
+ const flowJobs = mod.flow_jobs;
82030
+ if (flowJobs && flowJobs.length > 0) {
82031
+ for (let iter = 0;iter < flowJobs.length; iter++) {
82032
+ try {
82033
+ const stepLogs = await getJobLogs({
82034
+ workspace: workspace.workspaceId,
82035
+ id: flowJobs[iter]
82036
+ });
82037
+ if (stepLogs) {
82038
+ console.log(colors.bold.cyan(`
82039
+ ====== ${label} (iteration ${iter}) ======`));
82040
+ console.log(stripHint(stepLogs));
82041
+ hasLogs = true;
82042
+ }
82043
+ } catch {}
82044
+ }
82045
+ } else if (mod.job) {
82046
+ try {
82047
+ const stepLogs = await getJobLogs({
82048
+ workspace: workspace.workspaceId,
82049
+ id: mod.job
82050
+ });
82051
+ if (stepLogs) {
82052
+ console.log(colors.bold.cyan(`
82053
+ ====== ${label} ======`));
82054
+ console.log(stripHint(stepLogs));
82055
+ hasLogs = true;
82056
+ }
82057
+ } catch {}
82058
+ }
82059
+ }
82060
+ if (!hasLogs) {
82061
+ info("No logs available for this flow's steps.");
82062
+ }
81769
82063
  return;
81770
82064
  }
81771
82065
  } catch {}
@@ -81776,8 +82070,9 @@ async function logs(opts, id) {
81776
82070
  if (jobLogs == null || jobLogs === "") {
81777
82071
  info("No logs available for this job.");
81778
82072
  } else {
82073
+ const stripped = jobLogs.replace(/^to remove ansi colors.*\n?/gm, "");
81779
82074
  console.error("to remove ansi colors, use: | sed 's/\\x1B\\[[0-9;]\\{1,\\}[A-Za-z]//g'");
81780
- console.log(jobLogs);
82075
+ console.log(stripped);
81781
82076
  }
81782
82077
  }
81783
82078
  async function cancel(opts, id) {
@@ -81793,8 +82088,8 @@ async function cancel(opts, id) {
81793
82088
  });
81794
82089
  info(colors.green(`Job ${id} canceled.`));
81795
82090
  }
81796
- var listOptions = (cmd) => cmd.option("--json", "Output as JSON (for piping to jq)").option("--script-path <scriptPath:string>", "Filter by exact script/flow path").option("--created-by <createdBy:string>", "Filter by creator username").option("--running", "Show only running jobs").option("--failed", "Show only failed jobs").option("--success <success:boolean>", "Filter by success status (true/false)").option("--limit <limit:number>", "Number of jobs to return (default 30, max 100)").option("--job-kinds <jobKinds:string>", "Filter by job kinds (default: script,flow,singlestepflow)").option("--label <label:string>", "Filter by job label").option("--all", "Include sub-jobs (flow steps). By default only top-level jobs are shown");
81797
- var command29 = listOptions(new Command().description("Manage jobs (list, inspect, cancel)")).action(list13).command("list", listOptions(new Command().description("List recent jobs"))).action(list13).command("get", "Get job details and result").arguments("<id:string>").option("--json", "Output as JSON (for piping to jq)").action(get10).command("result", "Get the result of a completed job (machine-friendly)").arguments("<id:string>").action(result).command("logs", "Get job logs").arguments("<id:string>").action(logs).command("cancel", "Cancel a running or queued job").arguments("<id:string>").option("--reason <reason:string>", "Reason for cancellation").action(cancel);
82091
+ var listOptions = (cmd) => cmd.option("--json", "Output as JSON (for piping to jq)").option("--script-path <scriptPath:string>", "Filter by exact script/flow path").option("--created-by <createdBy:string>", "Filter by creator username").option("--running", "Show only running jobs").option("--failed", "Show only failed jobs").option("--success <success:boolean>", "Filter by success status (true/false)").option("--limit <limit:number>", "Number of jobs to return (default 30, max 100)").option("--job-kinds <jobKinds:string>", "Filter by job kinds (default: script,flow,singlestepflow)").option("--label <label:string>", "Filter by job label").option("--all", "Include sub-jobs (flow steps). By default only top-level jobs are shown").option("--parent <parent:string>", "Filter by parent job ID (show sub-jobs of a specific flow)").option("--is-flow-step", "Show only flow step jobs");
82092
+ var command29 = listOptions(new Command().description("Manage jobs (list, inspect, cancel)")).action(list13).command("list", listOptions(new Command().description("List recent jobs"))).action(list13).command("get", "Get job details. For flows: shows step tree with sub-job IDs").arguments("<id:string>").option("--json", "Output as JSON (for piping to jq)").action(get10).command("result", "Get the result of a completed job (machine-friendly)").arguments("<id:string>").action(result).command("logs", "Get job logs. For flows: aggregates all step logs").arguments("<id:string>").action(logs).command("cancel", "Cancel a running or queued job").arguments("<id:string>").option("--reason <reason:string>", "Reason for cancellation").action(cancel);
81798
82093
  var job_default = command29;
81799
82094
 
81800
82095
  // src/commands/group/group.ts
@@ -82362,7 +82657,7 @@ var config_default = command35;
82362
82657
 
82363
82658
  // src/main.ts
82364
82659
  await init_context();
82365
- var VERSION = "1.670.0";
82660
+ var VERSION = "1.672.0";
82366
82661
  var command36 = new Command().name("wmill").action(() => info(`Welcome to Windmill CLI ${VERSION}. Use -h for help.`)).description("Windmill CLI").globalOption("--workspace <workspace:string>", "Specify the target workspace. This overrides the default workspace.").globalOption("--debug --verbose", "Show debug/verbose logs").globalOption("--show-diffs", "Show diff informations when syncing (may show sensitive informations)").globalOption("--token <token:string>", "Specify an API token. This will override any stored token.").globalOption("--base-url <baseUrl:string>", "Specify the base URL of the API. If used, --token and --workspace are required and no local remote/workspace already set will be used.").globalOption("--config-dir <configDir:string>", "Specify a custom config directory. Overrides WMILL_CONFIG_DIR environment variable and default ~/.config location.").env("HEADERS <headers:string>", `Specify headers to use for all requests. e.g: "HEADERS='h1: v1, h2: v2'"`).version(VERSION).versionOption(false).command("init", init_default).command("app", app_default).command("flow", flow_default).command("script", script_default).command("workspace", workspace_default).command("resource", resource_default).command("resource-type", resource_type_default).command("user", user_default).command("variable", variable_default).command("hub", hub_default).command("folder", folder_default).command("schedule", schedule_default).command("trigger", trigger_default).command("dev", dev_default2).command("sync", sync_default).command("lint", lint_default).command("gitsync-settings", gitsync_settings_default).command("instance", instance_default).command("worker-groups", worker_groups_default).command("workers", workers_default).command("queues", queues_default).command("dependencies", dependencies_default).command("jobs", jobs_default).command("job", job_default).command("group", group_default).command("audit", audit_default).command("token", token_default).command("generate-metadata", generate_metadata_default).command("docs", docs_default).command("config", config_default).command("version --version", "Show version information").action(async (opts) => {
82367
82662
  console.log("CLI version: " + VERSION);
82368
82663
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "windmill-cli",
3
- "version": "1.670.0",
3
+ "version": "1.672.0",
4
4
  "description": "CLI for Windmill",
5
5
  "license": "Apache 2.0",
6
6
  "type": "module",