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

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 (64) 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-DkovGogv.js} +3 -3
  5. package/dist/browser/{dialog-BcFBfRB1.js → dialog-CFFuRM8s.js} +37 -86
  6. package/dist/browser/{exportSvg-CG8hEUNk.js → exportSvg-CYwyYiuH.js} +2 -2
  7. package/dist/browser/{flowChanges-BGo_4Ypo.js → flowChanges-CN2R-m1M.js} +1258 -422
  8. package/dist/browser/{flowWorkspace-BFHGev7s.js → flowWorkspace-CXVL1FNv.js} +634 -628
  9. package/dist/browser/licenses.md +1 -1
  10. package/dist/browser/preview.css +512 -462
  11. package/dist/browser/preview.js +8183 -6384
  12. package/dist/browser/{tooltip-BAWSUexx.js → tooltip-BDcVTNpu.js} +65 -2
  13. package/dist/browser/{typeScriptSession-CRoezJPN.js → typeScriptSession-CoEIptNC.js} +1 -1
  14. package/dist/browser/ui.css +5 -24
  15. package/dist/browser/workbench.css +512 -462
  16. package/dist/browser/workbench.d.ts +2 -3
  17. package/dist/browser/workbench.js +65 -690
  18. package/dist/browser/{workbenchSelect-CjK-xdm3.js → workbenchSelect-CkOS95gX.js} +2 -2
  19. package/dist/command/index.js +31 -31
  20. package/dist/common/authoring.d.ts +3 -3
  21. package/dist/common/authoringExamples.d.ts +3 -3
  22. package/dist/common/authoringSchema.d.ts +14 -22
  23. package/dist/common/control-api-conformance.js +91 -25
  24. package/dist/common/control-api.d.ts +8 -3
  25. package/dist/common/control-api.js +51 -25
  26. package/dist/common/control-requests.d.ts +9 -3
  27. package/dist/common/control-requests.js +54 -42
  28. package/dist/common/flow-authoring.js +32 -19
  29. package/dist/common/flow-change.d.ts +1 -1
  30. package/dist/common/flow-change.js +32 -19
  31. package/dist/common/flow-encoding.js +31 -18
  32. package/dist/common/flow-semantics.js +41 -26
  33. package/dist/common/flow-state.d.ts +49 -43
  34. package/dist/common/flow-state.js +95 -110
  35. package/dist/common/mcp.d.ts +35 -4
  36. package/dist/common/mcp.js +51 -26
  37. package/dist/common/run-events.js +19 -9
  38. package/dist/common/runtime-contract.d.ts +4 -1
  39. package/dist/common/runtime-contract.js +66 -17
  40. package/dist/common/runtime-nodejs-compat.js +11 -12
  41. package/dist/common/scheduler.d.ts +2 -1
  42. package/dist/common/scheduler.js +82 -55
  43. package/dist/common/state-conformance.d.ts +3 -3
  44. package/dist/index.d.ts +1 -0
  45. package/package.json +1 -1
  46. package/src/control/common/api.ts +36 -7
  47. package/src/control/common/authoring.ts +6 -5
  48. package/src/control/common/authoringExamples.ts +6 -5
  49. package/src/control/common/authoringSchema.ts +7 -7
  50. package/src/control/common/conformance.ts +31 -0
  51. package/src/control/common/mcp.ts +7 -2
  52. package/src/control/common/requests.ts +10 -1
  53. package/src/execution/common/events.ts +22 -10
  54. package/src/execution/common/node-execution.ts +1 -1
  55. package/src/execution/common/scheduler.ts +28 -14
  56. package/src/flow/common/change.ts +1 -1
  57. package/src/flow/common/changeSchema.ts +4 -4
  58. package/src/flow/common/errorHandling.ts +1 -1
  59. package/src/flow/common/semantics.ts +12 -6
  60. package/src/runtime/common/nodejsGuest.generated.json +11 -12
  61. package/src/runtime/common/runtime.ts +45 -12
  62. package/src/state/common/conformance.ts +32 -21
  63. package/src/state/common/state.ts +85 -80
  64. package/src/types/index.ts +1 -0
