@oomol-lab/open-flow 0.1.0-beta.74 → 0.1.0-beta.75

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 (60) hide show
  1. package/README.md +1 -0
  2. package/dist/browser/{ContentIcon-CUQHjQIw.js → ContentIcon-FCpKiwjp.js} +2 -1
  3. package/dist/browser/{FlowCanvas-Dtf8_cQp.js → FlowCanvas-dzRBnlUT.js} +1 -1
  4. package/dist/browser/{createResourceDialog-DI9pL-Yr.js → createResourceDialog-BeYDdUl-.js} +2 -2
  5. package/dist/browser/{dialog-BYJGvItc.js → dialog-ZtpsFbl4.js} +284 -13
  6. package/dist/browser/{exportSvg-BOoy7-Db.js → exportSvg-DqXw1yf-.js} +2 -2
  7. package/dist/browser/{flowChanges-RL-H4CjD.js → flowChanges-b_5UobFj.js} +285 -44
  8. package/dist/browser/{flowWorkspace-BkDsETj0.js → flowWorkspace-ItojZbO4.js} +414 -53
  9. package/dist/browser/preview.css +181 -176
  10. package/dist/browser/preview.js +955 -182
  11. package/dist/browser/workbench.css +181 -176
  12. package/dist/browser/workbench.js +12 -6
  13. package/dist/browser/{workbenchSelect-BcWPWGzw.js → workbenchSelect-M441Lz0d.js} +1 -1
  14. package/dist/command/index.js +36 -36
  15. package/dist/common/authoring.d.ts +4 -4
  16. package/dist/common/authoringExamples.d.ts +3 -2
  17. package/dist/common/authoringSchema.d.ts +67 -4
  18. package/dist/common/control-api-conformance.js +58 -7
  19. package/dist/common/control-api.js +58 -7
  20. package/dist/common/control-requests.d.ts +3 -3
  21. package/dist/common/control-requests.js +234 -62
  22. package/dist/common/engine-contract.d.ts +1 -0
  23. package/dist/common/flow-authoring.js +56 -7
  24. package/dist/common/flow-change.d.ts +1 -1
  25. package/dist/common/flow-change.js +56 -7
  26. package/dist/common/flow-encoding.js +55 -7
  27. package/dist/common/flow-graph.d.ts +2 -1
  28. package/dist/common/flow-retry.d.ts +1 -1
  29. package/dist/common/flow-semantics.js +810 -577
  30. package/dist/common/http-request.d.ts +156 -0
  31. package/dist/common/http-request.js +5254 -0
  32. package/dist/common/mcp.d.ts +3 -3
  33. package/dist/common/mcp.js +58 -7
  34. package/dist/common/run-events.js +11 -9
  35. package/dist/common/runtime-contract.js +3 -0
  36. package/dist/common/scheduler.d.ts +1 -1
  37. package/dist/common/scheduler.js +70 -21
  38. package/dist/common/webhook-response.d.ts +0 -2
  39. package/dist/common/webhook-trigger.js +12 -11
  40. package/package.json +5 -1
  41. package/skills/open-flow/references/flow-authoring.md +1 -0
  42. package/skills/open-flow/references/flow-n8n-conversion.md +19 -15
  43. package/src/control/common/api.ts +1 -0
  44. package/src/control/common/authoring.ts +14 -3
  45. package/src/control/common/authoringExamples.ts +8 -0
  46. package/src/control/common/authoringSchema.ts +30 -24
  47. package/src/execution/common/engineContract.ts +4 -0
  48. package/src/execution/common/events.ts +3 -0
  49. package/src/execution/common/node-execution.ts +4 -2
  50. package/src/execution/common/scheduler.ts +1 -0
  51. package/src/flow/common/change.ts +3 -2
  52. package/src/flow/common/changeSchema.ts +12 -3
  53. package/src/flow/common/graph.ts +10 -4
  54. package/src/flow/common/retry.ts +1 -1
  55. package/src/flow/common/semantics.ts +60 -3
  56. package/src/flow/common/webhookResponse.ts +3 -7
  57. package/src/{openapi → http}/common/authBindings.ts +3 -3
  58. package/src/http/common/headers.ts +5 -0
  59. package/src/http/common/httpRequest.ts +251 -0
  60. package/src/trigger/common/webhook.ts +4 -3
@@ -12285,14 +12285,186 @@ function decisionTask(questions = [defaultDecisionQuestion()], name = "AI Decisi
12285
12285
  };
12286
12286
  }
12287
12287
  //#endregion
12288
- //#region src/openapi/common/authBindings.ts
12288
+ //#region src/http/common/authBindings.ts
12289
12289
  /** Never allow credentials to enter a Revision as literal node inputs. */
