@vaudience/aigentflow-flow-validator 0.21.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/PARITY.md CHANGED
@@ -4,7 +4,33 @@ This document maps every rule in this JavaScript validator back to the AIgentFlo
4
4
  Go reference implementation, records the intentional divergences, and defines the
5
5
  discipline for keeping the two in sync.
6
6
 
7
- **Tracks AIgentFlow flow schema: `v2.827.0`** (`SPEC_VERSION` in [`src/spec/aigentflow-spec.json`](./src/spec/aigentflow-spec.json)), which adds the `here` executor scheme, the `strict_templates` flow key and the rule `template_default_piped` (package 0.21.0, below). Earlier: the warning `compliance_residency_unenforced` (package 0.20.0). `orchestrator.region` / `orchestrator.enforce_region` (package 0.19.0). Earlier: the AigentChat executor was removed (package 0.18.0). Earlier additions: the `examples:` block (package 0.17.0), `server_owned_query_key` (package 0.15.1) from the AIgentFlow release after v2.790.0, `credential_endpoint_unpaired` (package 0.15.2) from AIgentFlow v2.793.0 and the release after it, and `display_name_too_long` (package 0.15.3) from AIgentFlow v2.802.0.
7
+ **Tracks AIgentFlow flow schema: `v2.828.0`** (`SPEC_VERSION` in [`src/spec/aigentflow-spec.json`](./src/spec/aigentflow-spec.json)), which adds the rule `flow_data_key_reserved` (package 0.22.0, below). Earlier: the `here` executor scheme, the `strict_templates` flow key and the rule `template_default_piped` (package 0.21.0). Earlier: the warning `compliance_residency_unenforced` (package 0.20.0). `orchestrator.region` / `orchestrator.enforce_region` (package 0.19.0). Earlier: the AigentChat executor was removed (package 0.18.0). Earlier additions: the `examples:` block (package 0.17.0), `server_owned_query_key` (package 0.15.1) from the AIgentFlow release after v2.790.0, `credential_endpoint_unpaired` (package 0.15.2) from AIgentFlow v2.793.0 and the release after it, and `display_name_too_long` (package 0.15.3) from AIgentFlow v2.802.0.
8
+
9
+ > package 0.22.0 — **`flow_data_key_reserved`**, in step with the reference (AIgentFlow
10
+ > v2.828.0; `specVersion` 2.828.0). One new **error**: a key of the top-level `data:` map
11
+ > may not name engine state. `data:` is seeded into the mission's `.data` namespace, which
12
+ > is also where the engine keeps its own bookkeeping, so a flow declaring such a key would
13
+ > pre-seed or forge it (a taint list, the current flow, the mission's organization).
14
+ >
15
+ > | Rule (reference) | Code | Severity | Module |
16
+ > | -------------------------------------------------------------- | ------------------------ | -------- | --------------------------------------------------------- |
17
+ > | `FlowDataKeyReservation`, `validateFlowDataKeysAreNotReserved` | `flow_data_key_reserved` | error | `validators/flowDataKeys.ts`, spec `flowDataReservedKeys` |
18
+ >
19
+ > - **Reserved** (exact, case-sensitive; spec `flowDataReservedKeys`): `memory`,
20
+ > `orchestrator`, `campaign`, `voiceagent`, `org_id`, `current_flow`, `modified_flow`,
21
+ > `modification_history`, `modification_pending`, `modification_requests`,
22
+ > `bottleneck_state`; or a key starting with `_`, `.`, `memory.`, `orchestrator.`,
23
+ > `campaign.` or `voiceagent.`. Each carries the reference's reason. A key that merely
24
+ > contains a reserved word (`memory_notes`, `orgid`, `my_memory`) or differs in case is legal.
25
+ > - **Finding.** Field `data.<key>`, one per key, sorted by key; the message is the
26
+ > reference's `data: key "<key>" is reserved: <reason>. It would write into the engine's
27
+ own state, so it is not seeded.` and the suggestion its text (spec keys `messageFormat`,
28
+ > `suggestion`). There is no loop-body twin, so no demotion.
29
+ > - **Divergence from the reference's run door.** The reference tolerates this code when a
30
+ > stored flow is _run_ (the key is skipped, the flow still runs) and refuses it at save.
31
+ > This validator answers "would this save", so it is an error here, as for every other
32
+ > save-door rule. Key order is JavaScript's UTF-16 code-unit order, Go's is byte order;
33
+ > they differ only for keys with characters outside the Basic Multilingual Plane.
8
34
 
9
35
  > package 0.21.0 — **`here://`, `strict_templates` and `template_default_piped`**, in step
10
36
  > with the reference (AIgentFlow v2.827.0; `specVersion` 2.827.0). Three changes.
@@ -1084,6 +1110,7 @@ Comparison contract: **error `code` + `valid` verdict**, not message wording. Th
1084
1110
  | `tool_discovery` vocabulary, mock scenario `delay`, empty `output:` entry | `validateToolDiscoveryVocabulary`, `validateMockScenarioDelays`, `validateOutputParameters` (parser.go) | `validators/saveDoor.ts` | `validate.test.ts`, `conformance.test.ts` |
1085
1111
  | `executor_config` `${NAME}` references outside the key's scope (`executor_config_env_scope`) | `ValidateExecutorConfigEnvScopes` (parser.go) | `validators/executorConfigEnv.ts` | `v0150-save-door.test.ts`, `conformance.test.ts` |
1086
1112
  | A step or loop sub-step `query:` declaring a server-owned key (`server_owned_query_key`) | `validateNoServerOwnedStepQueryKeys` (validation.go), `IsServerOwnedParamKey` | `validators/serverOwnedQueryKeys.ts` | `v0151-save-door.test.ts`, `conformance.test.ts` |
1113
+ | A top-level `data:` key naming engine state (`flow_data_key_reserved`) | `FlowDataKeyReservation`, `validateFlowDataKeysAreNotReserved` (validation.flowdata.go) | `validators/flowDataKeys.ts` | `v0220-flow-data-keys.test.ts`, `conformance.test.ts` |
1087
1114
  | An endpoint a server-supplied credential will never be sent to (`credential_endpoint_unpaired`, warning) | `validateCredentialEndpointPairing`, `validateFamilyCredentialEndpointPairing` (validation.go) | `validators/credentialEndpoint.ts`, `validators/goUrl.ts` | `v0152-credential-endpoint.test.ts`, `conformance.test.ts` |
1088
1115
  | The `examples:` block: 58 codes, one walker (ids, inputs, references, expectations, checkpoints, variants) | the examples walker (`ValidateExamples`) | `validators/examples.ts`, `validators/goUrl.ts` | `examples.test.ts`, `conformance.test.ts` |
1089
1116
  | Orchestrator `.exons` frontmatter: spec, provider, `requirements.resources`, `tools.allow` withholds | `validateOrchestrator` (parser.go), `validateOrchestratorToolAllowWithholds` | `validators/exons.ts` | `v0150-save-door.test.ts`, `conformance.test.ts` |
package/README.md CHANGED
@@ -116,7 +116,7 @@ Low-level: parse YAML into `{ flow?, parseErrors, parseWarnings }` without valid
116
116
 
117
117
  ### `SPEC_VERSION: string`
118
118
 
119
- The AIgentFlow flow-schema version this build tracks (e.g. `"2.827.0"`).
119
+ The AIgentFlow flow-schema version this build tracks (e.g. `"2.828.0"`).
120
120
 
121
121
  ### Options
122
122
 
@@ -174,6 +174,7 @@ interface ValidationIssue {
174
174
  - **Orchestrator `.exons` frontmatter** — the document must open with a closed `---` frontmatter spec (`orchestrator_exons_parse_failed`) that sets `execution.provider` (`orchestrator_exons_no_provider`) and declares no `requirements.resources` (`exons_resources_unhonoured`); a tool that `orchestrator.tools` and the definition's `tools.allow` disagree about is never offered (`orchestrator_tool_withheld`, warning). The document body is not parsed: that needs the `.exons` engine (see PARITY.md).
175
175
  - **`executor_config` environment references** — a `${NAME}` value may name only the variables of the provider or protocol it is written under (`executor_config_env_scope`).
176
176
  - **Server-owned query keys** — a step's or loop sub-step's `query:` may not declare `aiv_api_key`, `aiv_base_url` or `aiv_delegation`, which only the server sets (`server_owned_query_key`).
177
+ - **Reserved `data:` keys** — a key of the top-level `data:` map may not name engine state: `memory`, `orchestrator`, `campaign`, `voiceagent`, `org_id`, `current_flow`, the `modif*`/`bottleneck_state` bookkeeping names, a key starting with `_` or `.`, or one under `memory.`, `orchestrator.`, `campaign.`, `voiceagent.` (`flow_data_key_reserved`).
177
178
  - **Credential endpoints** — an endpoint a server-supplied credential will never be sent to (warning, `credential_endpoint_unpaired`): an `ai://` `<provider>_base_url` or a literal `executor_config.<provider>.base_url` with no key of the flow's own, a family secret that is a server `${AIGENTFLOW_…}` variable beside a literal non-default endpoint.
178
179
  - **Credential bindings** — `stored/{provider}/{name}` format, `inject_as`, `credential`/`credentials` mutual exclusion.
179
180
  - **Expression functions** — exactly one of `package`/`function`.
package/dist/cli.js CHANGED
@@ -455,7 +455,7 @@ function validateBasicStructure(flow, issues) {
455
455
  // src/spec/aigentflow-spec.json
456
456
  var aigentflow_spec_default = {
457
457
  $comment: "Vendored enum surface mirrored from the AIgentFlow Go reference implementation. Regenerate / review on every flow-YAML schema change. See PARITY.md. Values here are public protocol/type names only.",
458
- specVersion: "2.827.0",
458
+ specVersion: "2.828.0",
459
459
  inputSchemaVersion: 1,
460
460
  executorSchemes: [
461
461
  "a2a",
@@ -965,6 +965,34 @@ var aigentflow_spec_default = {
965
965
  stepFieldFormat: "steps.%s.query.%s",
966
966
  loopStepFieldFormat: "steps.%s.loop.steps.%s.query.%s"
967
967
  },
968
+ flowDataReservedKeys: {
969
+ $comment: "flow_data_key_reserved (ERROR, no loop demotion): a key of the top-level data: map that names engine state. data: is seeded into the mission's .data namespace, where the engine keeps its own bookkeeping, so a flow declaring such a key would forge it. Matching is exact and case-sensitive. A key is reserved when it is one of exact (each with its reason), or starts with one of prefixes (checked in order, first wins; each with its reason). The finding's field is fieldFormat with the key; the message is messageFormat with the key (JSON-quoted, as Go's %q) then the reason; the suggestion is verbatim. Findings are sorted by key. Reference: FlowDataKeyReservation, validateFlowDataKeysAreNotReserved (aigentflow#247 review).",
970
+ code: "flow_data_key_reserved",
971
+ fieldFormat: "data.%s",
972
+ messageFormat: "data: key %s is reserved: %s. It would write into the engine's own state, so it is not seeded.",
973
+ suggestion: "Rename the key (no leading `_` or `.`, and not one of the engine namespaces memory., orchestrator., campaign., voiceagent.).",
974
+ exact: {
975
+ org_id: "the mission's organization, set by the engine for Thalamus/Engram calls",
976
+ memory: "the memory scope namespace",
977
+ orchestrator: "the orchestrator's state namespace",
978
+ campaign: "the campaign's state namespace",
979
+ voiceagent: "the voice agent's state namespace",
980
+ current_flow: "engine bookkeeping (the current flow definition)",
981
+ modified_flow: "engine bookkeeping (dynamic flow modification)",
982
+ modification_history: "engine bookkeeping (dynamic flow modification)",
983
+ modification_pending: "engine bookkeeping (dynamic flow modification)",
984
+ modification_requests: "engine bookkeeping (dynamic flow modification)",
985
+ bottleneck_state: "engine bookkeeping (bottleneck analysis)"
986
+ },
987
+ prefixes: [
988
+ { prefix: "_", reason: "keys starting with `_` are engine bookkeeping (current flow, loop and for_each context, taint lists such as `__untrusted_*`)" },
989
+ { prefix: ".", reason: "keys starting with `.` are engine step-response slots" },
990
+ { prefix: "memory.", reason: "the `memory.` namespace belongs to the engine" },
991
+ { prefix: "orchestrator.", reason: "the `orchestrator.` namespace belongs to the orchestrator" },
992
+ { prefix: "campaign.", reason: "the `campaign.` namespace belongs to the campaign engine" },
993
+ { prefix: "voiceagent.", reason: "the `voiceagent.` namespace belongs to the voice agent" }
994
+ ]
995
+ },
968
996
  credentialEndpointPairing: {
969
997
  $comment: "credential_endpoint_unpaired (WARNING, never an error): the one statically certain shape of AIgentFlow's run-time credential/endpoint pairing refusal. ai: an ai://<provider>/... step (top-level or loop sub-step) whose query holds a non-empty string <provider>+providerBaseUrlParamSuffix while neither its query (genericKeyParam or <provider>+providerKeyParamSuffix, non-empty strings) nor executor_config.<provider>.api_key (a literal, not an env reference) supplies a key; and executor_config.<provider>.base_url for a provider in executorConfigProviders when it is a literal, the block has no literal api_key, and it is not the case that at least one step uses the provider and every such step brings its own query key. keylessProviders are exempt from both. families: a step whose executor resolves (the key protocol+familyKeySeparator+driver, else protocol) to a row. implicitServerCredential rows are never judged. A row warns when its secret (the first non-empty string secretParam in the query, else per configKey in order: executor_config api_key when configApiKeyParam is a secretParam, else the first non-empty string secretParam under extra) is exactly an env reference to a serverEnv name on a row with expandsServerEnvReferences, and its endpoint (the first non-empty string endpointParam in the query, else per configKey: base_url when configBaseUrlParam is an endpointParam, else the first non-empty string endpointParam under extra) is a literal: no templateActionOpen, not an env reference, and not of the same origin as a defaultEndpoint (lower-cased scheme://host:port, defaultPorts filled). serverEnv is the reference's per-family server-variable set, evaluated. Field formats are the reference's. Reference: validateCredentialEndpointPairing (DC-FORGE-231) and validateFamilyCredentialEndpointPairing (DC-FORGE-233).",
970
998
  stepFieldFormat: "steps.%s.query.%s",
@@ -1979,6 +2007,14 @@ var SERVER_OWNED_QUERY_KEYS = {
1979
2007
  stepFieldFormat: aigentflow_spec_default.serverOwnedQueryKeys.stepFieldFormat,
1980
2008
  loopStepFieldFormat: aigentflow_spec_default.serverOwnedQueryKeys.loopStepFieldFormat
1981
2009
  };
2010
+ var FLOW_DATA_RESERVED_KEYS = {
2011
+ code: aigentflow_spec_default.flowDataReservedKeys.code,
2012
+ fieldFormat: aigentflow_spec_default.flowDataReservedKeys.fieldFormat,
2013
+ messageFormat: aigentflow_spec_default.flowDataReservedKeys.messageFormat,
2014
+ suggestion: aigentflow_spec_default.flowDataReservedKeys.suggestion,
2015
+ exact: aigentflow_spec_default.flowDataReservedKeys.exact,
2016
+ prefixes: aigentflow_spec_default.flowDataReservedKeys.prefixes
2017
+ };
1982
2018
  var CREDENTIAL_ENDPOINT_PAIRING = {
1983
2019
  stepFieldFormat: aigentflow_spec_default.credentialEndpointPairing.stepFieldFormat,
1984
2020
  loopStepFieldFormat: aigentflow_spec_default.credentialEndpointPairing.loopStepFieldFormat,
@@ -3901,16 +3937,16 @@ function skipSpaces(src, idx) {
3901
3937
  }
3902
3938
  return i;
3903
3939
  }
3904
- function scanString(src, start, quote) {
3940
+ function scanString(src, start, quote2) {
3905
3941
  let i = start + 1;
3906
- const raw = quote === "`";
3942
+ const raw = quote2 === "`";
3907
3943
  while (i < src.length) {
3908
3944
  const c = src[i];
3909
3945
  if (!raw && c === "\\") {
3910
3946
  i += 2;
3911
3947
  continue;
3912
3948
  }
3913
- if (c === quote) return i;
3949
+ if (c === quote2) return i;
3914
3950
  i += 1;
3915
3951
  }
3916
3952
  return -1;
@@ -4513,6 +4549,35 @@ function validateRetiredKeys(flow, issues) {
4513
4549
  }
4514
4550
  }
4515
4551
 
4552
+ // src/validators/flowDataKeys.ts
4553
+ var KEY_DATA = "data";
4554
+ function quote(key) {
4555
+ return JSON.stringify(key);
4556
+ }
4557
+ function flowDataKeyReservation(key) {
4558
+ if (Object.prototype.hasOwnProperty.call(FLOW_DATA_RESERVED_KEYS.exact, key)) {
4559
+ return FLOW_DATA_RESERVED_KEYS.exact[key];
4560
+ }
4561
+ for (const p of FLOW_DATA_RESERVED_KEYS.prefixes) {
4562
+ if (key.startsWith(p.prefix)) return p.reason;
4563
+ }
4564
+ return void 0;
4565
+ }
4566
+ function validateFlowDataKeys(flow, issues) {
4567
+ const data = flow[KEY_DATA];
4568
+ if (!isRecord(data)) return;
4569
+ for (const key of Object.keys(data).sort()) {
4570
+ const reason = flowDataKeyReservation(key);
4571
+ if (reason === void 0) continue;
4572
+ issues.error({
4573
+ field: FLOW_DATA_RESERVED_KEYS.fieldFormat.replace("%s", () => key),
4574
+ message: FLOW_DATA_RESERVED_KEYS.messageFormat.replace("%s", () => quote(key)).replace("%s", () => reason),
4575
+ code: FLOW_DATA_RESERVED_KEYS.code,
4576
+ suggestion: FLOW_DATA_RESERVED_KEYS.suggestion
4577
+ });
4578
+ }
4579
+ }
4580
+
4516
4581
  // src/validators/saveDoor.ts
4517
4582
  var TOOL_DISCOVERY_MODES = /* @__PURE__ */ new Set(["eager", "lazy", "off"]);
4518
4583
  var TEMPLATE_ACTION_OPEN2 = "{{";
@@ -6977,6 +7042,7 @@ function validateParsed(flow, opts, sources) {
6977
7042
  validateSaveDoorExtras(f, issues);
6978
7043
  validateExecutorConfigEnvScopes(f, issues);
6979
7044
  validateServerOwnedQueryKeys(f, issues);
7045
+ validateFlowDataKeys(f, issues);
6980
7046
  validateCredentialEndpointPairing(f, issues);
6981
7047
  validateExamples(f, issues);
6982
7048
  validateOrchestratorExons(f, issues);
package/dist/index.cjs CHANGED
@@ -451,7 +451,7 @@ function validateBasicStructure(flow, issues) {
451
451
 
452
452
  // src/spec/aigentflow-spec.json
453
453
  var aigentflow_spec_default = {
454
- specVersion: "2.827.0",
454
+ specVersion: "2.828.0",
455
455
  inputSchemaVersion: 1,
456
456
  executorSchemes: [
457
457
  "a2a",
@@ -948,6 +948,33 @@ var aigentflow_spec_default = {
948
948
  stepFieldFormat: "steps.%s.query.%s",
949
949
  loopStepFieldFormat: "steps.%s.loop.steps.%s.query.%s"
950
950
  },
951
+ flowDataReservedKeys: {
952
+ code: "flow_data_key_reserved",
953
+ fieldFormat: "data.%s",
954
+ messageFormat: "data: key %s is reserved: %s. It would write into the engine's own state, so it is not seeded.",
955
+ suggestion: "Rename the key (no leading `_` or `.`, and not one of the engine namespaces memory., orchestrator., campaign., voiceagent.).",
956
+ exact: {
957
+ org_id: "the mission's organization, set by the engine for Thalamus/Engram calls",
958
+ memory: "the memory scope namespace",
959
+ orchestrator: "the orchestrator's state namespace",
960
+ campaign: "the campaign's state namespace",
961
+ voiceagent: "the voice agent's state namespace",
962
+ current_flow: "engine bookkeeping (the current flow definition)",
963
+ modified_flow: "engine bookkeeping (dynamic flow modification)",
964
+ modification_history: "engine bookkeeping (dynamic flow modification)",
965
+ modification_pending: "engine bookkeeping (dynamic flow modification)",
966
+ modification_requests: "engine bookkeeping (dynamic flow modification)",
967
+ bottleneck_state: "engine bookkeeping (bottleneck analysis)"
968
+ },
969
+ prefixes: [
970
+ { prefix: "_", reason: "keys starting with `_` are engine bookkeeping (current flow, loop and for_each context, taint lists such as `__untrusted_*`)" },
971
+ { prefix: ".", reason: "keys starting with `.` are engine step-response slots" },
972
+ { prefix: "memory.", reason: "the `memory.` namespace belongs to the engine" },
973
+ { prefix: "orchestrator.", reason: "the `orchestrator.` namespace belongs to the orchestrator" },
974
+ { prefix: "campaign.", reason: "the `campaign.` namespace belongs to the campaign engine" },
975
+ { prefix: "voiceagent.", reason: "the `voiceagent.` namespace belongs to the voice agent" }
976
+ ]
977
+ },
951
978
  credentialEndpointPairing: {
952
979
  stepFieldFormat: "steps.%s.query.%s",
953
980
  loopStepFieldFormat: "steps.%s.loop.steps.%s.query.%s",
@@ -1885,6 +1912,14 @@ var SERVER_OWNED_QUERY_KEYS = {
1885
1912
  stepFieldFormat: aigentflow_spec_default.serverOwnedQueryKeys.stepFieldFormat,
1886
1913
  loopStepFieldFormat: aigentflow_spec_default.serverOwnedQueryKeys.loopStepFieldFormat
1887
1914
  };
1915
+ var FLOW_DATA_RESERVED_KEYS = {
1916
+ code: aigentflow_spec_default.flowDataReservedKeys.code,
1917
+ fieldFormat: aigentflow_spec_default.flowDataReservedKeys.fieldFormat,
1918
+ messageFormat: aigentflow_spec_default.flowDataReservedKeys.messageFormat,
1919
+ suggestion: aigentflow_spec_default.flowDataReservedKeys.suggestion,
1920
+ exact: aigentflow_spec_default.flowDataReservedKeys.exact,
1921
+ prefixes: aigentflow_spec_default.flowDataReservedKeys.prefixes
1922
+ };
1888
1923
  var CREDENTIAL_ENDPOINT_PAIRING = {
1889
1924
  stepFieldFormat: aigentflow_spec_default.credentialEndpointPairing.stepFieldFormat,
1890
1925
  loopStepFieldFormat: aigentflow_spec_default.credentialEndpointPairing.loopStepFieldFormat,
@@ -3804,16 +3839,16 @@ function skipSpaces(src, idx) {
3804
3839
  }
3805
3840
  return i;
3806
3841
  }
3807
- function scanString(src, start, quote) {
3842
+ function scanString(src, start, quote2) {
3808
3843
  let i = start + 1;
3809
- const raw = quote === "`";
3844
+ const raw = quote2 === "`";
3810
3845
  while (i < src.length) {
3811
3846
  const c = src[i];
3812
3847
  if (!raw && c === "\\") {
3813
3848
  i += 2;
3814
3849
  continue;
3815
3850
  }
3816
- if (c === quote) return i;
3851
+ if (c === quote2) return i;
3817
3852
  i += 1;
3818
3853
  }
3819
3854
  return -1;
@@ -4416,6 +4451,35 @@ function validateRetiredKeys(flow, issues) {
4416
4451
  }
4417
4452
  }
4418
4453
 
4454
+ // src/validators/flowDataKeys.ts
4455
+ var KEY_DATA = "data";
4456
+ function quote(key) {
4457
+ return JSON.stringify(key);
4458
+ }
4459
+ function flowDataKeyReservation(key) {
4460
+ if (Object.prototype.hasOwnProperty.call(FLOW_DATA_RESERVED_KEYS.exact, key)) {
4461
+ return FLOW_DATA_RESERVED_KEYS.exact[key];
4462
+ }
4463
+ for (const p of FLOW_DATA_RESERVED_KEYS.prefixes) {
4464
+ if (key.startsWith(p.prefix)) return p.reason;
4465
+ }
4466
+ return void 0;
4467
+ }
4468
+ function validateFlowDataKeys(flow, issues) {
4469
+ const data = flow[KEY_DATA];
4470
+ if (!isRecord(data)) return;
4471
+ for (const key of Object.keys(data).sort()) {
4472
+ const reason = flowDataKeyReservation(key);
4473
+ if (reason === void 0) continue;
4474
+ issues.error({
4475
+ field: FLOW_DATA_RESERVED_KEYS.fieldFormat.replace("%s", () => key),
4476
+ message: FLOW_DATA_RESERVED_KEYS.messageFormat.replace("%s", () => quote(key)).replace("%s", () => reason),
4477
+ code: FLOW_DATA_RESERVED_KEYS.code,
4478
+ suggestion: FLOW_DATA_RESERVED_KEYS.suggestion
4479
+ });
4480
+ }
4481
+ }
4482
+
4419
4483
  // src/validators/saveDoor.ts
4420
4484
  var TOOL_DISCOVERY_MODES = /* @__PURE__ */ new Set(["eager", "lazy", "off"]);
4421
4485
  var TEMPLATE_ACTION_OPEN2 = "{{";
@@ -6878,6 +6942,7 @@ function validateParsed(flow, opts, sources) {
6878
6942
  validateSaveDoorExtras(f, issues);
6879
6943
  validateExecutorConfigEnvScopes(f, issues);
6880
6944
  validateServerOwnedQueryKeys(f, issues);
6945
+ validateFlowDataKeys(f, issues);
6881
6946
  validateCredentialEndpointPairing(f, issues);
6882
6947
  validateExamples(f, issues);
6883
6948
  validateOrchestratorExons(f, issues);