@oomol-lab/open-flow 0.1.0-beta.77 → 0.1.0-beta.78

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 (58) hide show
  1. package/README.md +2 -0
  2. package/dist/browser/{ContentIcon-VZQO8VZU.js → ContentIcon-CMn0c-kl.js} +93 -3
  3. package/dist/browser/{FlowCanvas-BNGDMaWr.js → FlowCanvas-BQGBcfoL.js} +1675 -591
  4. package/dist/browser/{createResourceDialog-BmtakfVg.js → createResourceDialog-DCGxfKFM.js} +3 -3
  5. package/dist/browser/{dialog-BcFBfRB1.js → dialog-WzR4CRFH.js} +11 -81
  6. package/dist/browser/{exportSvg-CG8hEUNk.js → exportSvg-CYwyYiuH.js} +2 -2
  7. package/dist/browser/{flowChanges-BGo_4Ypo.js → flowChanges-Bz4dbDN_.js} +1204 -387
  8. package/dist/browser/{flowWorkspace-BFHGev7s.js → flowWorkspace-BI6L6a4r.js} +557 -597
  9. package/dist/browser/licenses.md +1 -1
  10. package/dist/browser/preview.css +512 -462
  11. package/dist/browser/preview.js +8457 -6732
  12. package/dist/browser/{tooltip-BAWSUexx.js → tooltip-BDcVTNpu.js} +65 -2
  13. package/dist/browser/ui.css +5 -24
  14. package/dist/browser/workbench.css +512 -462
  15. package/dist/browser/workbench.d.ts +2 -3
  16. package/dist/browser/workbench.js +65 -690
  17. package/dist/browser/{workbenchSelect-CjK-xdm3.js → workbenchSelect-DtAd0zc4.js} +2 -2
  18. package/dist/command/index.js +25 -25
  19. package/dist/common/authoring.d.ts +3 -3
  20. package/dist/common/authoringExamples.d.ts +3 -3
  21. package/dist/common/authoringSchema.d.ts +14 -22
  22. package/dist/common/control-api-conformance.js +11 -18
  23. package/dist/common/control-api.d.ts +5 -1
  24. package/dist/common/control-api.js +11 -18
  25. package/dist/common/control-requests.d.ts +4 -3
  26. package/dist/common/control-requests.js +26 -37
  27. package/dist/common/flow-authoring.js +6 -14
  28. package/dist/common/flow-change.d.ts +1 -1
  29. package/dist/common/flow-change.js +6 -14
  30. package/dist/common/flow-encoding.js +5 -13
  31. package/dist/common/flow-semantics.js +15 -21
  32. package/dist/common/flow-state.d.ts +16 -39
  33. package/dist/common/flow-state.js +47 -105
  34. package/dist/common/mcp.d.ts +3 -3
  35. package/dist/common/mcp.js +11 -18
  36. package/dist/common/run-events.js +19 -9
  37. package/dist/common/runtime-contract.js +6 -12
  38. package/dist/common/runtime-nodejs-compat.js +11 -12
  39. package/dist/common/scheduler.d.ts +2 -1
  40. package/dist/common/scheduler.js +56 -50
  41. package/dist/common/state-conformance.d.ts +3 -3
  42. package/package.json +1 -1
  43. package/src/control/common/api.ts +20 -3
  44. package/src/control/common/authoring.ts +6 -5
  45. package/src/control/common/authoringExamples.ts +6 -5
  46. package/src/control/common/authoringSchema.ts +7 -7
  47. package/src/control/common/requests.ts +7 -1
  48. package/src/execution/common/events.ts +22 -10
  49. package/src/execution/common/node-execution.ts +1 -1
  50. package/src/execution/common/scheduler.ts +28 -14
  51. package/src/flow/common/change.ts +1 -1
  52. package/src/flow/common/changeSchema.ts +4 -4
  53. package/src/flow/common/errorHandling.ts +1 -1
  54. package/src/flow/common/semantics.ts +12 -6
  55. package/src/runtime/common/nodejsGuest.generated.json +11 -12
  56. package/src/runtime/common/runtime.ts +6 -7
  57. package/src/state/common/conformance.ts +16 -21
  58. package/src/state/common/state.ts +44 -72
@@ -12485,18 +12485,14 @@ strictObject({
12485
12485
  scope: _enum(["draft", "live"]),
12486
12486
  space: string().regex(/^(flow|node:.+)$/)
12487
12487
  });