@@ -5604,44 +5604,58 @@ function dequal(foo, bar) {
5604
5604
  //#region src/state/common/conformance.ts
5605
5605
  var stateConformanceCases = [
5606
5606
  {
5607
- name: "persists watermarks by instant and resets changed modes atomically",
5607
+ name: "reads selected State keys in input order with missing, falsy and independent values",
5608
5608
  async verify(harness) {
5609
- const config = {
5610
- field: "",
5611
- mode: "increasing",
5612
- space: "node"
5613
- };
5614
- if (!dequal(await harness.dedupe(config, [
5615
- "2026-10-30T09:00:00+08:00",
5616
- "2026-10-30T01:00:00Z",
5617
- "2026-10-30T02:00:00Z"
5618
- ]), {
5619
- items: ["2026-10-30T09:00:00+08:00", "2026-10-30T02:00:00Z"],
5620
- duplicates: ["2026-10-30T01:00:00Z"]
5621
- })) throw new Error("Watermarks must compare instants and preserve item order.");
5622
- let rejected = false;
5623
- try {
5624
- await harness.dedupe(config, [1]);
5625
- } catch {
5626
- rejected = true;
5627
- }
5628
- if (!rejected) throw new Error("Mixed watermark types must fail.");
5629
- if (!dequal(await harness.dedupe(config, ["2026-10-30T02:00:00Z", "2026-10-30T03:00:00Z"]), {
5630
- items: ["2026-10-30T03:00:00Z"],
5631
- duplicates: ["2026-10-30T02:00:00Z"]
5632
- })) throw new Error("A failed batch must preserve the previous watermark.");
5633
- await harness.dedupe({
5634
- ...config,
5635
- mode: "new"
5636
- }, []);
5637
- if (!dequal(await harness.dedupe(config, [
5638
- 5,
5639
- 3,
5640
- 5
5641
- ]), {
5642
- items: [5, 3],
5643
- duplicates: [5]
5644
- })) throw new Error("A mode reset must discard previous watermarks, including on empty batches.");
5609
+ if (!dequal(await harness.run(`export default async (_, context) => {
5610
+ await context.state.set('object', { nested: [1] });
5611
+ await context.state.set('zero', 0);
5612
+ await context.state.set('false', false);
5613
+ await context.state.set('empty', '');
5614
+ const values = await context.state.get(['false', 'missing', 'object', 'zero', 'empty']);
5615
+ values[2].nested.push(2);
5616
+ return {result: {values, retained: await context.state.get('object'), zero: await context.state.get('zero')}};
5617
+ }`), {
5618
+ values: [
5619
+ false,
5620
+ null,
5621
+ { nested: [1, 2] },
5622
+ 0,
5623
+ ""
5624
+ ],
5625
+ retained: { nested: [1] },
5626
+ zero: 0
5627
+ })) throw new Error("State batch reads must preserve order, missing keys and independent JSON values.");
5628
+ }
5629
+ },
5630
+ {
5631
+ name: "skips composite identities across successful Runs without depending on field order",
5632
+ async verify(harness) {
5633
+ const items = [
5634
+ {
5635
+ company: "a",
5636
+ id: 1
5637
+ },
5638
+ {
5639
+ company: "b",
5640
+ id: 1
5641
+ },
5642
+ {
5643
+ company: "a",
5644
+ id: 1
5645
+ }
5646
+ ];
5647
+ if (!dequal(await harness.skipDuplicates({ fields: ["company", "id"] }, items), {
5648
+ items: items.slice(0, 2),
5649
+ duplicates: [items[2]]
5650
+ })) throw new Error("Composite identities must retain the first item in input order.");
5651
+ if (!dequal(await harness.skipDuplicates({ fields: ["id", "company"] }, items), {
5652
+ items: [],
5653
+ duplicates: items
5654
+ })) throw new Error("Successful Run history must persist independently of field order.");
5655
+ if (!dequal(await harness.skipDuplicates({ fields: ["id"] }, items), {
5656
+ items: [items[0]],
5657
+ duplicates: items.slice(1)
5658
+ })) throw new Error("The field definition must participate in identity.");
5645
5659
  }
5646
5660
  },
5647
5661
  {
@@ -5710,30 +5724,35 @@ var stateConformanceCases = [
5710
5724
  ];
5711
5725
  //#endregion
5712
5726
  //#region src/state/common/state.ts
5727
+ var stateReadLimits = {
5728
+ maxKeys: 16,
5729
+ maxBytes: 1048576
5730
+ };
5731
+ var keySchema = string().min(1).refine((value) => new TextEncoder().encode(value).length <= 256);
5713
5732
  var stateQuerySchema = strictObject({
5714
5733
  scope: _enum(["draft", "live"]),
5715
- values: boolean().optional()
5734
+ values: literal(false).optional()
5735
+ });
5736
+ var stateReadSchema = strictObject({
5737
+ scope: _enum(["draft", "live"]),
5738
+ keys: array(keySchema).min(1).max(stateReadLimits.maxKeys).refine((keys) => new Set(keys).size === keys.length, "State keys must be distinct.")
5716
5739
  });
5717
5740
  var stateClearSchema = strictObject({
5718
5741
  scope: _enum(["draft", "live"]),
5719
- key: string().min(1).refine((key) => new TextEncoder().encode(key).length <= 256).optional()
5742
+ key: keySchema.optional()
5720
5743
  });
5721
5744
  var dedupeClearSchema = strictObject({
5722
5745
  scope: _enum(["draft", "live"]),
5723
5746
  space: string().regex(/^(flow|node:.+)$/)
5724
5747
  });
5725
- var dedupeConfigSchema = strictObject({
5726
- field: string().default(""),
5727
- mode: _enum(["new", "increasing"]).default("new"),
5728
- space: _enum(["node", "flow"]).default("node")
5729
- });
5730
- var dedupeExecutorSchema = dedupeConfigSchema.extend({ kind: literal("dedupe") });
5731
- function dedupeTask(config = dedupeConfigSchema.parse({}), name = "Remove Duplicates") {
5748
+ 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([]) });
5749
+ var skipDuplicatesExecutorSchema = skipDuplicatesConfigSchema.extend({ kind: literal("skip-duplicates") });
5750
+ function skipDuplicatesTask(config = skipDuplicatesConfigSchema.parse({}), name = "Skip Duplicates") {
5732
5751
  return {
5733
5752
  name,
5734
5753
  executor: {
5735
5754
  ...config,
5736
- kind: "dedupe"
5755
+ kind: "skip-duplicates"
5737
5756
  },
5738
5757
  inputs: [{
5739
5758
  handle: "items",
@@ -5753,7 +5772,7 @@ var StateError = class extends Error {
5753
5772
  this.retryable = false;
5754
5773
  this.name = "StateError";
5755
5774
  this.code = code;
5756
- this.catchable = code === "dedupe.full";
5775
+ this.catchable = code === "dedupe.full" || code === "node.input-invalid";
5757
5776
  }
5758
5777
  };
5759
5778
  function stateKey(key) {
@@ -5767,58 +5786,18 @@ function dedupeIdentities(value) {
5767
5786
  async function hashIdentities(ids) {
5768
5787
  return Promise.all(ids.map(async (id) => new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(`${typeof id}:${String(id)}`))).slice(0, 16)));
5769
5788
  }
5770
- function watermarkValue(id) {
5771
- if (typeof id === "number") {
5772
- if (!Number.isFinite(id)) throw new StateError("capability.invalid", "Watermarks must be finite.");
5773
- return id;
5774
- }
5775
- const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,3}))?(Z|[+-]\d{2}:\d{2})$/.exec(id);
5776
- if (match == null) throw new StateError("capability.invalid", "Watermarks require RFC 3339 timestamps with an explicit timezone.");
5777
- const [, year, month, day, hour, minute, second, , zone] = match;
5778
- const y = Number(year);
5779
- const days = [
5780
- 31,
5781
- y % 4 === 0 && (y % 100 !== 0 || y % 400 === 0) ? 29 : 28,
5782
- 31,
5783
- 30,
5784
- 31,
5785
- 30,
5786
- 31,
5787
- 31,
5788
- 30,
5789
- 31,
5790
- 30,
5791
- 31
5792
- ][Number(month) - 1] ?? 0;
5793
- const result = Date.parse(id);
5794
- if (Number(month) < 1 || Number(month) > 12 || Number(day) < 1 || Number(day) > days || Number(hour) > 23 || Number(minute) > 59 || Number(second) > 59 || zone !== "Z" && (Number(zone.slice(1, 3)) > 23 || Number(zone.slice(4)) > 59) || !Number.isFinite(result)) throw new StateError("capability.invalid", "Watermarks require valid calendar timestamps.");
5795
- return result;
5796
- }
5797
- function increasingIdentities(ids, watermark) {
5798
- const type = typeof (watermark ?? ids[0]);
5799
- if (ids.some((id) => typeof id !== type)) throw new StateError("capability.invalid", "Watermark types must not be mixed.");
5800
- const initial = watermark == null ? -Infinity : watermarkValue(watermark);
5801
- const seen = /* @__PURE__ */ new Set();
5802
- let maximum = initial;
5803
- let next = watermark;
5804
- return {
5805
- inserted: ids.map((id) => {
5806
- const value = watermarkValue(id);
5807
- const fresh = value > initial && !seen.has(value);
5808
- seen.add(value);
5809
- if (value > maximum) {
5810
- maximum = value;
5811
- next = id;
5812
- }
5813
- return fresh;
5814
- }),
5815
- watermark: next
5816
- };
5817
- }
5818
- function itemIdentities(items, field) {
5819
- if (!Array.isArray(items)) throw new StateError("capability.invalid", "Deduplication requires an array of items.");
5820
- const path = field === "" ? [] : field.split(".");
5821
- return dedupeIdentities(items.map((item) => path.reduce((value, key) => value != null && typeof value === "object" && Object.hasOwn(value, key) ? value[key] : void 0, item)));
5789
+ /** Canonical identities include the field definition and preserve scalar types. */
5790
+ function skipDuplicatesIdentities(items, fields) {
5791
+ if (!Array.isArray(items)) throw new StateError("node.input-invalid", "Skip Duplicates requires an array of items.");
5792
+ const paths = fields.toSorted();
5793
+ return items.map((item, index) => {
5794
+ const values = (paths.length === 0 ? [""] : paths).map((field) => {
5795
+ const value = field === "" ? item : field.split(".").reduce((current, key) => current != null && typeof current === "object" && Object.hasOwn(current, key) ? current[key] : void 0, item);
5796
+ if (typeof value !== "string" && !(typeof value === "number" && Number.isFinite(value))) throw new StateError("node.input-invalid", `Item ${index + 1}, ${field === "" ? "the item itself" : `field ${JSON.stringify(field)}`} must be a string or finite number.`);
5797
+ return value;
5798
+ });
5799
+ return JSON.stringify([paths, values]);
5800
+ });
5822
5801
  }
5823
5802
  var flowStateViewSchema = object({
5824
5803
  version: literal(1),
@@ -5830,23 +5809,29 @@ var flowStateViewSchema = object({
5830
5809
  maxIdentities: number().positive(),
5831
5810
  maxRunWrites: number().positive()
5832
5811
  }),
5833
- values: array(object({
5812
+ values: array(strictObject({
5834
5813
  key: string(),
5835
5814
  size: number().nonnegative(),
5836
- updatedAt: number(),
5837
- value: json().optional()
5815
+ updatedAt: number()
5838
5816
  })),
5839
5817
  spaces: array(object({
5840
5818
  space: string(),
5841
- mode: _enum(["new", "increasing"]),
5842
5819
  count: number().nonnegative(),
5843
- watermark: union([
5844
- string(),
5845
- number(),
5846
- _null()
5847
- ]),
5820
+ pendingCount: number().nonnegative(),
5821
+ reservedCount: number().nonnegative(),
5848
5822
  updatedAt: number()
5849
5823
  }))
5850
5824
  });
5825
+ var flowStateValuesSchema = object({
5826
+ version: literal(1),
5827
+ scope: _enum(["draft", "live"]),
5828
+ values: array(strictObject({
5829
+ key: keySchema,
5830
+ value: json()
5831
+ })).min(1).max(stateReadLimits.maxKeys)
5832
+ }).refine((response) => {
5833
+ const encoder = new TextEncoder();
5834
+ return response.values.reduce((bytes, item) => bytes + (item.value === null ? 0 : encoder.encode(JSON.stringify(item.value)).byteLength), 0) <= stateReadLimits.maxBytes;
5835
+ }, "State values exceed the 1 MiB read limit.");
5851
5836
  //#endregion
5852
- export { StateError, dedupeClearSchema, dedupeConfigSchema, dedupeExecutorSchema, dedupeIdentities, dedupeTask, flowStateViewSchema, hashIdentities, increasingIdentities, itemIdentities, stateClearSchema, stateConformanceCases, stateKey, stateQuerySchema, watermarkValue };
5837
+ export { StateError, dedupeClearSchema, dedupeIdentities, flowStateValuesSchema, flowStateViewSchema, hashIdentities, skipDuplicatesConfigSchema, skipDuplicatesExecutorSchema, skipDuplicatesIdentities, skipDuplicatesTask, stateClearSchema, stateConformanceCases, stateKey, stateQuerySchema, stateReadLimits, stateReadSchema };
@@ -22,7 +22,38 @@ export declare const mcpTools: {
22
22
  validate(value: unknown): {
23
23
  value: {
24
24
  scope: "draft" | "live";
25
- values?: boolean | undefined;
25
+ values?: false | undefined;
26
+ flowId: string;
27
+ };
28
+ } | {
29
+ issues: {
30
+ message: string;
31
+ path: string[];
32
+ }[];
33
+ };
34
+ };
35
+ };
36
+ };
37
+ flow_state_read: {
38
+ description: string;
39
+ annotations: {
40
+ readOnlyHint: boolean;
41
+ destructiveHint: boolean;
42
+ idempotentHint: boolean;
43
+ openWorldHint: boolean;
44
+ };
45
+ inputSchema: {
46
+ '~standard': {
47
+ version: 1;
48
+ vendor: string;
49
+ jsonSchema: {
50
+ input: () => Record<string, unknown>;
51
+ output: () => Record<string, unknown>;
52
+ };
53
+ validate(value: unknown): {
54
+ value: {
55
+ scope: "draft" | "live";
56
+ keys: string[];
26
57
  flowId: string;
27
58
  };
28
59
  } | {
@@ -254,7 +285,7 @@ export declare const mcpTools: {
254
285
  value: {
255
286
  revision?: string | undefined;
256
287
  query: string;
257
- type?: "agent" | "approval" | "code" | "condition" | "connector" | "cron" | "decision" | "dedupe" | "error" | "http-request" | "integration" | "llm" | "openapi" | "outputs" | "poll" | "start" | "value" | "wait" | "webhook" | "webhook-response" | "workflow" | undefined;
288
+ type?: "agent" | "approval" | "code" | "condition" | "connector" | "cron" | "decision" | "error" | "http-request" | "integration" | "llm" | "openapi" | "outputs" | "poll" | "skip-duplicates" | "start" | "value" | "wait" | "webhook" | "webhook-response" | "workflow" | undefined;
258
289
  offset: number;
259
290
  limit: number;
260
291
  flowId: string;
@@ -286,7 +317,7 @@ export declare const mcpTools: {
286
317
  };
287
318
  validate(value: unknown): {
288
319
  value: {
289
- type?: "agent" | "approval" | "code" | "condition" | "connector" | "cron" | "decision" | "dedupe" | "error" | "http-request" | "integration" | "llm" | "openapi" | "outputs" | "poll" | "start" | "value" | "wait" | "webhook" | "webhook-response" | "workflow" | undefined;
320
+ type?: "agent" | "approval" | "code" | "condition" | "connector" | "cron" | "decision" | "error" | "http-request" | "integration" | "llm" | "openapi" | "outputs" | "poll" | "skip-duplicates" | "start" | "value" | "wait" | "webhook" | "webhook-response" | "workflow" | undefined;
290
321
  action?: string | undefined;
291
322
  flowId?: string | undefined;
292
323
  };
@@ -414,7 +445,7 @@ export declare const mcpTools: {
414
445
  edits: ({
415
446
  op: "node.add";
416
447
  as: string;
417
- type: "agent" | "approval" | "code" | "condition" | "connector" | "cron" | "decision" | "dedupe" | "error" | "http-request" | "integration" | "llm" | "openapi" | "outputs" | "poll" | "start" | "value" | "wait" | "webhook" | "webhook-response" | "workflow";
448
+ type: "agent" | "approval" | "code" | "condition" | "connector" | "cron" | "decision" | "error" | "http-request" | "integration" | "llm" | "openapi" | "outputs" | "poll" | "skip-duplicates" | "start" | "value" | "wait" | "webhook" | "webhook-response" | "workflow";
418
449
  name: string;
419
450
  onError?: "error" | "stop" | undefined;
420
451
  retry?: {
@@ -8848,24 +8848,29 @@ var httpRequestConfigSchema = strictObject({
8848
8848
  var httpRequestExecutorSchema = httpRequestConfigSchema.extend({ kind: literal("http-request") });
8849
8849
  //#endregion
8850
8850
  //#region src/state/common/state.ts
8851
+ var stateReadLimits = {
8852
+ maxKeys: 16,
8853
+ maxBytes: 1048576
8854
+ };
8855
+ var keySchema = string$1().min(1).refine((value) => new TextEncoder().encode(value).length <= 256);
8851
8856
  var stateQuerySchema = strictObject({
8852
8857
  scope: _enum(["draft", "live"]),
8853
- values: boolean().optional()
8858
+ values: literal(false).optional()
8859
+ });
8860
+ var stateReadSchema = strictObject({
8861
+ scope: _enum(["draft", "live"]),
8862
+ keys: array(keySchema).min(1).max(stateReadLimits.maxKeys).refine((keys) => new Set(keys).size === keys.length, "State keys must be distinct.")
8854
8863
  });
8855
8864
  var stateClearSchema = strictObject({
8856
8865
  scope: _enum(["draft", "live"]),
8857
- key: string$1().min(1).refine((key) => new TextEncoder().encode(key).length <= 256).optional()
8866
+ key: keySchema.optional()
8858
8867
  });
8859
8868
  var dedupeClearSchema = strictObject({
8860
8869
  scope: _enum(["draft", "live"]),
8861
8870
  space: string$1().regex(/^(flow|node:.+)$/)
8862
8871
  });
8863
- var dedupeConfigSchema = strictObject({
8864
- field: string$1().default(""),
8865
- mode: _enum(["new", "increasing"]).default("new"),
8866
- space: _enum(["node", "flow"]).default("node")
8867
- });
8868
- var dedupeExecutorSchema = dedupeConfigSchema.extend({ kind: literal("dedupe") });
8872
+ var skipDuplicatesConfigSchema = strictObject({ fields: array(string$1().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([]) });
8873
+ var skipDuplicatesExecutorSchema = skipDuplicatesConfigSchema.extend({ kind: literal("skip-duplicates") });
8869
8874
  var flowStateViewSchema = object({
8870
8875
  version: literal(1),
8871
8876
  scope: _enum(["draft", "live"]),
@@ -8876,24 +8881,30 @@ var flowStateViewSchema = object({
8876
8881
  maxIdentities: number().positive(),
8877
8882
  maxRunWrites: number().positive()
8878
8883
  }),
8879
- values: array(object({
8884
+ values: array(strictObject({
8880
8885
  key: string$1(),
8881
8886
  size: number().nonnegative(),
8882
- updatedAt: number(),
8883
- value: json$4().optional()
8887
+ updatedAt: number()
8884
8888
  })),
8885
8889
  spaces: array(object({
8886
8890
  space: string$1(),
8887
- mode: _enum(["new", "increasing"]),
8888
8891
  count: number().nonnegative(),
8889
- watermark: union([
8890
- string$1(),
8891
- number(),
8892
- _null()
8893
- ]),
8892
+ pendingCount: number().nonnegative(),
8893
+ reservedCount: number().nonnegative(),
8894
8894
  updatedAt: number()
8895
8895
  }))
8896
8896
  });
8897
+ var flowStateValuesSchema = object({
8898
+ version: literal(1),
8899
+ scope: _enum(["draft", "live"]),
8900
+ values: array(strictObject({
8901
+ key: keySchema,
8902
+ value: json$4()
8903
+ })).min(1).max(stateReadLimits.maxKeys)
8904
+ }).refine((response) => {
8905
+ const encoder = new TextEncoder();
8906
+ return response.values.reduce((bytes, item) => bytes + (item.value === null ? 0 : encoder.encode(JSON.stringify(item.value)).byteLength), 0) <= stateReadLimits.maxBytes;
8907
+ }, "State values exceed the 1 MiB read limit.");
8897
8908
  function checkJsonDepth(value, depth = 0) {
8898
8909
  if (depth > 64) throw new TypeError("Value exceeds the maximum JSON depth.");
8899
8910
  if (value != null && typeof value == "object") for (const child of Object.values(value)) checkJsonDepth(child, depth + 1);
@@ -9183,7 +9194,7 @@ var managedFields = object({
9183
9194
  revisionId: text
9184
9195
  }),
9185
9196
  httpRequestExecutorSchema,
9186
- dedupeExecutorSchema
9197
+ skipDuplicatesExecutorSchema
9187
9198
  ])
9188
9199
  });
9189
9200
  var managed = managedFields.transform(limitDecisionTask);
@@ -9655,7 +9666,7 @@ array(union([...new Map(Object.entries(shapes).map(([kind, shape]) => [kind, sha
9655
9666
  ...shape
9656
9667
  })])).values()])).min(1);
9657
9668
  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.");
9669
+ 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
9670
  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
9671
  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
9672
  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.");
@@ -9812,7 +9823,7 @@ var type = _enum([
9812
9823
  "decision",
9813
9824
  "openapi",
9814
9825
  "http-request",
9815
- "dedupe",
9826
+ "skip-duplicates",
9816
9827
  "condition",
9817
9828
  "value",
9818
9829
  "wait",
@@ -9943,7 +9954,7 @@ var authoringConfigurations = {
9943
9954
  "connector": authoringDefinitionSchemas.connector.omit({ outputMode: true }).strict(),
9944
9955
  "openapi": authoringOpenApiSchema,
9945
9956
  "http-request": httpRequestConfigSchema,
9946
- "dedupe": dedupeConfigSchema,
9957
+ "skip-duplicates": skipDuplicatesConfigSchema,
9947
9958
  "agent": authoringDefinitionSchemas.agent.omit({
9948
9959
  prompt: true,
9949
9960
  tools: true
@@ -12057,7 +12068,7 @@ function decodeRunEvent(value) {
12057
12068
  switch (kind) {
12058
12069
  case "node.started": {
12059
12070
  const nodeKind = payload.nodeKind;
12060
- if (nodeKind !== void 0 && nodeKind !== "approval" && nodeKind !== "condition" && nodeKind !== "connector" && nodeKind !== "javascript" && nodeKind !== "decision" && nodeKind !== "openapi" && nodeKind !== "http-request" && nodeKind !== "dedupe" && nodeKind !== "llm" && nodeKind !== "agent" && nodeKind !== "value" && nodeKind !== "outputs" && nodeKind !== "webhook-response" && nodeKind !== "workflow" && nodeKind !== "wait") return invalidResponse();
12071
+ if (nodeKind !== void 0 && nodeKind !== "approval" && nodeKind !== "condition" && nodeKind !== "connector" && nodeKind !== "javascript" && nodeKind !== "decision" && nodeKind !== "openapi" && nodeKind !== "http-request" && nodeKind !== "skip-duplicates" && nodeKind !== "llm" && nodeKind !== "agent" && nodeKind !== "value" && nodeKind !== "outputs" && nodeKind !== "webhook-response" && nodeKind !== "workflow" && nodeKind !== "wait") return invalidResponse();
12061
12072
  return {
12062
12073
  ...base,
12063
12074
  kind,
@@ -12282,12 +12293,13 @@ var ControlClient = class {
12282
12293
  })
12283
12294
  }));
12284
12295
  }
12285
- async createFlow(name, idempotencyKey = `flow-${randomId()}`, teamId, tagIds) {
12296
+ async createFlow(name, idempotencyKey = `flow-${randomId()}`, teamId, tagIds, initialPresentation) {
12286
12297
  return flow$1(await this.request("/v1/flows", {
12287
12298
  body: JSON.stringify({
12288
12299
  name,
12289
12300
  ...teamId == null ? {} : { teamId },
12290
12301
  ...tagIds == null ? {} : { tagIds },
12302
+ ...initialPresentation == null ? {} : { presentation: initialPresentation },
12291
12303
  version: 1
12292
12304
  }),
12293
12305
  headers: { "idempotency-key": idempotencyKey },
@@ -12369,10 +12381,22 @@ var ControlClient = class {
12369
12381
  })
12370
12382
  }));
12371
12383
  }
12372
- async getFlowState(flowId, scope, values = false) {
12373
- const result = flowStateViewSchema.safeParse(await this.request(`/v1/flows/${segment(flowId)}/state?scope=${scope}&values=${values}`));
12384
+ async getFlowState(flowId, scope) {
12385
+ const result = flowStateViewSchema.safeParse(await this.request(`/v1/flows/${segment(flowId)}/state?scope=${scope}`));
12374
12386
  return result.success && result.data.scope === scope ? result.data : invalidResponse();
12375
12387
  }
12388
+ async getFlowStateValues(flowId, scope, keys, signal) {
12389
+ const result = flowStateValuesSchema.safeParse(await this.request(`/v1/flows/${segment(flowId)}/state/read`, {
12390
+ method: "POST",
12391
+ body: JSON.stringify({
12392
+ version: 1,
12393
+ scope,
12394
+ keys
12395
+ }),
12396
+ signal
12397
+ }));
12398
+ return result.success && result.data.scope === scope && result.data.values.length === keys.length && result.data.values.every((item, index) => item.key === keys[index]) ? result.data : invalidResponse();
12399
+ }
12376
12400
  async clearFlowState(flowId, scope, key) {
12377
12401
  const source = record(await this.request(`/v1/flows/${segment(flowId)}/state?scope=${scope}${key == null ? "" : `&key=${encodeURIComponent(key)}`}`, { method: "DELETE" }));
12378
12402
  if (source.version !== 1 || source.cleared !== true) invalidResponse();
@@ -13135,7 +13159,8 @@ function tool(description, schema, readOnly, idempotent = true) {
13135
13159
  };
13136
13160
  }
13137
13161
  var mcpTools = {
13138
- flow_state: tool("Inspect Flow state usage, business keys and deduplication spaces. Values are omitted unless requested. Scope is required.", stateQuerySchema.extend({ flowId: flow }), true),
13162
+ flow_state: tool("Inspect Flow state usage, business key metadata and deduplication spaces. Never returns values. Scope is required.", stateQuerySchema.extend({ flowId: flow }), true),
13163
+ flow_state_read: tool("Read values for 1–16 distinct named State keys in one explicit scope. Values are returned in request order; missing keys return null. The combined stored JSON limit is 1 MiB; oversized reads fail without partial results.", stateReadSchema.extend({ flowId: flow }), true),
13139
13164
  flow_state_clear: tool("Delete one business state key or clear all business values. Explicit draft/live scope is required; live changes production state immediately.", stateClearSchema.extend({ flowId: flow }), false),
13140
13165
  flow_dedupe_clear: tool("Clear one deduplication space (flow or node:<id>). Explicit draft/live scope is required; live changes production history immediately.", dedupeClearSchema.extend({ flowId: flow }), false),
13141
13166
  flow_canvas: tool("Broadcast a transient canvas command to connected Workbenches viewing this Flow. Success only confirms Server broadcast, not delivery or execution. No replay, session selection or Presentation save. Pan uses screen pixels (positive right/down); zoom is an absolute ratio (0.1–3); fit includes all nodes. Repeating pan moves again.", strictObject({
@@ -54,7 +54,7 @@ var nodeKinds = /* @__PURE__ */ new Set([
54
54
  "javascript",
55
55
  "openapi",
56
56
  "http-request",
57
- "dedupe",
57
+ "skip-duplicates",
58
58
  "llm",
59
59
  "value",
60
60
  "wait",
@@ -81,12 +81,12 @@ function nodeKind(value) {
81
81
  function runId(event) {
82
82
  return string(event.runId, "Runtime event runId");
83
83
  }
84
- function nodeContext(event, flows, platformRunId) {
84
+ function nodeContext(event, flows, platformRunId, identify) {
85
85
  const rawRunId = runId(event);
86
86
  const flowId = flows.get(rawRunId);
87
87
  if (flowId == null) throw new TypeError("Runtime node event preceded its run.started event.");
88
88
  return {
89
- executionId: projectExecutionId(platformRunId, rawRunId, string(event.jobId, "Runtime node event jobId")),
89
+ executionId: identify("execution", platformRunId, rawRunId, string(event.jobId, "Runtime node event jobId")),
90
90
  flowId,
91
91
  nodeId: string(event.nodeId, "Runtime node event nodeId"),
92
92
  rawRunId
@@ -139,6 +139,16 @@ function createEventProjector(platformRunId, nodeFailureCodes) {
139
139
  const jobs = /* @__PURE__ */ new Map();
140
140
  const nodeProgressBuckets = /* @__PURE__ */ new Map();
141
141
  const runProgressBuckets = /* @__PURE__ */ new Map();
142
+ const identities = /* @__PURE__ */ new Map();
143
+ const identify = (prefix, ...parts) => {
144
+ const key = JSON.stringify([prefix, ...parts]);
145
+ let identity = identities.get(key);
146
+ if (identity == null) {
147
+ identity = opaqueId(prefix, ...parts);
148
+ identities.set(key, identity);
149
+ }
150
+ return identity;
151
+ };
142
152
  return async (value) => {
143
153
  const event = object(value, "Runtime event");
144
154
  const type = string(event.type, "Runtime event type");
@@ -157,8 +167,8 @@ function createEventProjector(platformRunId, nodeFailureCodes) {
157
167
  kind: "run.started",
158
168
  payload: {
159
169
  flowId,
160
- ...parentRunId === void 0 ? {} : { parentScopeId: await opaqueId("scope", platformRunId, parentRunId) },
161
- scopeId: await opaqueId("scope", platformRunId, rawRunId)
170
+ ...parentRunId === void 0 ? {} : { parentScopeId: await identify("scope", platformRunId, parentRunId) },
171
+ scopeId: await identify("scope", platformRunId, rawRunId)
162
172
  }
163
173
  };
164
174
  }
@@ -175,7 +185,7 @@ function createEventProjector(platformRunId, nodeFailureCodes) {
175
185
  payload: {
176
186
  flowId,
177
187
  progress: event.progress,
178
- scopeId: await opaqueId("scope", platformRunId, rawRunId)
188
+ scopeId: await identify("scope", platformRunId, rawRunId)
179
189
  }
180
190
  };
181
191
  }
@@ -183,7 +193,7 @@ function createEventProjector(platformRunId, nodeFailureCodes) {
183
193
  const rawRunId = runId(event);
184
194
  if (type == "run.failed" && rawRunId == platformRunId && event.failure != null) {
185
195
  const failure = object(event.failure, "Runtime terminal failure");
186
- const context = nodeContext(failure, flows, platformRunId);
196
+ const context = nodeContext(failure, flows, platformRunId, identify);
187
197
  return {
188
198
  kind: "node.failed",
189
199
  payload: {
@@ -201,13 +211,13 @@ function createEventProjector(platformRunId, nodeFailureCodes) {
201
211
  return;
202
212
  }
203
213
  if (type == "node.started" || type == "node.progress" || type == "node.artifact" || type == "node.log" || type == "node.completed" || type == "node.failed") {
204
- const context = nodeContext(event, flows, platformRunId);
214
+ const context = nodeContext(event, flows, platformRunId, identify);
205
215
  const executionId = await context.executionId;
206
216
  const payload = {
207
217
  executionId,
208
218
  flowId: context.flowId,
209
219
  nodeId: context.nodeId,
210
- scopeId: await opaqueId("scope", platformRunId, context.rawRunId),
220
+ scopeId: await identify("scope", platformRunId, context.rawRunId),
211
221
  ...event.systemOutputs === void 0 ? {} : { systemOutputs: object(event.systemOutputs, "Runtime system outputs") },
212
222
  ...event.handled === true ? { handled: true } : {}
213
223
  };
@@ -3,7 +3,10 @@ export { currentEngineContract, nodejsEngineContract, findEngineContract, type E
3
3
  /** Serialized into the guest; keep validation and helpers self-contained. */
4
4
  export declare function createState(invoke: (kind: string, payload: JsonValue) => Promise<RuntimeCapabilityResponse>): {
5
5
  state: Readonly<{
6
- get(key: unknown): Promise<JsonValue>;
6
+ get: {
7
+ (key: string): Promise<JsonValue>;
8
+ (keys: readonly string[]): Promise<JsonValue[]>;
9
+ };
7
10
  set(key: unknown, value: unknown): Promise<void>;
8
11
  insert(key: unknown, value: unknown): Promise<boolean>;
9
12
  delete(key: unknown): Promise<void>;