12290
- function assertOpenApiAuthBindings(document) {
12290
+ function assertHttpAuthBindings(document) {
12291
12291
  for (const node of Object.values(document.graph.nodes)) {
12292
- if (node.kind != "task" || !("executor" in node.task) || node.task.executor.kind != "openapi") continue;
12293
- for (const [handle, mapping] of Object.entries(node.inputs)) if (handle.startsWith("auth.") && mapping.kind == "value") throw new TypeError("OpenAPI authentication accepts deployment variables and upstream outputs only.");
12292
+ if (node.kind != "task" || !("executor" in node.task) || !["openapi", "http-request"].includes(node.task.executor.kind)) continue;
12293
+ for (const [handle, mapping] of Object.entries(node.inputs)) if (handle.startsWith("auth.") && mapping.kind == "value") throw new TypeError("HTTP authentication accepts deployment variables and upstream outputs only.");
12294
12294
  }
12295
12295
  }
12296
+ //#endregion
12297
+ //#region src/flow/common/portChanges.ts
12298
+ function renamedPort(previous, next) {
12299
+ const previousNames = new Set(previous.flatMap((port) => "handle" in port ? [port.handle] : []));
12300
+ const nextNames = new Set(next.flatMap((port) => "handle" in port ? [port.handle] : []));
12301
+ const removed = [...previousNames].filter((name) => !nextNames.has(name));
12302
+ const added = [...nextNames].filter((name) => !previousNames.has(name));
12303
+ return removed.length == 1 && added.length == 1 ? [removed[0], added[0]] : void 0;
12304
+ }
12305
+ function renameOutputMapping(mapping, nodeIds, rename) {
12306
+ if (rename == null || mapping.kind != "sources") return mapping;
12307
+ const sources = mapping.sources.map((source) => source.kind == "node" && !source.system && nodeIds.has(source.nodeId) && source.output == rename[0] ? {
12308
+ ...source,
12309
+ output: rename[1]
12310
+ } : source);
12311
+ return sources.every((source, index) => source === mapping.sources[index]) ? mapping : {
12312
+ kind: "sources",
12313
+ sources
12314
+ };
12315
+ }
12316
+ /** Carry saved mappings through a field rename and remove only fields absent from the new contract. */
12317
+ function remapInputMappings(mappings, ports, rename) {
12318
+ const inputs = { ...mappings };
12319
+ if (rename != null && Object.hasOwn(inputs, rename[0])) {
12320
+ Object.defineProperty(inputs, rename[1], {
12321
+ value: inputs[rename[0]],
12322
+ enumerable: true,
12323
+ configurable: true,
12324
+ writable: true
12325
+ });
12326
+ delete inputs[rename[0]];
12327
+ }
12328
+ const names = new Set(ports.flatMap((port) => "handle" in port ? [port.handle] : []));
12329
+ for (const name of Object.keys(inputs)) if (!names.has(name)) delete inputs[name];
12330
+ return inputs;
12331
+ }
12332
+ //#endregion
12333
+ //#region src/http/common/httpRequest.ts
12334
+ var parameterSchema = strictObject({
12335
+ mode: _enum(["fields", "object"]).default("fields"),
12336
+ names: array(string()).default([])
12337
+ });
12338
+ var httpRequestConfigSchema = strictObject({
12339
+ method: _enum([
12340
+ "GET",
12341
+ "POST",
12342
+ "PUT",
12343
+ "PATCH",
12344
+ "DELETE",
12345
+ "HEAD",
12346
+ "OPTIONS"
12347
+ ]).default("GET"),
12348
+ query: parameterSchema.default({
12349
+ mode: "fields",
12350
+ names: []
12351
+ }),
12352
+ headers: parameterSchema.default({
12353
+ mode: "fields",
12354
+ names: []
12355
+ }),
12356
+ bodyFormat: _enum([
12357
+ "none",
12358
+ "json",
12359
+ "text",
12360
+ "form"
12361
+ ]).default("none"),
12362
+ authentication: union([
12363
+ strictObject({ type: literal("none") }),
12364
+ strictObject({ type: _enum(["bearer", "basic"]) }),
12365
+ strictObject({
12366
+ type: literal("apiKey"),
12367
+ name: string(),
12368
+ in: _enum(["header", "query"])
12369
+ })
12370
+ ]).default({ type: "none" }),
12371
+ responseFormat: _enum([
12372
+ "auto",
12373
+ "json",
12374
+ "text"
12375
+ ]).default("auto"),
12376
+ statusPolicy: _enum(["success", "all"]).default("success")
12377
+ });
12378
+ var httpRequestExecutorSchema = httpRequestConfigSchema.extend({ kind: literal("http-request") });
12379
+ var scalarSchema = { type: [
12380
+ "string",
12381
+ "number",
12382
+ "boolean"
12383
+ ] };
12384
+ var queryValueSchema = { anyOf: [scalarSchema, {
12385
+ type: "array",
12386
+ items: scalarSchema
12387
+ }] };
12388
+ var httpHeaderSchema = {
12389
+ type: "object",
12390
+ additionalProperties: { type: "string" }
12391
+ };
12392
+ var httpQuerySchema = {
12393
+ type: "object",
12394
+ additionalProperties: queryValueSchema
12395
+ };
12396
+ function httpAuthHandles(config) {
12397
+ return config.authentication.type == "none" ? [] : config.authentication.type == "basic" ? ["auth.username", "auth.password"] : ["auth.token"];
12398
+ }
12399
+ function retain(ports, old) {
12400
+ const remaining = new Map(ports.map((port) => [port.handle, port]));
12401
+ return [...old.flatMap((item) => {
12402
+ if ("group" in item) return [item];
12403
+ const next = remaining.get(item.handle);
12404
+ if (next == null) return [];
12405
+ remaining.delete(item.handle);
12406
+ return [dequal(item.jsonSchema, next.jsonSchema) && item.nullable == next.nullable ? item : next];
12407
+ }), ...remaining.values()];
12408
+ }
12409
+ function httpRequestTask(config = httpRequestConfigSchema.parse({}), name = "HTTP Request", previous) {
12410
+ const inputs = [{
12411
+ handle: "url",
12412
+ jsonSchema: { type: "string" },
12413
+ nullable: false
12414
+ }];
12415
+ for (const group of ["query", "headers"]) if (config[group].mode == "object") inputs.push({
12416
+ handle: group,
12417
+ jsonSchema: group == "query" ? httpQuerySchema : httpHeaderSchema,
12418
+ nullable: true
12419
+ });
12420
+ else for (const parameterName of config[group].names.filter((value) => value.trim())) inputs.push({
12421
+ handle: `${group == "query" ? "query" : "header"}.${parameterName}`,
12422
+ jsonSchema: group == "query" ? queryValueSchema : { type: "string" },
12423
+ nullable: true
12424
+ });
12425
+ if (config.bodyFormat != "none") inputs.push({
12426
+ handle: "body",
12427
+ jsonSchema: config.bodyFormat == "json" ? {} : config.bodyFormat == "text" ? { type: "string" } : httpQuerySchema,
12428
+ nullable: config.bodyFormat == "json"
12429
+ });
12430
+ inputs.push(...httpAuthHandles(config).map((handle) => ({
12431
+ handle,
12432
+ jsonSchema: { type: "string" },
12433
+ nullable: false
12434
+ })));
12435
+ const outputs = [
12436
+ {
12437
+ handle: "body",
12438
+ jsonSchema: config.responseFormat == "text" ? { type: "string" } : {},
12439
+ nullable: true
12440
+ },
12441
+ {
12442
+ handle: "statusCode",
12443
+ jsonSchema: { type: "integer" },
12444
+ nullable: false
12445
+ },
12446
+ {
12447
+ handle: "headers",
12448
+ jsonSchema: httpHeaderSchema,
12449
+ nullable: false
12450
+ }
12451
+ ];
12452
+ return {
12453
+ name,
12454
+ executor: {
12455
+ ...config,
12456
+ kind: "http-request"
12457
+ },
12458
+ inputs: retain(inputs, previous?.inputs ?? []),
12459
+ outputs: retain(outputs, previous?.outputs ?? [])
12460
+ };
12461
+ }
12462
+ /** Only a named parameter rename carries a value; changing source modes is not a rename. */
12463
+ function httpRequestInputs(mappings, previous, next) {
12464
+ const candidate = renamedPort(previous.inputs, next.inputs);
12465
+ const rename = candidate != null && ["query.", "header."].some((prefix) => candidate.every((name) => name.startsWith(prefix))) ? candidate : void 0;
12466
+ return remapInputMappings(mappings, next.inputs, rename);
12467
+ }
12296
12468
  function checkJsonDepth(value, depth = 0) {
12297
12469
  if (depth > 64) throw new TypeError("Value exceeds the maximum JSON depth.");
12298
12470
  if (value != null && typeof value == "object") for (const child of Object.values(value)) checkJsonDepth(child, depth + 1);
@@ -12303,6 +12475,7 @@ var retryNodeTypes = [
12303
12475
  "code",
12304
12476
  "connector",
12305
12477
  "openapi",
12478
+ "http-request",
12306
12479
  "llm",
12307
12480
  "decision"
12308
12481
  ];
@@ -12602,7 +12775,8 @@ var managedFields = object$1({
12602
12775
  kind: literal("workflow"),
12603
12776
  flowId: text$1,
12604
12777
  revisionId: text$1
12605
- })
12778
+ }),
12779
+ httpRequestExecutorSchema
12606
12780
  ])