12488
- var dedupeConfigSchema = strictObject({
12489
- field: string().default(""),
12490
- mode: _enum(["new", "increasing"]).default("new"),
12491
- space: _enum(["node", "flow"]).default("node")
12492
- });
12493
- var dedupeExecutorSchema = dedupeConfigSchema.extend({ kind: literal("dedupe") });
12494
- function dedupeTask(config = dedupeConfigSchema.parse({}), name = "Remove Duplicates") {
12488
+ var skipDuplicatesConfigSchema = strictObject({ fields: array(string().refine((field) => field.length > 0 && field.split(".").every((part) => part.length > 0), "Use a nonempty dot-separated field path.")).refine((fields) => new Set(fields).size === fields.length, "Identity fields must be distinct.").default([]) });
12489
+ var skipDuplicatesExecutorSchema = skipDuplicatesConfigSchema.extend({ kind: literal("skip-duplicates") });
12490
+ function skipDuplicatesTask(config = skipDuplicatesConfigSchema.parse({}), name = "Skip Duplicates") {
12495
12491
  return {
12496
12492
  name,
12497
12493
  executor: {
12498
12494
  ...config,
12499
- kind: "dedupe"
12495
+ kind: "skip-duplicates"
12500
12496
  },
12501
12497
  inputs: [{
12502
12498
  handle: "items",
@@ -12528,13 +12524,9 @@ object$1({
12528
12524
  })),
12529
12525
  spaces: array(object$1({
12530
12526
  space: string(),
12531
- mode: _enum(["new", "increasing"]),
12532
12527
  count: number().nonnegative(),
12533
- watermark: union([
12534
- string(),
12535
- number(),
12536
- _null()
12537
- ]),
12528
+ pendingCount: number().nonnegative(),
12529
+ reservedCount: number().nonnegative(),
12538
12530
  updatedAt: number()
12539
12531
  }))
12540
12532
  });
@@ -12858,7 +12850,7 @@ var managedFields = object$1({
12858
12850
  revisionId: text$1
12859
12851
  }),
12860
12852
  httpRequestExecutorSchema,
12861
- dedupeExecutorSchema
12853
+ skipDuplicatesExecutorSchema
12862
12854
  ])
12863
12855
  });
12864
12856
  var managed = managedFields.transform(limitDecisionTask);
@@ -13354,7 +13346,7 @@ function changeOperationsSchema(kind) {
13354
13346
  return toJSONSchema(schema, { io: "input" });
13355
13347
  }
