@vaudience/aigentflow-flow-validator 0.21.0 → 0.23.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 +58 -1
- package/README.md +2 -1
- package/dist/cli.js +172 -6
- package/dist/index.cjs +170 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +170 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/PARITY.md
CHANGED
|
@@ -4,7 +4,57 @@ 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.
|
|
7
|
+
**Tracks AIgentFlow flow schema: `v2.831.0`** (`SPEC_VERSION` in [`src/spec/aigentflow-spec.json`](./src/spec/aigentflow-spec.json)), which adds the rule `unknown_function_operation` and a clearer `unknown_executor_scheme` message (package 0.23.0, below). Earlier: the rule `flow_data_key_reserved` (package 0.22.0). 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.23.0 — **`unknown_function_operation`**, in step with the reference (AIgentFlow
|
|
10
|
+
> v2.831.0; `specVersion` 2.831.0). One new **error**: a step or loop sub-step whose executor
|
|
11
|
+
> is `function://<a>/<b>` where `<a>/<b>` is not one of the 32 registered built-ins. Before
|
|
12
|
+
> this, `function://utils/noop` validated and then failed every run with "function
|
|
13
|
+
> 'utils/noop' not found".
|
|
14
|
+
>
|
|
15
|
+
> | Rule (reference) | Code | Severity | Module |
|
|
16
|
+
> | -------------------------------------------- | ---------------------------- | -------- | ---------------------------------------------------- |
|
|
17
|
+
> | `validateFunctionOperationKnown` (parser.go) | `unknown_function_operation` | error | `validators/executors.ts`, spec `functionOperations` |
|
|
18
|
+
>
|
|
19
|
+
> - **Operation** = authority + `/` + path of the executor URL (so `binary-processor/validate-and-store`).
|
|
20
|
+
> The set is closed and sorted in spec `functionOperations.operations`. Templated executors
|
|
21
|
+
> (containing `{{`), unparsable URLs (`invalid_executor_url` owns them) and other schemes are
|
|
22
|
+
> skipped; the scheme match is exact and case-sensitive.
|
|
23
|
+
> - **Finding.** Field `steps.<id>.executor` (loop sub-step: `steps.<id>.loop.steps[i].executor`,
|
|
24
|
+
> step id `<id>.<sub>`); message `step "<id>" uses function://<op>, which is not a built-in
|
|
25
|
+
function. Available: <sorted list, ", ">`; the suggestion carries the list.
|
|
26
|
+
> - **Divergence 23** (below): the reference tolerates this code on its load door.
|
|
27
|
+
> - **`unknown_executor_scheme` text (still a warning).** For `decide` the message appends
|
|
28
|
+
> `Decision models run through ai://{provider}/decide (for example ai://openai/decide), not
|
|
29
|
+
through a scheme of their own.`; for any other unknown scheme with a known scheme within
|
|
30
|
+
> edit distance 2 (case-insensitive; the closest, ties to the alphabetically first) it
|
|
31
|
+
> appends `Did you mean '<scheme>'?`. No verdict changes.
|
|
32
|
+
|
|
33
|
+
> package 0.22.0 — **`flow_data_key_reserved`**, in step with the reference (AIgentFlow
|
|
34
|
+
> v2.828.0; `specVersion` 2.828.0). One new **error**: a key of the top-level `data:` map
|
|
35
|
+
> may not name engine state. `data:` is seeded into the mission's `.data` namespace, which
|
|
36
|
+
> is also where the engine keeps its own bookkeeping, so a flow declaring such a key would
|
|
37
|
+
> pre-seed or forge it (a taint list, the current flow, the mission's organization).
|
|
38
|
+
>
|
|
39
|
+
> | Rule (reference) | Code | Severity | Module |
|
|
40
|
+
> | -------------------------------------------------------------- | ------------------------ | -------- | --------------------------------------------------------- |
|
|
41
|
+
> | `FlowDataKeyReservation`, `validateFlowDataKeysAreNotReserved` | `flow_data_key_reserved` | error | `validators/flowDataKeys.ts`, spec `flowDataReservedKeys` |
|
|
42
|
+
>
|
|
43
|
+
> - **Reserved** (exact, case-sensitive; spec `flowDataReservedKeys`): `memory`,
|
|
44
|
+
> `orchestrator`, `campaign`, `voiceagent`, `org_id`, `current_flow`, `modified_flow`,
|
|
45
|
+
> `modification_history`, `modification_pending`, `modification_requests`,
|
|
46
|
+
> `bottleneck_state`; or a key starting with `_`, `.`, `memory.`, `orchestrator.`,
|
|
47
|
+
> `campaign.` or `voiceagent.`. Each carries the reference's reason. A key that merely
|
|
48
|
+
> contains a reserved word (`memory_notes`, `orgid`, `my_memory`) or differs in case is legal.
|
|
49
|
+
> - **Finding.** Field `data.<key>`, one per key, sorted by key; the message is the
|
|
50
|
+
> reference's `data: key "<key>" is reserved: <reason>. It would write into the engine's
|
|
51
|
+
own state, so it is not seeded.` and the suggestion its text (spec keys `messageFormat`,
|
|
52
|
+
> `suggestion`). There is no loop-body twin, so no demotion.
|
|
53
|
+
> - **Divergence from the reference's run door.** The reference tolerates this code when a
|
|
54
|
+
> stored flow is _run_ (the key is skipped, the flow still runs) and refuses it at save.
|
|
55
|
+
> This validator answers "would this save", so it is an error here, as for every other
|
|
56
|
+
> save-door rule. Key order is JavaScript's UTF-16 code-unit order, Go's is byte order;
|
|
57
|
+
> they differ only for keys with characters outside the Basic Multilingual Plane.
|
|
8
58
|
|
|
9
59
|
> package 0.21.0 — **`here://`, `strict_templates` and `template_default_piped`**, in step
|
|
10
60
|
> with the reference (AIgentFlow v2.827.0; `specVersion` 2.827.0). Three changes.
|
|
@@ -1084,6 +1134,8 @@ Comparison contract: **error `code` + `valid` verdict**, not message wording. Th
|
|
|
1084
1134
|
| `tool_discovery` vocabulary, mock scenario `delay`, empty `output:` entry | `validateToolDiscoveryVocabulary`, `validateMockScenarioDelays`, `validateOutputParameters` (parser.go) | `validators/saveDoor.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1085
1135
|
| `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
1136
|
| 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` |
|
|
1137
|
+
| A `function://` operation outside the built-in set (`unknown_function_operation`) | `validateFunctionOperationKnown` (parser.go) | `validators/executors.ts` | `v0230-function-operation.test.ts`, `conformance.test.ts` |
|
|
1138
|
+
| 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
1139
|
| 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
1140
|
| 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
1141
|
| 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` |
|
|
@@ -1353,6 +1405,11 @@ number their shared divergences alike._
|
|
|
1353
1405
|
- The recursion bound of 32 on a nested value is the reference's own and is not in the
|
|
1354
1406
|
spec.
|
|
1355
1407
|
|
|
1408
|
+
23. **`unknown_function_operation` is tolerated on the reference's load door (0.23.0).** The
|
|
1409
|
+
reference refuses an unknown `function://` operation when a flow is _saved_ but loads
|
|
1410
|
+
(and then fails at run time) a stored flow that already carries one. This validator
|
|
1411
|
+
answers "would this save", so it is an error here, as for every other save-door rule.
|
|
1412
|
+
|
|
1356
1413
|
---
|
|
1357
1414
|
|
|
1358
1415
|
## The examples block
|
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.
|
|
119
|
+
The AIgentFlow flow-schema version this build tracks (e.g. `"2.831.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
|
@@ -438,7 +438,7 @@ function validateBasicStructure(flow, issues) {
|
|
|
438
438
|
message: "Executor is required for each step",
|
|
439
439
|
code: "missing_required_field",
|
|
440
440
|
stepId: stepID,
|
|
441
|
-
suggestion: "Specify an executor URL (e.g., 'function://
|
|
441
|
+
suggestion: "Specify an executor URL (e.g., 'function://utils/log')"
|
|
442
442
|
});
|
|
443
443
|
} else if (executorText === null) {
|
|
444
444
|
issues.error({
|
|
@@ -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.
|
|
458
|
+
specVersion: "2.831.0",
|
|
459
459
|
inputSchemaVersion: 1,
|
|
460
460
|
executorSchemes: [
|
|
461
461
|
"a2a",
|
|
@@ -965,6 +965,75 @@ var aigentflow_spec_default = {
|
|
|
965
965
|
stepFieldFormat: "steps.%s.query.%s",
|
|
966
966
|
loopStepFieldFormat: "steps.%s.loop.steps.%s.query.%s"
|
|
967
967
|
},
|
|
968
|
+
functionOperations: {
|
|
969
|
+
$comment: `unknown_function_operation (ERROR): a step or loop sub-step whose executor is function://<authority>/<path> where authority+"/"+path is not in operations. Templated executors (containing templateActionOpen), unparsable URLs (invalid_executor_url owns them) and other schemes are skipped; the scheme match is exact and case-sensitive, as in the reference. The message is messageFormat with the stepId, the operation, then operations joined by ', '. Reference: validateFunctionOperationKnown (parser.go), functionSchemaOperations.`,
|
|
970
|
+
code: "unknown_function_operation",
|
|
971
|
+
scheme: "function",
|
|
972
|
+
messageFormat: "step %s uses function://%s, which is not a built-in function. Available: %s",
|
|
973
|
+
suggestionFormat: "Use one of the built-in function operations: %s. For custom logic use a script://, http:// or ai:// executor instead.",
|
|
974
|
+
operations: [
|
|
975
|
+
"binary-processor/validate-and-store",
|
|
976
|
+
"data/create",
|
|
977
|
+
"data/from-json",
|
|
978
|
+
"data/to-json",
|
|
979
|
+
"data/validate",
|
|
980
|
+
"demo/adult-processor",
|
|
981
|
+
"demo/calculator",
|
|
982
|
+
"demo/data-processor",
|
|
983
|
+
"demo/finalizer",
|
|
984
|
+
"demo/senior-processor",
|
|
985
|
+
"demo/youth-processor",
|
|
986
|
+
"document/finalize",
|
|
987
|
+
"document/prepare",
|
|
988
|
+
"json/validate",
|
|
989
|
+
"math/basic",
|
|
990
|
+
"math/calculate",
|
|
991
|
+
"math/sum",
|
|
992
|
+
"results-compiler/aggregate",
|
|
993
|
+
"system/info",
|
|
994
|
+
"text/analysis",
|
|
995
|
+
"text/count-characters",
|
|
996
|
+
"text/count-lines",
|
|
997
|
+
"text/count-occurrences",
|
|
998
|
+
"text/count-words",
|
|
999
|
+
"text/find-pattern",
|
|
1000
|
+
"text/generate",
|
|
1001
|
+
"text/replace-text",
|
|
1002
|
+
"text/search",
|
|
1003
|
+
"utils/datetime",
|
|
1004
|
+
"utils/format-date",
|
|
1005
|
+
"utils/log",
|
|
1006
|
+
"utils/parse-date"
|
|
1007
|
+
]
|
|
1008
|
+
},
|
|
1009
|
+
flowDataReservedKeys: {
|
|
1010
|
+
$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).",
|
|
1011
|
+
code: "flow_data_key_reserved",
|
|
1012
|
+
fieldFormat: "data.%s",
|
|
1013
|
+
messageFormat: "data: key %s is reserved: %s. It would write into the engine's own state, so it is not seeded.",
|
|
1014
|
+
suggestion: "Rename the key (no leading `_` or `.`, and not one of the engine namespaces memory., orchestrator., campaign., voiceagent.).",
|
|
1015
|
+
exact: {
|
|
1016
|
+
org_id: "the mission's organization, set by the engine for Thalamus/Engram calls",
|
|
1017
|
+
memory: "the memory scope namespace",
|
|
1018
|
+
orchestrator: "the orchestrator's state namespace",
|
|
1019
|
+
campaign: "the campaign's state namespace",
|
|
1020
|
+
voiceagent: "the voice agent's state namespace",
|
|
1021
|
+
current_flow: "engine bookkeeping (the current flow definition)",
|
|
1022
|
+
modified_flow: "engine bookkeeping (dynamic flow modification)",
|
|
1023
|
+
modification_history: "engine bookkeeping (dynamic flow modification)",
|
|
1024
|
+
modification_pending: "engine bookkeeping (dynamic flow modification)",
|
|
1025
|
+
modification_requests: "engine bookkeeping (dynamic flow modification)",
|
|
1026
|
+
bottleneck_state: "engine bookkeeping (bottleneck analysis)"
|
|
1027
|
+
},
|
|
1028
|
+
prefixes: [
|
|
1029
|
+
{ prefix: "_", reason: "keys starting with `_` are engine bookkeeping (current flow, loop and for_each context, taint lists such as `__untrusted_*`)" },
|
|
1030
|
+
{ prefix: ".", reason: "keys starting with `.` are engine step-response slots" },
|
|
1031
|
+
{ prefix: "memory.", reason: "the `memory.` namespace belongs to the engine" },
|
|
1032
|
+
{ prefix: "orchestrator.", reason: "the `orchestrator.` namespace belongs to the orchestrator" },
|
|
1033
|
+
{ prefix: "campaign.", reason: "the `campaign.` namespace belongs to the campaign engine" },
|
|
1034
|
+
{ prefix: "voiceagent.", reason: "the `voiceagent.` namespace belongs to the voice agent" }
|
|
1035
|
+
]
|
|
1036
|
+
},
|
|
968
1037
|
credentialEndpointPairing: {
|
|
969
1038
|
$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
1039
|
stepFieldFormat: "steps.%s.query.%s",
|
|
@@ -1979,6 +2048,14 @@ var SERVER_OWNED_QUERY_KEYS = {
|
|
|
1979
2048
|
stepFieldFormat: aigentflow_spec_default.serverOwnedQueryKeys.stepFieldFormat,
|
|
1980
2049
|
loopStepFieldFormat: aigentflow_spec_default.serverOwnedQueryKeys.loopStepFieldFormat
|
|
1981
2050
|
};
|
|
2051
|
+
var FLOW_DATA_RESERVED_KEYS = {
|
|
2052
|
+
code: aigentflow_spec_default.flowDataReservedKeys.code,
|
|
2053
|
+
fieldFormat: aigentflow_spec_default.flowDataReservedKeys.fieldFormat,
|
|
2054
|
+
messageFormat: aigentflow_spec_default.flowDataReservedKeys.messageFormat,
|
|
2055
|
+
suggestion: aigentflow_spec_default.flowDataReservedKeys.suggestion,
|
|
2056
|
+
exact: aigentflow_spec_default.flowDataReservedKeys.exact,
|
|
2057
|
+
prefixes: aigentflow_spec_default.flowDataReservedKeys.prefixes
|
|
2058
|
+
};
|
|
1982
2059
|
var CREDENTIAL_ENDPOINT_PAIRING = {
|
|
1983
2060
|
stepFieldFormat: aigentflow_spec_default.credentialEndpointPairing.stepFieldFormat,
|
|
1984
2061
|
loopStepFieldFormat: aigentflow_spec_default.credentialEndpointPairing.loopStepFieldFormat,
|
|
@@ -2044,9 +2121,51 @@ var TEMPLATE_DEFAULT_PIPED = {
|
|
|
2044
2121
|
messageFormat: aigentflow_spec_default.templateDefaultPiped.messageFormat,
|
|
2045
2122
|
suggestion: aigentflow_spec_default.templateDefaultPiped.suggestion
|
|
2046
2123
|
};
|
|
2124
|
+
var FUNCTION_OPERATIONS = {
|
|
2125
|
+
code: aigentflow_spec_default.functionOperations.code,
|
|
2126
|
+
scheme: aigentflow_spec_default.functionOperations.scheme,
|
|
2127
|
+
messageFormat: aigentflow_spec_default.functionOperations.messageFormat,
|
|
2128
|
+
suggestionFormat: aigentflow_spec_default.functionOperations.suggestionFormat,
|
|
2129
|
+
operations: new Set(aigentflow_spec_default.functionOperations.operations),
|
|
2130
|
+
sorted: aigentflow_spec_default.functionOperations.operations
|
|
2131
|
+
};
|
|
2047
2132
|
|
|
2048
2133
|
// src/validators/executors.ts
|
|
2049
2134
|
var SCHEME_SEPARATOR = "://";
|
|
2135
|
+
var UNKNOWN_SCHEME_BASE = (scheme) => `Unknown executor scheme '${scheme}'. It may be valid in a newer AIgentFlow release, or it could be a typo.`;
|
|
2136
|
+
var DECIDE_SCHEME = "decide";
|
|
2137
|
+
var DECIDE_HINT = "Decision models run through ai://{provider}/decide (for example ai://openai/decide), not through a scheme of their own.";
|
|
2138
|
+
var MAX_SCHEME_EDIT_DISTANCE = 2;
|
|
2139
|
+
function editDistance(a, b) {
|
|
2140
|
+
let prev = Array.from({ length: b.length + 1 }, (_, j) => j);
|
|
2141
|
+
for (let i = 1; i <= a.length; i++) {
|
|
2142
|
+
const cur = [i];
|
|
2143
|
+
for (let j = 1; j <= b.length; j++) {
|
|
2144
|
+
cur[j] = Math.min(
|
|
2145
|
+
prev[j] + 1,
|
|
2146
|
+
cur[j - 1] + 1,
|
|
2147
|
+
prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1)
|
|
2148
|
+
);
|
|
2149
|
+
}
|
|
2150
|
+
prev = cur;
|
|
2151
|
+
}
|
|
2152
|
+
return prev[b.length];
|
|
2153
|
+
}
|
|
2154
|
+
function unknownSchemeMessage(scheme) {
|
|
2155
|
+
const lower = scheme.toLowerCase();
|
|
2156
|
+
const base = UNKNOWN_SCHEME_BASE(scheme);
|
|
2157
|
+
if (lower === DECIDE_SCHEME) return `${base} ${DECIDE_HINT}`;
|
|
2158
|
+
let best;
|
|
2159
|
+
let bestDist = MAX_SCHEME_EDIT_DISTANCE + 1;
|
|
2160
|
+
for (const known of [...EXECUTOR_SCHEMES].sort()) {
|
|
2161
|
+
const d = editDistance(lower, known);
|
|
2162
|
+
if (d < bestDist) {
|
|
2163
|
+
best = known;
|
|
2164
|
+
bestDist = d;
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
return best === void 0 ? base : `${base} Did you mean '${best}'?`;
|
|
2168
|
+
}
|
|
2050
2169
|
function checkExecutor(executor, field, stepID, issues) {
|
|
2051
2170
|
const sepIdx = executor.indexOf(SCHEME_SEPARATOR);
|
|
2052
2171
|
const scheme = sepIdx > 0 ? executor.slice(0, sepIdx) : "";
|
|
@@ -2065,11 +2184,28 @@ function checkExecutor(executor, field, stepID, issues) {
|
|
|
2065
2184
|
if (!EXECUTOR_SCHEMES.has(scheme.toLowerCase())) {
|
|
2066
2185
|
issues.warn({
|
|
2067
2186
|
field,
|
|
2068
|
-
message:
|
|
2187
|
+
message: unknownSchemeMessage(scheme),
|
|
2069
2188
|
code: "unknown_executor_scheme",
|
|
2070
2189
|
stepId: stepID
|
|
2071
2190
|
});
|
|
2191
|
+
return;
|
|
2072
2192
|
}
|
|
2193
|
+
checkFunctionOperation(executor, field, stepID, templated, issues);
|
|
2194
|
+
}
|
|
2195
|
+
function checkFunctionOperation(executor, field, stepID, templated, issues) {
|
|
2196
|
+
if (templated) return;
|
|
2197
|
+
const m = EXECUTOR_URL_PATTERN.exec(executor);
|
|
2198
|
+
if (m === null || m[1] !== FUNCTION_OPERATIONS.scheme) return;
|
|
2199
|
+
const op = `${m[2]}/${m[3]}`;
|
|
2200
|
+
if (FUNCTION_OPERATIONS.operations.has(op)) return;
|
|
2201
|
+
const list = FUNCTION_OPERATIONS.sorted.join(", ");
|
|
2202
|
+
issues.error({
|
|
2203
|
+
field,
|
|
2204
|
+
message: FUNCTION_OPERATIONS.messageFormat.replace("%s", () => JSON.stringify(stepID)).replace("%s", () => op).replace("%s", () => list),
|
|
2205
|
+
code: FUNCTION_OPERATIONS.code,
|
|
2206
|
+
stepId: stepID,
|
|
2207
|
+
suggestion: FUNCTION_OPERATIONS.suggestionFormat.replace("%s", () => list)
|
|
2208
|
+
});
|
|
2073
2209
|
}
|
|
2074
2210
|
function validateExecutors(flow, issues) {
|
|
2075
2211
|
const steps = flow.steps;
|
|
@@ -3901,16 +4037,16 @@ function skipSpaces(src, idx) {
|
|
|
3901
4037
|
}
|
|
3902
4038
|
return i;
|
|
3903
4039
|
}
|
|
3904
|
-
function scanString(src, start,
|
|
4040
|
+
function scanString(src, start, quote2) {
|
|
3905
4041
|
let i = start + 1;
|
|
3906
|
-
const raw =
|
|
4042
|
+
const raw = quote2 === "`";
|
|
3907
4043
|
while (i < src.length) {
|
|
3908
4044
|
const c = src[i];
|
|
3909
4045
|
if (!raw && c === "\\") {
|
|
3910
4046
|
i += 2;
|
|
3911
4047
|
continue;
|
|
3912
4048
|
}
|
|
3913
|
-
if (c ===
|
|
4049
|
+
if (c === quote2) return i;
|
|
3914
4050
|
i += 1;
|
|
3915
4051
|
}
|
|
3916
4052
|
return -1;
|
|
@@ -4513,6 +4649,35 @@ function validateRetiredKeys(flow, issues) {
|
|
|
4513
4649
|
}
|
|
4514
4650
|
}
|
|
4515
4651
|
|
|
4652
|
+
// src/validators/flowDataKeys.ts
|
|
4653
|
+
var KEY_DATA = "data";
|
|
4654
|
+
function quote(key) {
|
|
4655
|
+
return JSON.stringify(key);
|
|
4656
|
+
}
|
|
4657
|
+
function flowDataKeyReservation(key) {
|
|
4658
|
+
if (Object.prototype.hasOwnProperty.call(FLOW_DATA_RESERVED_KEYS.exact, key)) {
|
|
4659
|
+
return FLOW_DATA_RESERVED_KEYS.exact[key];
|
|
4660
|
+
}
|
|
4661
|
+
for (const p of FLOW_DATA_RESERVED_KEYS.prefixes) {
|
|
4662
|
+
if (key.startsWith(p.prefix)) return p.reason;
|
|
4663
|
+
}
|
|
4664
|
+
return void 0;
|
|
4665
|
+
}
|
|
4666
|
+
function validateFlowDataKeys(flow, issues) {
|
|
4667
|
+
const data = flow[KEY_DATA];
|
|
4668
|
+
if (!isRecord(data)) return;
|
|
4669
|
+
for (const key of Object.keys(data).sort()) {
|
|
4670
|
+
const reason = flowDataKeyReservation(key);
|
|
4671
|
+
if (reason === void 0) continue;
|
|
4672
|
+
issues.error({
|
|
4673
|
+
field: FLOW_DATA_RESERVED_KEYS.fieldFormat.replace("%s", () => key),
|
|
4674
|
+
message: FLOW_DATA_RESERVED_KEYS.messageFormat.replace("%s", () => quote(key)).replace("%s", () => reason),
|
|
4675
|
+
code: FLOW_DATA_RESERVED_KEYS.code,
|
|
4676
|
+
suggestion: FLOW_DATA_RESERVED_KEYS.suggestion
|
|
4677
|
+
});
|
|
4678
|
+
}
|
|
4679
|
+
}
|
|
4680
|
+
|
|
4516
4681
|
// src/validators/saveDoor.ts
|
|
4517
4682
|
var TOOL_DISCOVERY_MODES = /* @__PURE__ */ new Set(["eager", "lazy", "off"]);
|
|
4518
4683
|
var TEMPLATE_ACTION_OPEN2 = "{{";
|
|
@@ -6977,6 +7142,7 @@ function validateParsed(flow, opts, sources) {
|
|
|
6977
7142
|
validateSaveDoorExtras(f, issues);
|
|
6978
7143
|
validateExecutorConfigEnvScopes(f, issues);
|
|
6979
7144
|
validateServerOwnedQueryKeys(f, issues);
|
|
7145
|
+
validateFlowDataKeys(f, issues);
|
|
6980
7146
|
validateCredentialEndpointPairing(f, issues);
|
|
6981
7147
|
validateExamples(f, issues);
|
|
6982
7148
|
validateOrchestratorExons(f, issues);
|
package/dist/index.cjs
CHANGED
|
@@ -435,7 +435,7 @@ function validateBasicStructure(flow, issues) {
|
|
|
435
435
|
message: "Executor is required for each step",
|
|
436
436
|
code: "missing_required_field",
|
|
437
437
|
stepId: stepID,
|
|
438
|
-
suggestion: "Specify an executor URL (e.g., 'function://
|
|
438
|
+
suggestion: "Specify an executor URL (e.g., 'function://utils/log')"
|
|
439
439
|
});
|
|
440
440
|
} else if (executorText === null) {
|
|
441
441
|
issues.error({
|
|
@@ -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.
|
|
454
|
+
specVersion: "2.831.0",
|
|
455
455
|
inputSchemaVersion: 1,
|
|
456
456
|
executorSchemes: [
|
|
457
457
|
"a2a",
|
|
@@ -948,6 +948,73 @@ var aigentflow_spec_default = {
|
|
|
948
948
|
stepFieldFormat: "steps.%s.query.%s",
|
|
949
949
|
loopStepFieldFormat: "steps.%s.loop.steps.%s.query.%s"
|
|
950
950
|
},
|
|
951
|
+
functionOperations: {
|
|
952
|
+
code: "unknown_function_operation",
|
|
953
|
+
scheme: "function",
|
|
954
|
+
messageFormat: "step %s uses function://%s, which is not a built-in function. Available: %s",
|
|
955
|
+
suggestionFormat: "Use one of the built-in function operations: %s. For custom logic use a script://, http:// or ai:// executor instead.",
|
|
956
|
+
operations: [
|
|
957
|
+
"binary-processor/validate-and-store",
|
|
958
|
+
"data/create",
|
|
959
|
+
"data/from-json",
|
|
960
|
+
"data/to-json",
|
|
961
|
+
"data/validate",
|
|
962
|
+
"demo/adult-processor",
|
|
963
|
+
"demo/calculator",
|
|
964
|
+
"demo/data-processor",
|
|
965
|
+
"demo/finalizer",
|
|
966
|
+
"demo/senior-processor",
|
|
967
|
+
"demo/youth-processor",
|
|
968
|
+
"document/finalize",
|
|
969
|
+
"document/prepare",
|
|
970
|
+
"json/validate",
|
|
971
|
+
"math/basic",
|
|
972
|
+
"math/calculate",
|
|
973
|
+
"math/sum",
|
|
974
|
+
"results-compiler/aggregate",
|
|
975
|
+
"system/info",
|
|
976
|
+
"text/analysis",
|
|
977
|
+
"text/count-characters",
|
|
978
|
+
"text/count-lines",
|
|
979
|
+
"text/count-occurrences",
|
|
980
|
+
"text/count-words",
|
|
981
|
+
"text/find-pattern",
|
|
982
|
+
"text/generate",
|
|
983
|
+
"text/replace-text",
|
|
984
|
+
"text/search",
|
|
985
|
+
"utils/datetime",
|
|
986
|
+
"utils/format-date",
|
|
987
|
+
"utils/log",
|
|
988
|
+
"utils/parse-date"
|
|
989
|
+
]
|
|
990
|
+
},
|
|
991
|
+
flowDataReservedKeys: {
|
|
992
|
+
code: "flow_data_key_reserved",
|
|
993
|
+
fieldFormat: "data.%s",
|
|
994
|
+
messageFormat: "data: key %s is reserved: %s. It would write into the engine's own state, so it is not seeded.",
|
|
995
|
+
suggestion: "Rename the key (no leading `_` or `.`, and not one of the engine namespaces memory., orchestrator., campaign., voiceagent.).",
|
|
996
|
+
exact: {
|
|
997
|
+
org_id: "the mission's organization, set by the engine for Thalamus/Engram calls",
|
|
998
|
+
memory: "the memory scope namespace",
|
|
999
|
+
orchestrator: "the orchestrator's state namespace",
|
|
1000
|
+
campaign: "the campaign's state namespace",
|
|
1001
|
+
voiceagent: "the voice agent's state namespace",
|
|
1002
|
+
current_flow: "engine bookkeeping (the current flow definition)",
|
|
1003
|
+
modified_flow: "engine bookkeeping (dynamic flow modification)",
|
|
1004
|
+
modification_history: "engine bookkeeping (dynamic flow modification)",
|
|
1005
|
+
modification_pending: "engine bookkeeping (dynamic flow modification)",
|
|
1006
|
+
modification_requests: "engine bookkeeping (dynamic flow modification)",
|
|
1007
|
+
bottleneck_state: "engine bookkeeping (bottleneck analysis)"
|
|
1008
|
+
},
|
|
1009
|
+
prefixes: [
|
|
1010
|
+
{ prefix: "_", reason: "keys starting with `_` are engine bookkeeping (current flow, loop and for_each context, taint lists such as `__untrusted_*`)" },
|
|
1011
|
+
{ prefix: ".", reason: "keys starting with `.` are engine step-response slots" },
|
|
1012
|
+
{ prefix: "memory.", reason: "the `memory.` namespace belongs to the engine" },
|
|
1013
|
+
{ prefix: "orchestrator.", reason: "the `orchestrator.` namespace belongs to the orchestrator" },
|
|
1014
|
+
{ prefix: "campaign.", reason: "the `campaign.` namespace belongs to the campaign engine" },
|
|
1015
|
+
{ prefix: "voiceagent.", reason: "the `voiceagent.` namespace belongs to the voice agent" }
|
|
1016
|
+
]
|
|
1017
|
+
},
|
|
951
1018
|
credentialEndpointPairing: {
|
|
952
1019
|
stepFieldFormat: "steps.%s.query.%s",
|
|
953
1020
|
loopStepFieldFormat: "steps.%s.loop.steps.%s.query.%s",
|
|
@@ -1885,6 +1952,14 @@ var SERVER_OWNED_QUERY_KEYS = {
|
|
|
1885
1952
|
stepFieldFormat: aigentflow_spec_default.serverOwnedQueryKeys.stepFieldFormat,
|
|
1886
1953
|
loopStepFieldFormat: aigentflow_spec_default.serverOwnedQueryKeys.loopStepFieldFormat
|
|
1887
1954
|
};
|
|
1955
|
+
var FLOW_DATA_RESERVED_KEYS = {
|
|
1956
|
+
code: aigentflow_spec_default.flowDataReservedKeys.code,
|
|
1957
|
+
fieldFormat: aigentflow_spec_default.flowDataReservedKeys.fieldFormat,
|
|
1958
|
+
messageFormat: aigentflow_spec_default.flowDataReservedKeys.messageFormat,
|
|
1959
|
+
suggestion: aigentflow_spec_default.flowDataReservedKeys.suggestion,
|
|
1960
|
+
exact: aigentflow_spec_default.flowDataReservedKeys.exact,
|
|
1961
|
+
prefixes: aigentflow_spec_default.flowDataReservedKeys.prefixes
|
|
1962
|
+
};
|
|
1888
1963
|
var CREDENTIAL_ENDPOINT_PAIRING = {
|
|
1889
1964
|
stepFieldFormat: aigentflow_spec_default.credentialEndpointPairing.stepFieldFormat,
|
|
1890
1965
|
loopStepFieldFormat: aigentflow_spec_default.credentialEndpointPairing.loopStepFieldFormat,
|
|
@@ -1947,9 +2022,51 @@ var TEMPLATE_DEFAULT_PIPED = {
|
|
|
1947
2022
|
messageFormat: aigentflow_spec_default.templateDefaultPiped.messageFormat,
|
|
1948
2023
|
suggestion: aigentflow_spec_default.templateDefaultPiped.suggestion
|
|
1949
2024
|
};
|
|
2025
|
+
var FUNCTION_OPERATIONS = {
|
|
2026
|
+
code: aigentflow_spec_default.functionOperations.code,
|
|
2027
|
+
scheme: aigentflow_spec_default.functionOperations.scheme,
|
|
2028
|
+
messageFormat: aigentflow_spec_default.functionOperations.messageFormat,
|
|
2029
|
+
suggestionFormat: aigentflow_spec_default.functionOperations.suggestionFormat,
|
|
2030
|
+
operations: new Set(aigentflow_spec_default.functionOperations.operations),
|
|
2031
|
+
sorted: aigentflow_spec_default.functionOperations.operations
|
|
2032
|
+
};
|
|
1950
2033
|
|
|
1951
2034
|
// src/validators/executors.ts
|
|
1952
2035
|
var SCHEME_SEPARATOR = "://";
|
|
2036
|
+
var UNKNOWN_SCHEME_BASE = (scheme) => `Unknown executor scheme '${scheme}'. It may be valid in a newer AIgentFlow release, or it could be a typo.`;
|
|
2037
|
+
var DECIDE_SCHEME = "decide";
|
|
2038
|
+
var DECIDE_HINT = "Decision models run through ai://{provider}/decide (for example ai://openai/decide), not through a scheme of their own.";
|
|
2039
|
+
var MAX_SCHEME_EDIT_DISTANCE = 2;
|
|
2040
|
+
function editDistance(a, b) {
|
|
2041
|
+
let prev = Array.from({ length: b.length + 1 }, (_, j) => j);
|
|
2042
|
+
for (let i = 1; i <= a.length; i++) {
|
|
2043
|
+
const cur = [i];
|
|
2044
|
+
for (let j = 1; j <= b.length; j++) {
|
|
2045
|
+
cur[j] = Math.min(
|
|
2046
|
+
prev[j] + 1,
|
|
2047
|
+
cur[j - 1] + 1,
|
|
2048
|
+
prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1)
|
|
2049
|
+
);
|
|
2050
|
+
}
|
|
2051
|
+
prev = cur;
|
|
2052
|
+
}
|
|
2053
|
+
return prev[b.length];
|
|
2054
|
+
}
|
|
2055
|
+
function unknownSchemeMessage(scheme) {
|
|
2056
|
+
const lower = scheme.toLowerCase();
|
|
2057
|
+
const base = UNKNOWN_SCHEME_BASE(scheme);
|
|
2058
|
+
if (lower === DECIDE_SCHEME) return `${base} ${DECIDE_HINT}`;
|
|
2059
|
+
let best;
|
|
2060
|
+
let bestDist = MAX_SCHEME_EDIT_DISTANCE + 1;
|
|
2061
|
+
for (const known of [...EXECUTOR_SCHEMES].sort()) {
|
|
2062
|
+
const d = editDistance(lower, known);
|
|
2063
|
+
if (d < bestDist) {
|
|
2064
|
+
best = known;
|
|
2065
|
+
bestDist = d;
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
return best === void 0 ? base : `${base} Did you mean '${best}'?`;
|
|
2069
|
+
}
|
|
1953
2070
|
function checkExecutor(executor, field, stepID, issues) {
|
|
1954
2071
|
const sepIdx = executor.indexOf(SCHEME_SEPARATOR);
|
|
1955
2072
|
const scheme = sepIdx > 0 ? executor.slice(0, sepIdx) : "";
|
|
@@ -1968,11 +2085,28 @@ function checkExecutor(executor, field, stepID, issues) {
|
|
|
1968
2085
|
if (!EXECUTOR_SCHEMES.has(scheme.toLowerCase())) {
|
|
1969
2086
|
issues.warn({
|
|
1970
2087
|
field,
|
|
1971
|
-
message:
|
|
2088
|
+
message: unknownSchemeMessage(scheme),
|
|
1972
2089
|
code: "unknown_executor_scheme",
|
|
1973
2090
|
stepId: stepID
|
|
1974
2091
|
});
|
|
2092
|
+
return;
|
|
1975
2093
|
}
|
|
2094
|
+
checkFunctionOperation(executor, field, stepID, templated, issues);
|
|
2095
|
+
}
|
|
2096
|
+
function checkFunctionOperation(executor, field, stepID, templated, issues) {
|
|
2097
|
+
if (templated) return;
|
|
2098
|
+
const m = EXECUTOR_URL_PATTERN.exec(executor);
|
|
2099
|
+
if (m === null || m[1] !== FUNCTION_OPERATIONS.scheme) return;
|
|
2100
|
+
const op = `${m[2]}/${m[3]}`;
|
|
2101
|
+
if (FUNCTION_OPERATIONS.operations.has(op)) return;
|
|
2102
|
+
const list = FUNCTION_OPERATIONS.sorted.join(", ");
|
|
2103
|
+
issues.error({
|
|
2104
|
+
field,
|
|
2105
|
+
message: FUNCTION_OPERATIONS.messageFormat.replace("%s", () => JSON.stringify(stepID)).replace("%s", () => op).replace("%s", () => list),
|
|
2106
|
+
code: FUNCTION_OPERATIONS.code,
|
|
2107
|
+
stepId: stepID,
|
|
2108
|
+
suggestion: FUNCTION_OPERATIONS.suggestionFormat.replace("%s", () => list)
|
|
2109
|
+
});
|
|
1976
2110
|
}
|
|
1977
2111
|
function validateExecutors(flow, issues) {
|
|
1978
2112
|
const steps = flow.steps;
|
|
@@ -3804,16 +3938,16 @@ function skipSpaces(src, idx) {
|
|
|
3804
3938
|
}
|
|
3805
3939
|
return i;
|
|
3806
3940
|
}
|
|
3807
|
-
function scanString(src, start,
|
|
3941
|
+
function scanString(src, start, quote2) {
|
|
3808
3942
|
let i = start + 1;
|
|
3809
|
-
const raw =
|
|
3943
|
+
const raw = quote2 === "`";
|
|
3810
3944
|
while (i < src.length) {
|
|
3811
3945
|
const c = src[i];
|
|
3812
3946
|
if (!raw && c === "\\") {
|
|
3813
3947
|
i += 2;
|
|
3814
3948
|
continue;
|
|
3815
3949
|
}
|
|
3816
|
-
if (c ===
|
|
3950
|
+
if (c === quote2) return i;
|
|
3817
3951
|
i += 1;
|
|
3818
3952
|
}
|
|
3819
3953
|
return -1;
|
|
@@ -4416,6 +4550,35 @@ function validateRetiredKeys(flow, issues) {
|
|
|
4416
4550
|
}
|
|
4417
4551
|
}
|
|
4418
4552
|
|
|
4553
|
+
// src/validators/flowDataKeys.ts
|
|
4554
|
+
var KEY_DATA = "data";
|
|
4555
|
+
function quote(key) {
|
|
4556
|
+
return JSON.stringify(key);
|
|
4557
|
+
}
|
|
4558
|
+
function flowDataKeyReservation(key) {
|
|
4559
|
+
if (Object.prototype.hasOwnProperty.call(FLOW_DATA_RESERVED_KEYS.exact, key)) {
|
|
4560
|
+
return FLOW_DATA_RESERVED_KEYS.exact[key];
|
|
4561
|
+
}
|
|
4562
|
+
for (const p of FLOW_DATA_RESERVED_KEYS.prefixes) {
|
|
4563
|
+
if (key.startsWith(p.prefix)) return p.reason;
|
|
4564
|
+
}
|
|
4565
|
+
return void 0;
|
|
4566
|
+
}
|
|
4567
|
+
function validateFlowDataKeys(flow, issues) {
|
|
4568
|
+
const data = flow[KEY_DATA];
|
|
4569
|
+
if (!isRecord(data)) return;
|
|
4570
|
+
for (const key of Object.keys(data).sort()) {
|
|
4571
|
+
const reason = flowDataKeyReservation(key);
|
|
4572
|
+
if (reason === void 0) continue;
|
|
4573
|
+
issues.error({
|
|
4574
|
+
field: FLOW_DATA_RESERVED_KEYS.fieldFormat.replace("%s", () => key),
|
|
4575
|
+
message: FLOW_DATA_RESERVED_KEYS.messageFormat.replace("%s", () => quote(key)).replace("%s", () => reason),
|
|
4576
|
+
code: FLOW_DATA_RESERVED_KEYS.code,
|
|
4577
|
+
suggestion: FLOW_DATA_RESERVED_KEYS.suggestion
|
|
4578
|
+
});
|
|
4579
|
+
}
|
|
4580
|
+
}
|
|
4581
|
+
|
|
4419
4582
|
// src/validators/saveDoor.ts
|
|
4420
4583
|
var TOOL_DISCOVERY_MODES = /* @__PURE__ */ new Set(["eager", "lazy", "off"]);
|
|
4421
4584
|
var TEMPLATE_ACTION_OPEN2 = "{{";
|
|
@@ -6878,6 +7041,7 @@ function validateParsed(flow, opts, sources) {
|
|
|
6878
7041
|
validateSaveDoorExtras(f, issues);
|
|
6879
7042
|
validateExecutorConfigEnvScopes(f, issues);
|
|
6880
7043
|
validateServerOwnedQueryKeys(f, issues);
|
|
7044
|
+
validateFlowDataKeys(f, issues);
|
|
6881
7045
|
validateCredentialEndpointPairing(f, issues);
|
|
6882
7046
|
validateExamples(f, issues);
|
|
6883
7047
|
validateOrchestratorExons(f, issues);
|