12607
12781
  });
12608
12782
  var managed = managedFields.transform(limitDecisionTask);
@@ -12902,7 +13076,7 @@ function decodeRevisionContent(value) {
12902
13076
  checkJsonDepth(value);
12903
13077
  const content = revision.parse(value);
12904
13078
  checkModelVersion(content);
12905
- assertOpenApiAuthBindings(content.document);
13079
+ assertHttpAuthBindings(content.document);
12906
13080
  return content;
12907
13081
  }
12908
13082
  var shapes = {
@@ -13096,6 +13270,7 @@ function changeOperationsSchema(kind) {
13096
13270
  return toJSONSchema(schema, { io: "input" });
13097
13271
  }
13098
13272
  function checkModelVersion(content) {
13273
+ if (content.modelVersion < 11 && Object.values(content.document.graph.nodes).some((candidateNode) => candidateNode.kind == "task" && "executor" in candidateNode.task && candidateNode.task.executor.kind == "http-request")) throw new TypeError("HTTP Request requires Flow model version 11.");
13099
13274
  if (content.modelVersion < 11 && Object.values(content.document.graph.nodes).some((candidateNode) => candidateNode.kind == "task" && ("moduleId" in candidateNode.task ? candidateNode.task.capabilities?.some((item) => item.kind == "workflow") : candidateNode.task.executor.kind == "agent" && candidateNode.task.executor.tools.some((item) => item.kind == "workflow")))) throw new TypeError("Workflow tools require Flow model version 11.");
13100
13275
  if (content.modelVersion < 10 && Object.values(content.document.graph.nodes).some((candidateNode) => candidateNode.kind == "webhook" && (candidateNode.options?.responseBody != null || candidateNode.options?.responseStatusCodeOutput != null || candidateNode.options?.responseHeadersSource != null || candidateNode.options?.responseHeaderOutputs != null))) throw new TypeError("Dynamic Webhook responses require Flow model version 10.");
13101
13276
  if (content.modelVersion < 9 && Object.values(content.document.graph.nodes).some((candidateNode) => "retry" in candidateNode && candidateNode.retry != null)) throw new TypeError("Node retries require Flow model version 9.");
@@ -13696,6 +13871,7 @@ var authoringTypes = [
13696
13871
  "llm",
13697
13872
  "decision",
13698
13873
  "openapi",
13874
+ "http-request",
13699
13875
  "condition",
13700
13876
  "value",
13701
13877
  "wait",
@@ -13822,6 +13998,7 @@ var authoringConfigurations = {
13822
13998
  }),
13823
13999
  "connector": authoringDefinitionSchemas.connector.omit({ outputMode: true }).strict(),
13824
14000
  "openapi": authoringOpenApiSchema,
14001
+ "http-request": httpRequestConfigSchema,
13825
14002
  "agent": authoringDefinitionSchemas.agent.omit({
13826
14003
  prompt: true,
13827
14004
  tools: true
@@ -14070,21 +14247,21 @@ function authoringSchema(kind) {
14070
14247
  "wait",
14071
14248
  "approval"
14072
14249
  ].includes(kind),
14073
- guidance: kind == "webhook-response" ? "Prepare an HTTP response using fixed inputs body (any JSON, default empty text), statusCode (number, default 200) and headers (scalar-valued object, default {}). Select this node in Webhook options.responseBody as {kind: \"webhook-response\", nodeId: \"node reference\", timeoutMs: 30000}. Its first completion responds immediately while the Run continues. It has no business outputs. Multiple response nodes are allowed; each Webhook selects one reachable node." : kind == "outputs" ? "Declare output fields through config.outputDefs. Set their values through node.add.inputs or input.set. Outputs is a singleton execution sink; it waits for its predecessors and produces workflow business outputs independently of node logs. No downstream edges or data references to Outputs are allowed." : kind == "webhook" ? "Use options.responseBody: {kind: \"webhook-response\", nodeId: \"node ID or $alias\", timeoutMs: 30000} to respond on the selected node’s first completion while the Run continues. Body, statusCode and headers come exclusively from that node; clear workflow-output-only status/header references when switching. Alternatively configure options.responseBody as {kind: \"workflow-outputs\", output?: \"top-level output name\", timeoutMs?: 30000} to respond after the Run succeeds. Omit output for the complete object. Connect Webhook to Outputs. Timeout is 1000–60000 ms; HTTP timeout does not cancel the Run. Strings return text; other values return JSON. Only in this mode, options.responseStatusCodeOutput can select a numeric top-level workflow output as the HTTP status, overriding the preserved fixed responseStatusCode (default 200). Its runtime value must be an integer from 200 to 599, excluding 204, 205, 304; missing or invalid values return 500. Use responseHeaders for fixed text headers. In workflow-output mode, responseHeaderOutputs maps header names to top-level output names; values can be string, number or boolean and are converted to text. Alternatively responseHeadersSource: {kind: \"workflow-outputs\", output?: \"object field\"} replaces the entire header object (omit output for all outputs), preserving but ignoring per-header settings. Missing, null, complex or invalid header values return 500 without fallback. Header restrictions and Content-Type policy still apply. Omit responseBody for immediate responseData. Clear options.responseBody, options.responseStatusCodeOutput, options.responseHeadersSource and options.responseHeaderOutputs explicitly to switch back." : kind == "start" ? "Declare workflow inputs through config.inputDefs; read them in Start node details. REST calls and manual runs supply them as trigger.outputs. Downstream nodes bind these parameters as Start outputs. Start has no input sources or defaults; non-nullable parameters are required, and omitted nullable parameters become null." : kind == "condition" ? "Edit branch operands through config.branches. A branch can test one expression, all expressions, or any alternatives. Unmatched execution uses branch otherwise." : ["poll", "integration"].includes(kind) ? "Configure static parameters with node.add.inputs or input.set. Definitions come from the selected Trigger key; execution output and variable sources are unavailable." : kind == "llm" ? "Configure model settings, message templates and history as defaults. Input bindings with the same names override these defaults; named business inputs can be bound directly. Template strings interpolate {{name}}." : kind == "openapi" ? "Select an operation; the tool resolves its ports and authentication. Read authenticationChoices from node details. authentication is replaced as a complete choice. Bind advertised credentials through inputs." : kind == "workflow" ? "Select another workflow with config.flowId. A workflow cannot call itself, including in a disconnected node. Owned unpublished or unavailable targets may be saved as incomplete drafts. Use node.refresh to load the current published interface." : kind == "agent" ? "Tools accept Connector Actions or {kind: \"workflow\", flowId, name, description, approval, inputs}. Workflow parameter sources use model, input or value. The platform derives the interface. Refresh a workflow tool with node.refresh and tool: its name." : kind == "code" ? "Select workflows through config.capabilities: [{kind: \"workflow\", flowId, name}]. Call context.workflows.call(name, input) and await its business output. The platform derives input/output types. Refresh a selected workflow tool with node.refresh and tool: its name. Calls obey node and Run time limits." : "Supply business configuration and input data. Fixed definitions are assembled by the tool. Existing constraints and defaults are preserved on partial updates.",
14250
+ guidance: kind == "webhook-response" ? "Prepare an HTTP response using fixed inputs body (any JSON, default empty text), statusCode (number, default 200) and headers (scalar-valued object, default {}). Select this node in Webhook options.responseBody as {kind: \"webhook-response\", nodeId: \"node reference\", timeoutMs: 30000}. Its first completion responds immediately while the Run continues. It has no business outputs. Multiple response nodes are allowed; each Webhook selects one reachable node." : kind == "outputs" ? "Declare output fields through config.outputDefs. Set their values through node.add.inputs or input.set. Outputs is a singleton execution sink; it waits for its predecessors and produces workflow business outputs independently of node logs. No downstream edges or data references to Outputs are allowed." : kind == "webhook" ? "Use options.responseBody: {kind: \"webhook-response\", nodeId: \"node ID or $alias\", timeoutMs: 30000} to respond on the selected node’s first completion while the Run continues. Body, statusCode and headers come exclusively from that node; clear workflow-output-only status/header references when switching. Alternatively configure options.responseBody as {kind: \"workflow-outputs\", output?: \"top-level output name\", timeoutMs?: 30000} to respond after the Run succeeds. Omit output for the complete object. Connect Webhook to Outputs. Timeout is 1000–60000 ms; HTTP timeout does not cancel the Run. Strings return text; other values return JSON. Only in this mode, options.responseStatusCodeOutput can select a numeric top-level workflow output as the HTTP status, overriding the preserved fixed responseStatusCode (default 200). Its runtime value must be an integer from 200 to 599, excluding 204, 205, 304; missing or invalid values return 500. Use responseHeaders for fixed text headers. In workflow-output mode, responseHeaderOutputs maps header names to top-level output names; values can be string, number or boolean and are converted to text. Alternatively responseHeadersSource: {kind: \"workflow-outputs\", output?: \"object field\"} replaces the entire header object (omit output for all outputs), preserving but ignoring per-header settings. Missing, null, complex or invalid header values return 500 without fallback. Header restrictions and Content-Type policy still apply. Omit responseBody for immediate responseData. Clear options.responseBody, options.responseStatusCodeOutput, options.responseHeadersSource and options.responseHeaderOutputs explicitly to switch back." : kind == "start" ? "Declare workflow inputs through config.inputDefs; read them in Start node details. REST calls and manual runs supply them as trigger.outputs. Downstream nodes bind these parameters as Start outputs. Start has no input sources or defaults; non-nullable parameters are required, and omitted nullable parameters become null." : kind == "condition" ? "Edit branch operands through config.branches. A branch can test one expression, all expressions, or any alternatives. Unmatched execution uses branch otherwise." : ["poll", "integration"].includes(kind) ? "Configure static parameters with node.add.inputs or input.set. Definitions come from the selected Trigger key; execution output and variable sources are unavailable." : kind == "llm" ? "Configure model settings, message templates and history as defaults. Input bindings with the same names override these defaults; named business inputs can be bound directly. Template strings interpolate {{name}}." : kind == "http-request" ? "Configure method, query/headers modes and names, bodyFormat, authentication, responseFormat and statusPolicy. Bind url, query.<name>/header.<name> or whole query/headers objects, body and auth.token (Basic: auth.username/auth.password). Credentials accept variables or upstream outputs only. Outputs: body, statusCode, headers. Authentication is replaced as a complete choice." : kind == "openapi" ? "Select an operation; the tool resolves its ports and authentication. Read authenticationChoices from node details. authentication is replaced as a complete choice. Bind advertised credentials through inputs." : kind == "workflow" ? "Select another workflow with config.flowId. A workflow cannot call itself, including in a disconnected node. Owned unpublished or unavailable targets may be saved as incomplete drafts. Use node.refresh to load the current published interface." : kind == "agent" ? "Tools accept Connector Actions or {kind: \"workflow\", flowId, name, description, approval, inputs}. Workflow parameter sources use model, input or value. The platform derives the interface. Refresh a workflow tool with node.refresh and tool: its name." : kind == "code" ? "Select workflows through config.capabilities: [{kind: \"workflow\", flowId, name}]. Call context.workflows.call(name, input) and await its business output. The platform derives input/output types. Refresh a selected workflow tool with node.refresh and tool: its name. Calls obey node and Run time limits." : "Supply business configuration and input data. Fixed definitions are assembled by the tool. Existing constraints and defaults are preserved on partial updates.",
14074
14251
  example: {
14075
14252
  op: "node.add",
14076
14253
  as: "step",
14077
14254
  type: kind,
14078
14255
  name: "Step",
14079
14256
  ...{
14080
- start: { config: { inputDefs: {} } },
14081
- outputs: { config: { outputDefs: {} } },
14082
- workflow: { config: { flowId: "<Flow ID>" } },
14083
- connector: { config: {
14257
+ "start": { config: { inputDefs: {} } },
14258
+ "outputs": { config: { outputDefs: {} } },
14259
+ "workflow": { config: { flowId: "<Flow ID>" } },
14260
+ "connector": { config: {
14084
14261
  action: "<Action ID>",
14085
14262
  connectionId: "<Connection ID>"
14086
14263
  } },
14087
- poll: { config: {
14264
+ "poll": { config: {
14088
14265
  key: "<Trigger key>",
14089
14266
  schedule: [{
14090
14267
  type: "every",
@@ -14092,20 +14269,27 @@ function authoringSchema(kind) {
14092
14269
  value: 5
14093
14270
  }]
14094
14271
  } },
14095
- integration: { config: { key: "<Trigger key>" } },
14096
- openapi: { config: {
14272
+ "integration": { config: { key: "<Trigger key>" } },
14273
+ "http-request": {
14274
+ config: {},
14275
+ inputs: { url: {
14276
+ kind: "value",
14277
+ value: "https://example.com"
14278
+ } }
14279
+ },
14280
+ "openapi": { config: {
14097
14281
  sourceUrl: "https://example.com/openapi.json",
14098
14282
  path: "/items",
14099
14283
  method: "get"
14100
14284
  } },
14101
- code: {
14285
+ "code": {
14102
14286
  code: "export default ({value}) => ({result:value})",
14103
14287
  inputs: { value: {
14104
14288
  kind: "value",
14105
14289
  value: "Hello"
14106
14290
  } }
14107
14291
  },
14108
- condition: { config: {
14292
+ "condition": { config: {
14109
14293
  match: "first",
14110
14294
  branches: [{
14111
14295
  name: "yes",
@@ -14122,32 +14306,32 @@ function authoringSchema(kind) {
14122
14306
  }
14123
14307
  }]
14124
14308
  } },
14125
- value: { config: { values: {
14309
+ "value": { config: { values: {
14126
14310
  threshold: 100,
14127
14311
  region: "EU"
14128
14312
  } } },
14129
- webhook: { config: {
14313
+ "webhook": { config: {
14130
14314
  method: "POST",
14131
14315
  body: { orderId: {
14132
14316
  schema: { type: "string" },
14133
14317
  nullable: false
14134
14318
  } }
14135
14319
  } },
14136
- wait: {
14320
+ "wait": {
14137
14321
  prompt: "Continue processing this order?",
14138
14322
  inputs: { order: {
14139
14323
  kind: "value",
14140
14324
  value: { orderId: "A-1" }
14141
14325
  } }
14142
14326
  },
14143
- approval: {
14327
+ "approval": {
14144
14328
  prompt: "Approve this order?",
14145
14329
  inputs: { order: {
14146
14330
  kind: "value",
14147
14331
  value: { orderId: "A-1" }
14148
14332
  } }
14149
14333
  },
14150
- llm: {
14334
+ "llm": {
14151
14335
  config: {
14152
14336
  model: { model: "deepseek-v4-flash" },
14153
14337
  template: [{
@@ -14163,7 +14347,7 @@ function authoringSchema(kind) {
14163
14347
  }
14164
14348
  } }
14165
14349
  },
14166
- agent: {
14350
+ "agent": {
14167
14351
  config: { model: "deepseek-v4-flash" },
14168
14352
  inputs: { orders: {
14169
14353
  kind: "value",
@@ -14999,7 +15183,7 @@ function applyFlowChanges(content, operations) {
14999
15183
  break;
15000
15184
  }
15001
15185
  }
15002
- assertOpenApiAuthBindings(document);
15186
+ assertHttpAuthBindings(document);
15003
15187
  return upgradeDecodedRevisionContent({
15004
15188
  document,
15005
15189
  modelVersion: 11,
@@ -15266,42 +15450,6 @@ async function imports(source) {
15266
15450
  }
15267
15451
  }
15268
15452
  //#endregion
15269
- //#region src/flow/common/portChanges.ts
15270
- function renamedPort(previous, next) {
15271
- const previousNames = new Set(previous.flatMap((port) => "handle" in port ? [port.handle] : []));
15272
- const nextNames = new Set(next.flatMap((port) => "handle" in port ? [port.handle] : []));
15273
- const removed = [...previousNames].filter((name) => !nextNames.has(name));
15274
- const added = [...nextNames].filter((name) => !previousNames.has(name));
15275
- return removed.length == 1 && added.length == 1 ? [removed[0], added[0]] : void 0;
15276
- }
15277
- function renameOutputMapping(mapping, nodeIds, rename) {
15278
- if (rename == null || mapping.kind != "sources") return mapping;
15279
- const sources = mapping.sources.map((source) => source.kind == "node" && !source.system && nodeIds.has(source.nodeId) && source.output == rename[0] ? {
15280
- ...source,
15281
- output: rename[1]
15282
- } : source);
15283
- return sources.every((source, index) => source === mapping.sources[index]) ? mapping : {
15284
- kind: "sources",
15285
- sources
15286
- };
15287
- }
15288
- /** Carry saved mappings through a field rename and remove only fields absent from the new contract. */
15289
- function remapInputMappings(mappings, ports, rename) {
15290
- const inputs = { ...mappings };
15291
- if (rename != null && Object.hasOwn(inputs, rename[0])) {
15292
- Object.defineProperty(inputs, rename[1], {
15293
- value: inputs[rename[0]],
15294
- enumerable: true,
15295
- configurable: true,
15296
- writable: true
15297
- });
15298
- delete inputs[rename[0]];
15299
- }
15300
- const names = new Set(ports.flatMap((port) => "handle" in port ? [port.handle] : []));
15301
- for (const name of Object.keys(inputs)) if (!names.has(name)) delete inputs[name];
15302
- return inputs;
15303
- }
15304
- //#endregion
15305
15453
  //#region src/flow/common/nodeChanges.ts
15306
15454
  var codeTaskTemplate = `export default async function (inputs, context) {
15307
15455
  return { result: inputs.value }
@@ -16263,7 +16411,10 @@ function authoringNode(content, ref) {
16263
16411
  inputs: fieldsView([...task.inputs, ...node.kind == "task" ? node.additionalInputs ?? [] : []].filter((port) => !("handle" in port) || !llmReservedInputs.includes(port.handle)))
16264
16412
  };
16265
16413
  else if (task.executor.kind == "workflow") config = { flowId: task.executor.flowId };
16266
- else if (task.executor.kind == "openapi") config = { ...openApiConfigView(task.executor) };
16414
+ else if (task.executor.kind == "http-request") {
16415
+ const { kind: _kind, ...settings } = task.executor;
16416
+ config = settings;
16417
+ } else if (task.executor.kind == "openapi") config = { ...openApiConfigView(task.executor) };
16267
16418
  } else if (node.kind == "task" && "moduleId" in node.task) {
16268
16419
  config = {
16269
16420
  inputs: fieldsView([...node.task.inputs, ...node.additionalInputs ?? []]),
@@ -16564,7 +16715,7 @@ async function compileAuthoring(base, request, host) {
16564
16715
  const timing = record(record(patch.config).autoContinue);
16565
16716
  if (Object.hasOwn(timing, "kind") && timing.kind != record(record(view.config).autoContinue).kind) record(update.config).autoContinue = timing;
16566
16717
  }
16567
- if (type == "openapi" && Object.hasOwn(record(patch.config), "authentication")) record(update.config).authentication = record(patch.config).authentication;
16718
+ if ((type == "openapi" || type == "http-request") && Object.hasOwn(record(patch.config), "authentication")) record(update.config).authentication = record(patch.config).authentication;
16568
16719
  for (const path of clear) clearPath(update, path);
16569
16720
  if (typeof update.name != "string") fail("node.name-required", "Node name cannot be cleared.");
16570
16721
  const config = authoringConfigurations[type].parse(update.config);
@@ -16650,7 +16801,13 @@ async function compileAuthoring(base, request, host) {
16650
16801
  };
16651
16802
  } else task = await connectorTask(config, String(update.name));
16652
16803
  } else if (type == "decision") task = decisionTask(config.questions, String(update.name));
16653
- else if (type == "openapi") task = await prepareOpenApiTask(config, before, (url) => host.openapi(url), String(update.name), {
16804
+ else if (type == "http-request") {
16805
+ task = httpRequestTask(httpRequestConfigSchema.parse(config), String(update.name), before);
16806
+ next = {
16807
+ ...next,
16808
+ inputs: httpRequestInputs(node.inputs, before, task)
16809
+ };
16810
+ } else if (type == "openapi") task = await prepareOpenApiTask(config, before, (url) => host.openapi(url), String(update.name), {
16654
16811
  authentication: explicit("authentication"),
16655
16812
  serverUrl: explicit("serverUrl")
16656
16813
  });
@@ -16991,6 +17148,9 @@ async function compileAuthoring(base, request, host) {
16991
17148
  case "connector":
16992
17149
  changes = createManagedTask(identity, await connectorTask(config, name));
16993
17150
  break;
17151
+ case "http-request":
17152
+ changes = createManagedTask(identity, httpRequestTask(httpRequestConfigSchema.parse(config), name));
17153
+ break;
16994
17154
  case "openapi":
16995
17155
  changes = createManagedTask(identity, await prepareOpenApiTask(authoringConfigurations.openapi.parse(config), void 0, (url) => host.openapi(url), name));
16996
17156
  break;
@@ -17209,6 +17369,7 @@ var descriptions = {
17209
17369
  "poll": "Gmail polling by key; replace CONNECTION_ID with an active Gmail connection.",
17210
17370
  "integration": "Telegram webhook integration by key; replace CONNECTION_ID with an active Telegram connection.",
17211
17371
  "decision": "AI Decision evaluates named questions against target and returns a complete answer object per output. Configure target before running.",
17372
+ "http-request": "A single HTTP request. Bind url and request inputs; outputs are body, statusCode and headers.",
17212
17373
  "openapi": "A fixed JSON API operation. Replace the example document and URL before running.",
17213
17374
  "connector": "A Connector Task and its node. Replace ACTION_ID, CONNECTION_ID and port definitions using connector_get / connector show. Set connectionId to an empty string to defer account configuration; execution will require an account.",
17214
17375
  "code": "A JavaScript module and its node. Use Code for custom computation; use Connector Tasks for existing actions.",
@@ -17252,6 +17413,17 @@ function authoringExample(name) {
17252
17413
  instructions: "Does the customer need support?"
17253
17414
  }]);
17254
17415
  break;
17416
+ case "http-request":
17417
+ operations = [...createManagedTask({ nodeId: "http" }, httpRequestTask()), {
17418
+ kind: "graph.node.input.set",
17419
+ nodeId: "http",
17420
+ handle: "url",
17421
+ value: {
17422
+ kind: "value",
17423
+ value: "https://example.com"
17424
+ }
17425
+ }];
17426
+ break;
17255
17427
  case "openapi":
17256
17428
  operations = createManagedTask({ nodeId: "api" }, openApiTask(selectOperation({
17257
17429
  openapi: "3.1.0",
@@ -6,6 +6,7 @@ export interface EngineContract {
6
6
  readonly waitAutoContinue?: boolean;
7
7
  readonly webhookResponses?: boolean;
8
8
  readonly workflowTools?: boolean;
9
+ readonly httpRequests?: boolean;
9
10
  }
10
11
  export declare const currentEngineContract = "open-flow-engine/v8";
11
12
  export declare const nodejsEngineContract = "open-flow-engine/v8/nodejs-compat-v1";
@@ -3009,12 +3009,12 @@ function decisionTask(questions = [defaultDecisionQuestion()], name = "AI Decisi
3009
3009
  };
3010
3010
  }
3011
3011
  //#endregion
3012
- //#region src/openapi/common/authBindings.ts
3012
+ //#region src/http/common/authBindings.ts
3013
3013
  /** Never allow credentials to enter a Revision as literal node inputs. */
3014
- function assertOpenApiAuthBindings(document) {
3014
+ function assertHttpAuthBindings(document) {
3015
3015
  for (const node of Object.values(document.graph.nodes)) {
3016
- if (node.kind != "task" || !("executor" in node.task) || node.task.executor.kind != "openapi") continue;
3017
- for (const [handle, mapping] of Object.entries(node.inputs)) if (handle.startsWith("auth.") && mapping.kind == "value") throw new TypeError("OpenAPI authentication accepts deployment variables and upstream outputs only.");
3016
+ if (node.kind != "task" || !("executor" in node.task) || !["openapi", "http-request"].includes(node.task.executor.kind)) continue;
3017
+ for (const [handle, mapping] of Object.entries(node.inputs)) if (handle.startsWith("auth.") && mapping.kind == "value") throw new TypeError("HTTP authentication accepts deployment variables and upstream outputs only.");
3018
3018
  }
3019
3019
  }
3020
3020
  //#endregion
@@ -8706,6 +8706,52 @@ function preprocess(fn, schema) {
8706
8706
  function datetime(params) {
8707
8707
  return /* @__PURE__ */ _isoDateTime(ZodISODateTime, params);
8708
8708
  }
8709
+ //#endregion
8710
+ //#region src/http/common/httpRequest.ts
8711
+ var parameterSchema = strictObject({
8712
+ mode: _enum(["fields", "object"]).default("fields"),
8713
+ names: array(string()).default([])
8714
+ });
8715
+ var httpRequestExecutorSchema = strictObject({
8716
+ method: _enum([
8717
+ "GET",
8718
+ "POST",
8719
+ "PUT",
8720
+ "PATCH",
8721
+ "DELETE",
8722
+ "HEAD",
8723
+ "OPTIONS"
8724
+ ]).default("GET"),
8725
+ query: parameterSchema.default({
8726
+ mode: "fields",
8727
+ names: []
8728
+ }),
8729
+ headers: parameterSchema.default({
8730
+ mode: "fields",
8731
+ names: []
8732
+ }),
8733
+ bodyFormat: _enum([
8734
+ "none",
8735
+ "json",
8736
+ "text",
8737
+ "form"
8738
+ ]).default("none"),
8739
+ authentication: union([
8740
+ strictObject({ type: literal("none") }),
8741
+ strictObject({ type: _enum(["bearer", "basic"]) }),
8742
+ strictObject({
8743
+ type: literal("apiKey"),
8744
+ name: string(),
8745
+ in: _enum(["header", "query"])
8746
+ })
8747
+ ]).default({ type: "none" }),
8748
+ responseFormat: _enum([
8749
+ "auto",
8750
+ "json",
8751
+ "text"
8752
+ ]).default("auto"),
8753
+ statusPolicy: _enum(["success", "all"]).default("success")
8754
+ }).extend({ kind: literal("http-request") });
8709
8755
  function checkJsonDepth(value, depth = 0) {
8710
8756
  if (depth > 64) throw new TypeError("Value exceeds the maximum JSON depth.");
8711
8757
  if (value != null && typeof value == "object") for (const child of Object.values(value)) checkJsonDepth(child, depth + 1);
@@ -8716,6 +8762,7 @@ var retryNodeTypes = [
8716
8762
  "code",
8717
8763
  "connector",
8718
8764
  "openapi",
8765
+ "http-request",
8719
8766
  "llm",
8720
8767
  "decision"
8721
8768
  ];
@@ -8992,7 +9039,8 @@ var managedFields = object({
8992
9039
  kind: literal("workflow"),
8993
9040
  flowId: text,
8994
9041
  revisionId: text
8995
- })
9042
+ }),
9043
+ httpRequestExecutorSchema
8996
9044
  ])
8997
9045
  });
8998
9046
  var managed = managedFields.transform(limitDecisionTask);
@@ -9292,7 +9340,7 @@ function decodeRevisionContent(value) {
9292
9340
  checkJsonDepth(value);
9293
9341
  const content = revision.parse(value);
9294
9342
  checkModelVersion(content);
9295
- assertOpenApiAuthBindings(content.document);
9343
+ assertHttpAuthBindings(content.document);
9296
9344
  return content;
9297
9345
  }
9298
9346
  var shapes = {
@@ -9462,6 +9510,7 @@ array(union([...new Map(Object.entries(shapes).map(([kind, shape]) => [kind, sha
9462
9510
  ...shape
9463
9511
  })])).values()])).min(1);
9464
9512
  function checkModelVersion(content) {
9513
+ if (content.modelVersion < 11 && Object.values(content.document.graph.nodes).some((candidateNode) => candidateNode.kind == "task" && "executor" in candidateNode.task && candidateNode.task.executor.kind == "http-request")) throw new TypeError("HTTP Request requires Flow model version 11.");
9465
9514
  if (content.modelVersion < 11 && Object.values(content.document.graph.nodes).some((candidateNode) => candidateNode.kind == "task" && ("moduleId" in candidateNode.task ? candidateNode.task.capabilities?.some((item) => item.kind == "workflow") : candidateNode.task.executor.kind == "agent" && candidateNode.task.executor.tools.some((item) => item.kind == "workflow")))) throw new TypeError("Workflow tools require Flow model version 11.");
9466
9515
  if (content.modelVersion < 10 && Object.values(content.document.graph.nodes).some((candidateNode) => candidateNode.kind == "webhook" && (candidateNode.options?.responseBody != null || candidateNode.options?.responseStatusCodeOutput != null || candidateNode.options?.responseHeadersSource != null || candidateNode.options?.responseHeaderOutputs != null))) throw new TypeError("Dynamic Webhook responses require Flow model version 10.");
9467
9516
  if (content.modelVersion < 9 && Object.values(content.document.graph.nodes).some((candidateNode) => "retry" in candidateNode && candidateNode.retry != null)) throw new TypeError("Node retries require Flow model version 9.");
@@ -10167,7 +10216,7 @@ function applyFlowChanges(content, operations) {
10167
10216
  break;
10168
10217
  }
10169
10218
  }
10170
- assertOpenApiAuthBindings(document);
10219
+ assertHttpAuthBindings(document);
10171
10220
  return upgradeDecodedRevisionContent({
10172
10221
  document,
10173
10222
  modelVersion: 11,
@@ -113,7 +113,7 @@ export interface ConditionNode extends GraphNodeBase {
113
113
  readonly matchMode: 'first' | 'all';
114
114
  readonly kind: 'condition';
115
115
  }
116
- export type ManagedTaskExecutor = import('./workflow.js').WorkflowExecutor | import('./decision.js').DecisionExecutor | import('./openapi.js').OpenApiExecutor | {
116
+ export type ManagedTaskExecutor = import('./http-request.js').HttpRequestExecutor | import('./workflow.js').WorkflowExecutor | import('./decision.js').DecisionExecutor | import('./openapi.js').OpenApiExecutor | {
117
117
  readonly kind: 'connector';
118
118
  readonly action: string;
119
119
  readonly connectionId?: string;