13356
13348
  function checkModelVersion(content) {
13357
- if (content.modelVersion < 13 && Object.values(content.document.graph.nodes).some((candidateNode) => candidateNode.kind === "task" && "executor" in candidateNode.task && candidateNode.task.executor.kind === "dedupe")) throw new Error("Deduplication requires Flow model v13.");
13349
+ if (content.modelVersion < 13 && Object.values(content.document.graph.nodes).some((candidateNode) => candidateNode.kind === "task" && "executor" in candidateNode.task && candidateNode.task.executor.kind === "skip-duplicates")) throw new Error("Skip Duplicates requires Flow model v13.");
13358
13350
  if (content.modelVersion < 12 && Object.values(content.document.graph.nodes).some((candidateNode) => candidateNode.kind == "task" && "moduleId" in candidateNode.task && candidateNode.task.capabilities?.some((item) => item.kind == "http"))) throw new TypeError("Code HTTP access requires Flow model version 12.");
13359
13351
  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.");
13360
13352
  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.");
@@ -14039,7 +14031,7 @@ var authoringTypes = [
14039
14031
  "decision",
14040
14032
  "openapi",
14041
14033
  "http-request",
14042
- "dedupe",
14034
+ "skip-duplicates",
14043
14035
  "condition",
14044
14036
  "value",
14045
14037
  "wait",
@@ -14171,7 +14163,7 @@ var authoringConfigurations = {
14171
14163
  "connector": authoringDefinitionSchemas.connector.omit({ outputMode: true }).strict(),
14172
14164
  "openapi": authoringOpenApiSchema,
14173
14165
  "http-request": httpRequestConfigSchema,
14174
- "dedupe": dedupeConfigSchema,
14166
+ "skip-duplicates": skipDuplicatesConfigSchema,
14175
14167
  "agent": authoringDefinitionSchemas.agent.omit({
14176
14168
  prompt: true,
14177
14169
  tools: true
@@ -14391,7 +14383,7 @@ function authoringSchema(kind) {
14391
14383
  guidance: "Read a node type schema before creating or configuring it. Use $alias to reference a preceding node.add in the same batch. Read returns stable node references. Long code and prompt fields use text reads and edits. Changes use the observed baseRevision and a stable requestId."
14392
14384
  } : {
14393
14385
  type: kind,
14394
- onError: kind == "dedupe" || retryNodeTypes.some((nodeType) => nodeType == kind) ? { enum: ["stop", "error"] } : false,
14386
+ onError: kind == "skip-duplicates" || retryNodeTypes.some((nodeType) => nodeType == kind) ? { enum: ["stop", "error"] } : false,
14395
14387
  retry: retryNodeTypes.some((nodeType) => nodeType == kind) ? toJSONSchema(retryConfigurationSchema) : false,
14396
14388
  inputSources: ["poll", "integration"].includes(kind) ? [
14397
14389
  "value",
@@ -14421,7 +14413,7 @@ function authoringSchema(kind) {
14421
14413
  "wait",
14422
14414
  "approval"
14423
14415
  ].includes(kind),
14424
- 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 == "dedupe" ? "Bind an items array. Configure field (dot-separated identity path, empty for scalar items), mode (new or increasing), and space (node or flow). Outputs are items and duplicates. History persists immediately; downstream failures do not roll it back. Increasing mode accepts finite numbers or RFC 3339 timestamps, never mixed types." : 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" ? codeGuidance : "Supply business configuration and input data. Fixed definitions are assembled by the tool. Existing constraints and defaults are preserved on partial updates.",
14416
+ 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 == "skip-duplicates" ? "Bind an items array. Configure fields as dot-separated identity paths; an empty list uses each scalar item. Outputs are items and duplicates. History belongs to this node and commits only when the Run completes successfully. Fields accept strings or finite numbers; missing or complex values reject the batch." : 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" ? codeGuidance : "Supply business configuration and input data. Fixed definitions are assembled by the tool. Existing constraints and defaults are preserved on partial updates.",
14425
14417
  example: {
14426
14418
  op: "node.add",
14427
14419
  as: "step",
@@ -14444,12 +14436,8 @@ function authoringSchema(kind) {
14444
14436
  }]
14445
14437
  } },
14446
14438
  "integration": { config: { key: "<Trigger key>" } },
14447
- "dedupe": {
14448
- config: {
14449
- field: "id",
14450
- mode: "new",
14451
- space: "node"
14452
- },
14439
+ "skip-duplicates": {
14440
+ config: { fields: ["id"] },
14453
14441
  inputs: { items: {
14454
14442
  kind: "value",
14455
14443
  value: [{ id: "a" }, { id: "b" }]
@@ -14714,7 +14702,7 @@ var nodeErrorSchema = {
14714
14702
  }
14715
14703
  };
14716
14704
  function supportsNodeErrorHandling(node) {
14717
- return supportsNodeRetry(node) || node.kind === "task" && "executor" in node.task && node.task.executor.kind === "dedupe";
14705
+ return supportsNodeRetry(node) || node.kind === "task" && "executor" in node.task && node.task.executor.kind === "skip-duplicates";
14718
14706
  }
14719
14707
  function nodeSystemOutputPorts(node) {
14720
14708
  return supportsNodeErrorHandling(node) && node.onError == "error" ? { error: {
@@ -16600,7 +16588,7 @@ function authoringNode(content, ref) {
16600
16588
  inputs: fieldsView([...task.inputs, ...node.kind == "task" ? node.additionalInputs ?? [] : []].filter((port) => !("handle" in port) || !llmReservedInputs.includes(port.handle)))
16601
16589
  };
16602
16590
  else if (task.executor.kind == "workflow") config = { flowId: task.executor.flowId };
16603
- else if (task.executor.kind == "http-request" || task.executor.kind == "dedupe") {
16591
+ else if (task.executor.kind == "http-request" || task.executor.kind == "skip-duplicates") {
16604
16592
  const { kind: _kind, ...settings } = task.executor;
16605
16593
  config = settings;
16606
16594
  } else if (task.executor.kind == "openapi") config = { ...openApiConfigView(task.executor) };
@@ -16990,12 +16978,12 @@ async function compileAuthoring(base, request, host) {
16990
16978
  };
16991
16979
  } else task = await connectorTask(config, String(update.name));
16992
16980
  } else if (type == "decision") task = decisionTask(config.questions, String(update.name));
16993
- else if (type == "dedupe") task = {
16981
+ else if (type == "skip-duplicates") task = {
16994
16982
  ...before,
16995
16983
  name: String(update.name),
16996
16984
  executor: {
16997
- kind: "dedupe",
16998
- ...dedupeConfigSchema.parse(config)
16985
+ kind: "skip-duplicates",
16986
+ ...skipDuplicatesConfigSchema.parse(config)
16999
16987
  }
17000
16988
  };
17001
16989
  else if (type == "http-request") {
@@ -17345,8 +17333,8 @@ async function compileAuthoring(base, request, host) {
17345
17333
  case "connector":
17346
17334
  changes = createManagedTask(identity, await connectorTask(config, name));
17347
17335
  break;
17348
- case "dedupe":
17349
- changes = createManagedTask(identity, dedupeTask(dedupeConfigSchema.parse(config), name));
17336
+ case "skip-duplicates":
17337
+ changes = createManagedTask(identity, skipDuplicatesTask(skipDuplicatesConfigSchema.parse(config), name));
17350
17338
  break;
17351
17339
  case "http-request":
17352
17340
  changes = createManagedTask(identity, httpRequestTask(httpRequestConfigSchema.parse(config), name));
@@ -17569,7 +17557,7 @@ var descriptions = {
17569
17557
  "poll": "Gmail polling by key; replace CONNECTION_ID with an active Gmail connection.",
17570
17558
  "integration": "Telegram webhook integration by key; replace CONNECTION_ID with an active Telegram connection.",
17571
17559
  "decision": "AI Decision evaluates named questions against target and returns a complete answer object per output. Configure target before running.",
17572
- "dedupe": "Filter items using persistent history. Records immediately, including when downstream work fails. Empty field uses each scalar item as identity.",
17560
+ "skip-duplicates": "Skip items already recorded by successful Runs. Configure identity fields; an empty list uses each string or numeric item. History is private to the node and only commits after Run success.",
17573
17561
  "http-request": "A single HTTP request. Bind url and request inputs; outputs are body, statusCode and headers.",
17574
17562
  "openapi": "A fixed JSON API operation. Replace the example document and URL before running.",
17575
17563
  "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.",
@@ -17614,10 +17602,10 @@ function authoringExample(name) {
17614
17602
  instructions: "Does the customer need support?"
17615
17603
  }]);
17616
17604
  break;
17617
- case "dedupe":
17618
- operations = [...createManagedTask({ nodeId: "dedupe" }, dedupeTask()), {
17605
+ case "skip-duplicates":
17606
+ operations = [...createManagedTask({ nodeId: "skip-duplicates" }, skipDuplicatesTask()), {
17619
17607
  kind: "graph.node.input.set",
17620
- nodeId: "dedupe",
17608
+ nodeId: "skip-duplicates",
17621
17609
  handle: "items",
17622
17610
  value: {
17623
17611
  kind: "value",
@@ -17907,6 +17895,7 @@ var schemas = {
17907
17895
  name: flowName,
17908
17896
  teamId: id.optional(),
17909
17897
  tagIds: tagIds.optional(),
17898
+ presentation: record$3(string(), json).optional(),
17910
17899
  version
17911
17900
  }),
17912
17901
  saveTag: strictObject({
@@ -8772,11 +8772,7 @@ strictObject({
8772
8772
  scope: _enum(["draft", "live"]),
8773
8773
  space: string().regex(/^(flow|node:.+)$/)
8774
8774
  });
8775
- var dedupeExecutorSchema = strictObject({
8776
- field: string().default(""),
8777
- mode: _enum(["new", "increasing"]).default("new"),
8778
- space: _enum(["node", "flow"]).default("node")
8779
- }).extend({ kind: literal("dedupe") });
8775
+ var skipDuplicatesExecutorSchema = strictObject({ fields: array(string().refine((field) => field.length > 0 && field.split(".").every((part) => part.length > 0), "Use a nonempty dot-separated field path.")).refine((fields) => new Set(fields).size === fields.length, "Identity fields must be distinct.").default([]) }).extend({ kind: literal("skip-duplicates") });
8780
8776
  object({
8781
8777
  version: literal(1),
8782
8778
  scope: _enum(["draft", "live"]),
@@ -8795,13 +8791,9 @@ object({
8795
8791
  })),
8796
8792
  spaces: array(object({
8797
8793
  space: string(),
8798
- mode: _enum(["new", "increasing"]),
8799
8794
  count: number().nonnegative(),
8800
- watermark: union([
8801
- string(),
8802
- number(),
8803
- _null()
8804
- ]),
8795
+ pendingCount: number().nonnegative(),
8796
+ reservedCount: number().nonnegative(),
8805
8797
  updatedAt: number()
8806
8798
  }))
8807
8799
  });
@@ -9101,7 +9093,7 @@ var managedFields = object({
9101
9093
  revisionId: text
9102
9094
  }),
9103
9095
  httpRequestExecutorSchema,
9104
- dedupeExecutorSchema
9096
+ skipDuplicatesExecutorSchema
9105
9097
  ])
9106
9098
  });
9107
9099
  var managed = managedFields.transform(limitDecisionTask);
@@ -9573,7 +9565,7 @@ array(union([...new Map(Object.entries(shapes).map(([kind, shape]) => [kind, sha
9573
9565
  ...shape
9574
9566
  })])).values()])).min(1);
9575
9567
  function checkModelVersion(content) {
9576
- if (content.modelVersion < 13 && Object.values(content.document.graph.nodes).some((candidateNode) => candidateNode.kind === "task" && "executor" in candidateNode.task && candidateNode.task.executor.kind === "dedupe")) throw new Error("Deduplication requires Flow model v13.");
9568
+ if (content.modelVersion < 13 && Object.values(content.document.graph.nodes).some((candidateNode) => candidateNode.kind === "task" && "executor" in candidateNode.task && candidateNode.task.executor.kind === "skip-duplicates")) throw new Error("Skip Duplicates requires Flow model v13.");
9577
9569
  if (content.modelVersion < 12 && Object.values(content.document.graph.nodes).some((candidateNode) => candidateNode.kind == "task" && "moduleId" in candidateNode.task && candidateNode.task.capabilities?.some((item) => item.kind == "http"))) throw new TypeError("Code HTTP access requires Flow model version 12.");
9578
9570
  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.");
9579
9571
  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.");
@@ -9652,7 +9644,7 @@ function setConditionInput(node, handle, mapping) {
9652
9644
  //#endregion
9653
9645
  //#region src/flow/common/errorHandling.ts
9654
9646
  function supportsNodeErrorHandling(node) {
9655
- return supportsNodeRetry(node) || node.kind === "task" && "executor" in node.task && node.task.executor.kind === "dedupe";
9647
+ return supportsNodeRetry(node) || node.kind === "task" && "executor" in node.task && node.task.executor.kind === "skip-duplicates";
9656
9648
  }
9657
9649
  //#endregion
9658
9650
  //#region src/flow/common/change.ts
@@ -71,7 +71,7 @@ export interface ConditionNode extends GraphNodeBase {
71
71
  readonly matchMode: 'first' | 'all';
72
72
  readonly kind: 'condition';
73
73
  }
74
- export type ManagedTaskExecutor = import('./http-request.js').HttpRequestExecutor | import('./flow-state.js').DedupeExecutor | WorkflowExecutor | import('./decision.js').DecisionExecutor | import('./openapi.js').OpenApiExecutor | {
74
+ export type ManagedTaskExecutor = import('./http-request.js').HttpRequestExecutor | import('./flow-state.js').SkipDuplicatesExecutor | WorkflowExecutor | import('./decision.js').DecisionExecutor | import('./openapi.js').OpenApiExecutor | {
75
75
  readonly kind: 'connector';
76
76
  readonly action: string;
77
77
  readonly connectionId?: string;
@@ -8845,11 +8845,7 @@ strictObject({
8845
8845
  scope: _enum(["draft", "live"]),
8846
8846
  space: string().regex(/^(flow|node:.+)$/)
8847
8847
  });
8848
- var dedupeExecutorSchema = strictObject({
8849
- field: string().default(""),
8850
- mode: _enum(["new", "increasing"]).default("new"),
8851
- space: _enum(["node", "flow"]).default("node")
8852
- }).extend({ kind: literal("dedupe") });
8848
+ var skipDuplicatesExecutorSchema = strictObject({ fields: array(string().refine((field) => field.length > 0 && field.split(".").every((part) => part.length > 0), "Use a nonempty dot-separated field path.")).refine((fields) => new Set(fields).size === fields.length, "Identity fields must be distinct.").default([]) }).extend({ kind: literal("skip-duplicates") });
8853
8849
  object({
8854
8850
  version: literal(1),
8855
8851
  scope: _enum(["draft", "live"]),
@@ -8868,13 +8864,9 @@ object({
8868
8864
  })),
8869
8865
  spaces: array(object({
8870
8866
  space: string(),
8871
- mode: _enum(["new", "increasing"]),
8872
8867
  count: number().nonnegative(),
8873
- watermark: union([
8874
- string(),
8875
- number(),
8876
- _null()
8877
- ]),
8868
+ pendingCount: number().nonnegative(),
8869
+ reservedCount: number().nonnegative(),
8878
8870
  updatedAt: number()
8879
8871
  }))
8880
8872
  });
@@ -9177,7 +9169,7 @@ var managedFields = object({
9177
9169
  revisionId: text
9178
9170
  }),
9179
9171
  httpRequestExecutorSchema,
9180
- dedupeExecutorSchema
9172
+ skipDuplicatesExecutorSchema
9181
9173
  ])
9182
9174
  });
9183
9175
  var managed = managedFields.transform(limitDecisionTask);
@@ -9673,7 +9665,7 @@ function changeOperationsSchema(kind) {
9673
9665
  return toJSONSchema(schema, { io: "input" });
9674
9666
  }
9675
9667
  function checkModelVersion(content) {
9676
- if (content.modelVersion < 13 && Object.values(content.document.graph.nodes).some((candidateNode) => candidateNode.kind === "task" && "executor" in candidateNode.task && candidateNode.task.executor.kind === "dedupe")) throw new Error("Deduplication requires Flow model v13.");
9668
+ if (content.modelVersion < 13 && Object.values(content.document.graph.nodes).some((candidateNode) => candidateNode.kind === "task" && "executor" in candidateNode.task && candidateNode.task.executor.kind === "skip-duplicates")) throw new Error("Skip Duplicates requires Flow model v13.");
9677
9669
  if (content.modelVersion < 12 && Object.values(content.document.graph.nodes).some((candidateNode) => candidateNode.kind == "task" && "moduleId" in candidateNode.task && candidateNode.task.capabilities?.some((item) => item.kind == "http"))) throw new TypeError("Code HTTP access requires Flow model version 12.");
9678
9670
  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.");
9679
9671
  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.");
@@ -9772,7 +9764,7 @@ var nodeErrorSchema = {
9772
9764
  }
9773
9765
  };
9774
9766
  function supportsNodeErrorHandling(node) {
9775
- return supportsNodeRetry(node) || node.kind === "task" && "executor" in node.task && node.task.executor.kind === "dedupe";
9767
+ return supportsNodeRetry(node) || node.kind === "task" && "executor" in node.task && node.task.executor.kind === "skip-duplicates";
9776
9768
  }
9777
9769
  function nodeSystemOutputPorts(node) {
9778
9770
  return supportsNodeErrorHandling(node) && node.onError == "error" ? { error: {
@@ -8616,11 +8616,7 @@ strictObject({
8616
8616
  scope: _enum(["draft", "live"]),
8617
8617
  space: string().regex(/^(flow|node:.+)$/)
8618
8618
  });
8619
- var dedupeExecutorSchema = strictObject({
8620
- field: string().default(""),
8621
- mode: _enum(["new", "increasing"]).default("new"),
8622
- space: _enum(["node", "flow"]).default("node")
8623
- }).extend({ kind: literal("dedupe") });
8619
+ var skipDuplicatesExecutorSchema = strictObject({ fields: array(string().refine((field) => field.length > 0 && field.split(".").every((part) => part.length > 0), "Use a nonempty dot-separated field path.")).refine((fields) => new Set(fields).size === fields.length, "Identity fields must be distinct.").default([]) }).extend({ kind: literal("skip-duplicates") });
8624
8620
  object({
8625
8621
  version: literal(1),
8626
8622
  scope: _enum(["draft", "live"]),
@@ -8639,13 +8635,9 @@ object({
8639
8635
  })),
8640
8636
  spaces: array(object({
8641
8637
  space: string(),
8642
- mode: _enum(["new", "increasing"]),
8643
8638
  count: number().nonnegative(),
8644
- watermark: union([
8645
- string(),
8646
- number(),
8647
- _null()
8648
- ]),
8639
+ pendingCount: number().nonnegative(),
8640
+ reservedCount: number().nonnegative(),
8649
8641
  updatedAt: number()
8650
8642
  }))
8651
8643
  });
@@ -8936,7 +8928,7 @@ var managedFields = object({
8936
8928
  revisionId: text
8937
8929
  }),
8938
8930
  httpRequestExecutorSchema,
8939
- dedupeExecutorSchema
8931
+ skipDuplicatesExecutorSchema
8940
8932
  ])
8941
8933
  });
8942
8934
  var managed = managedFields.transform(limitDecisionTask);
@@ -9655,7 +9647,7 @@ array(union([...new Map(Object.entries(shapes).map(([kind, shape]) => [kind, sha
9655
9647
  ...shape
9656
9648
  })])).values()])).min(1);
9657
9649
  function checkModelVersion(content) {
9658
- if (content.modelVersion < 13 && Object.values(content.document.graph.nodes).some((candidateNode) => candidateNode.kind === "task" && "executor" in candidateNode.task && candidateNode.task.executor.kind === "dedupe")) throw new Error("Deduplication requires Flow model v13.");
9650
+ if (content.modelVersion < 13 && Object.values(content.document.graph.nodes).some((candidateNode) => candidateNode.kind === "task" && "executor" in candidateNode.task && candidateNode.task.executor.kind === "skip-duplicates")) throw new Error("Skip Duplicates requires Flow model v13.");
9659
9651
  if (content.modelVersion < 12 && Object.values(content.document.graph.nodes).some((candidateNode) => candidateNode.kind == "task" && "moduleId" in candidateNode.task && candidateNode.task.capabilities?.some((item) => item.kind == "http"))) throw new TypeError("Code HTTP access requires Flow model version 12.");
9660
9652
  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.");
9661
9653
  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.");
@@ -10374,18 +10374,14 @@ strictObject({
10374
10374
  scope: _enum(["draft", "live"]),
10375
10375
  space: string().regex(/^(flow|node:.+)$/)
10376
10376
  });
10377
- var dedupeConfigSchema = strictObject({
10378
- field: string().default(""),
10379
- mode: _enum(["new", "increasing"]).default("new"),
10380
- space: _enum(["node", "flow"]).default("node")
10381
- });
10382
- var dedupeExecutorSchema = dedupeConfigSchema.extend({ kind: literal("dedupe") });
10383
- function dedupeTask(config = dedupeConfigSchema.parse({}), name = "Remove Duplicates") {
10377
+ var skipDuplicatesConfigSchema = strictObject({ fields: array(string().refine((field) => field.length > 0 && field.split(".").every((part) => part.length > 0), "Use a nonempty dot-separated field path.")).refine((fields) => new Set(fields).size === fields.length, "Identity fields must be distinct.").default([]) });
10378
+ var skipDuplicatesExecutorSchema = skipDuplicatesConfigSchema.extend({ kind: literal("skip-duplicates") });
10379
+ function skipDuplicatesTask(config = skipDuplicatesConfigSchema.parse({}), name = "Skip Duplicates") {
10384
10380
  return {
10385
10381
  name,
10386
10382
  executor: {
10387
10383
  ...config,
10388
- kind: "dedupe"
10384
+ kind: "skip-duplicates"
10389
10385
  },
10390
10386
  inputs: [{
10391
10387
  handle: "items",
@@ -10417,13 +10413,9 @@ object$1({
10417
10413
  })),
10418
10414
  spaces: array(object$1({
10419
10415
  space: string(),
10420
- mode: _enum(["new", "increasing"]),
10421
10416
  count: number().nonnegative(),
10422
- watermark: union([
10423
- string(),
10424
- number(),
10425
- _null()
10426
- ]),
10417
+ pendingCount: number().nonnegative(),
10418
+ reservedCount: number().nonnegative(),
10427
10419
  updatedAt: number()
10428
10420
  }))
10429
10421
  });
@@ -10802,7 +10794,7 @@ var managedFields = object$1({
10802
10794
  revisionId: text
10803
10795
  }),
10804
10796
  httpRequestExecutorSchema,
10805
- dedupeExecutorSchema
10797
+ skipDuplicatesExecutorSchema
10806
10798
  ])
10807
10799
  });
10808
10800
  var managed = managedFields.transform(limitDecisionTask);
@@ -11419,7 +11411,7 @@ var nodeErrorSchema = {
11419
11411
  }
11420
11412
  };
11421
11413
  function supportsNodeErrorHandling(node) {
11422
- return supportsNodeRetry(node) || node.kind === "task" && "executor" in node.task && node.task.executor.kind === "dedupe";
11414
+ return supportsNodeRetry(node) || node.kind === "task" && "executor" in node.task && node.task.executor.kind === "skip-duplicates";
11423
11415
  }
11424
11416
  function nodeSystemOutputPorts(node) {
11425
11417
  return supportsNodeErrorHandling(node) && node.onError == "error" ? { error: {
@@ -25007,10 +24999,10 @@ async function validateFlow(revision, engine) {
25007
24999
  message,
25008
25000
  path: `/document/graph/nodes/${nodeId}/task/executor`
25009
25001
  });
25010
- if (task.executor.kind === "dedupe") {
25011
- const expected = dedupeTask(task.executor);
25002
+ if (task.executor.kind === "skip-duplicates") {
25003
+ const expected = skipDuplicatesTask(task.executor);
25012
25004
  if (!dequal(httpPortContracts(task.inputs), httpPortContracts(expected.inputs)) || !dequal(httpPortContracts(task.outputs), httpPortContracts(expected.outputs))) checked.diagnostics.push({
25013
- code: "dedupe.ports-invalid",
25005
+ code: "skip-duplicates.ports-invalid",
25014
25006
  column: 0,
25015
25007
  line: 1,
25016
25008
  message: "Deduplication ports must match the fixed contract.",
@@ -25151,7 +25143,7 @@ var httpPortContracts = (ports) => ports.filter((port) => "handle" in port).map(
25151
25143
  async function prepareFlow(revision, engineContract, triggerId) {
25152
25144
  const engine = findEngineContract(engineContract);
25153
25145
  if (engine == null) return { kind: "engine-unsupported" };
25154
- if (!engine.flowState && Object.values(revision.document.graph.nodes).some((node) => node.kind === "task" && "executor" in node.task && node.task.executor.kind === "dedupe")) return { kind: "engine-unsupported" };
25146
+ if (!engine.flowState && Object.values(revision.document.graph.nodes).some((node) => node.kind === "task" && "executor" in node.task && node.task.executor.kind === "skip-duplicates")) return { kind: "engine-unsupported" };
25155
25147
  if (!engine.httpRequests && Object.values(revision.document.graph.nodes).some((node) => node.kind == "task" && "executor" in node.task && node.task.executor.kind == "http-request")) return { kind: "engine-unsupported" };
25156
25148
  if (!engine.codeFetch && Object.values(revision.document.graph.nodes).some((node) => node.kind == "task" && "moduleId" in node.task && node.task.capabilities?.some((item) => item.kind == "http"))) return { kind: "engine-unsupported" };
25157
25149
  if (!engine.workflowTools && Object.values(revision.document.graph.nodes).some((node) => nodeWorkflowReferences(node).some((ref) => ref.tool != null))) return { kind: "engine-unsupported" };
@@ -25200,11 +25192,13 @@ async function prepareFlow(revision, engineContract, triggerId) {
25200
25192
  }
25201
25193
  function createRuntimeProgram(prepared, entryModuleId, engineDigest) {
25202
25194
  if (!Object.hasOwn(prepared.modules, entryModuleId)) return;
25195
+ const modules = /* @__PURE__ */ new Set([entryModuleId]);
25196
+ for (const id of modules) for (const imported of prepared.modules[id].imports) modules.add(imported);
25203
25197
  return {
25204
25198
  engineContract: prepared.engineContract,
25205
25199
  engineDigest,
25206
25200
  entryModuleId,
25207
- modules: prepared.modules
25201
+ modules: Object.fromEntries([...modules].map((id) => [id, prepared.modules[id]]))
25208
25202
  };
25209
25203
  }
25210
25204
  function agentActions(flow) {
@@ -24,49 +24,29 @@ export declare const dedupeClearSchema: z.ZodObject<{
24
24
  space: z.ZodString;
25
25
  }, z.core.$strict>;
26
26
  export type DedupeIdentity = string | number;
27
- export declare const dedupeConfigSchema: z.ZodObject<{
28
- field: z.ZodDefault<z.ZodString>;
29
- mode: z.ZodDefault<z.ZodEnum<{
30
- increasing: "increasing";
31
- new: "new";
32
- }>>;
33
- space: z.ZodDefault<z.ZodEnum<{
34
- flow: "flow";
35
- node: "node";
36
- }>>;
27
+ export declare const skipDuplicatesConfigSchema: z.ZodObject<{
28
+ fields: z.ZodDefault<z.ZodArray<z.ZodString>>;
37
29
  }, z.core.$strict>;
38
- export type DedupeConfig = z.infer<typeof dedupeConfigSchema>;
39
- export interface DedupeExecutor extends DedupeConfig {
40
- readonly kind: 'dedupe';
30
+ export type SkipDuplicatesConfig = z.infer<typeof skipDuplicatesConfigSchema>;
31
+ export interface SkipDuplicatesExecutor extends SkipDuplicatesConfig {
32
+ readonly kind: 'skip-duplicates';
41
33
  }
42
- export declare const dedupeExecutorSchema: z.ZodObject<{
43
- field: z.ZodDefault<z.ZodString>;
44
- mode: z.ZodDefault<z.ZodEnum<{
45
- increasing: "increasing";
46
- new: "new";
47
- }>>;
48
- space: z.ZodDefault<z.ZodEnum<{
49
- flow: "flow";
50
- node: "node";
51
- }>>;
52
- kind: z.ZodLiteral<"dedupe">;
34
+ export declare const skipDuplicatesExecutorSchema: z.ZodObject<{
35
+ fields: z.ZodDefault<z.ZodArray<z.ZodString>>;
36
+ kind: z.ZodLiteral<"skip-duplicates">;
53
37
  }, z.core.$strict>;
54
- export declare function dedupeTask(config?: DedupeConfig, name?: string): ManagedTaskDefinitionOf<DedupeExecutor>;
38
+ export declare function skipDuplicatesTask(config?: SkipDuplicatesConfig, name?: string): ManagedTaskDefinitionOf<SkipDuplicatesExecutor>;
55
39
  export declare class StateError extends Error {
56
40
  readonly retryable = false;
57
41
  readonly catchable: boolean;
58
- readonly code: 'capability.invalid' | 'capability.denied' | 'dedupe.full' | 'limit-exceeded';
42
+ readonly code: 'capability.invalid' | 'capability.denied' | 'dedupe.full' | 'limit-exceeded' | 'node.input-invalid';
59
43
  constructor(code: StateError['code'], message: string);
60
44
  }
61
45
  export declare function stateKey(key: unknown): string;
62
46
  export declare function dedupeIdentities(value: unknown): DedupeIdentity[];
63
47
  export declare function hashIdentities(ids: readonly DedupeIdentity[]): Promise<Uint8Array[]>;
64
- export declare function watermarkValue(id: DedupeIdentity): number;
65
- export declare function increasingIdentities(ids: readonly DedupeIdentity[], watermark: DedupeIdentity | null): {
66
- inserted: boolean[];
67
- watermark: DedupeIdentity | null;
68
- };
69
- export declare function itemIdentities(items: JsonValue, field: string): DedupeIdentity[];
48
+ /** Canonical identities include the field definition and preserve scalar types. */
49
+ export declare function skipDuplicatesIdentities(items: JsonValue, fields: readonly string[]): string[];
70
50
  export interface FlowStateView {
71
51
  readonly version: 1;
72
52
  readonly scope: StateScope;
@@ -85,9 +65,9 @@ export interface FlowStateView {
85
65
  }[];
86
66
  readonly spaces: readonly {
87
67
  readonly space: string;
88
- readonly mode: 'new' | 'increasing';
89
68
  readonly count: number;
90
- readonly watermark: DedupeIdentity | null;
69
+ readonly pendingCount: number;
70
+ readonly reservedCount: number;
91
71
  readonly updatedAt: number;
92
72
  }[];
93
73
  }
@@ -112,12 +92,9 @@ export declare const flowStateViewSchema: z.ZodObject<{
112
92
  }, z.core.$strip>>;
113
93
  spaces: z.ZodArray<z.ZodObject<{
114
94
  space: z.ZodString;
115
- mode: z.ZodEnum<{
116
- increasing: "increasing";
117
- new: "new";
118
- }>;
119
95
  count: z.ZodNumber;
120
- watermark: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodNull]>;
96
+ pendingCount: z.ZodNumber;
97
+ reservedCount: z.ZodNumber;
121
98
  updatedAt: z.ZodNumber;
122
99
  }, z.core.$strip>>;
123
100
  }, z.core.$strip>;