@vaudience/aigentflow-flow-validator 0.20.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 +103 -35
- package/README.md +3 -2
- package/dist/cli.js +226 -4
- package/dist/index.cjs +222 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +222 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/PARITY.md
CHANGED
|
@@ -4,7 +4,73 @@ 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.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.
|
|
34
|
+
|
|
35
|
+
> package 0.21.0 — **`here://`, `strict_templates` and `template_default_piped`**, in step
|
|
36
|
+
> with the reference (AIgentFlow v2.827.0; `specVersion` 2.827.0). Three changes.
|
|
37
|
+
>
|
|
38
|
+
> 1. **The `here` executor scheme** (`here://maps/{geocode|reverse_geocode|route|places}`) is
|
|
39
|
+
> added to `executorSchemes`, between `getmd` and `homeassistant`; a `here://` step no longer
|
|
40
|
+
> draws `unknown_executor_scheme`. Fixture `valid-executor-here.yaml` forbids that code.
|
|
41
|
+
> 2. **`strict_templates`**, a new optional top-level boolean, is a known key (`knownKeys.Flow`).
|
|
42
|
+
> A value that is not a boolean is `invalid_type` (field `strict_templates`). A YAML null and
|
|
43
|
+
> the YAML 1.1 words the reference's decoder still reads into a `bool` (`yes`/`no`/`on`/`off`,
|
|
44
|
+
> `y`/`n`, in the usual cases) are accepted; this port cannot tell a quoted scalar from a plain
|
|
45
|
+
> one, so a quoted `"yes"` passes here (looser). Its run-time effect is engine behaviour and out
|
|
46
|
+
> of scope. Fixtures: `valid-strict-templates.yaml`, `invalid-strict-templates-type.yaml`.
|
|
47
|
+
> 3. **`template_default_piped`.** The template function `default` takes the value FIRST
|
|
48
|
+
> (`default .x "fallback"`). A pipeline that passes a value into it as a **non-first command**
|
|
49
|
+
> (`{{ .x | default "w" }}`, mid-chain, parenthesised, inside `if`/`else if`/`range`/`with`/
|
|
50
|
+
> `template`/`block`/`define`, or after a `$v :=` declaration) hands the piped value as the
|
|
51
|
+
> LAST argument, so the fallback is always the result and nothing fails at run time. An
|
|
52
|
+
> **error** at the top level and a **warning** inside a loop body (positional, exactly like
|
|
53
|
+
> `template_syntax_error`; the code survives the demotion), reported at the field of the
|
|
54
|
+
> template with its pipeline in the message. The rule runs only on a template that raised no
|
|
55
|
+
> syntax or function finding, because the reference walks a parse tree that exists only for a
|
|
56
|
+
> template that parses. Message and suggestion are the reference's text (spec section
|
|
57
|
+
> `templateDefaultPiped`). Never flagged: a field (`.query.default`), a variable (`$default`),
|
|
58
|
+
> a string or raw-string literal containing the word, a comment, `default` as the FIRST
|
|
59
|
+
> command (`default .x "w"`, `default (.x | upper) "w"`). The reference's run door tolerates
|
|
60
|
+
> this code (it only refuses a run for other errors); this port has no run door. Fixtures:
|
|
61
|
+
> `invalid-template-default-piped.yaml`, `warn-template-default-piped-loop.yaml`,
|
|
62
|
+
> `valid-template-default-clean.yaml` (forbids the code in both severities: its whole risk is
|
|
63
|
+
> a false positive). The all-correct `valid-minimal.yaml` was rewritten from
|
|
64
|
+
> `{{ .query.name | default "world" }}` to `{{ default .query.name "world" }}`.
|
|
65
|
+
>
|
|
66
|
+
> **Known limits.** There is no Go parse tree here: `src/template/gotmpl-default.ts` is a small
|
|
67
|
+
> tokenizer over each `{{ ... }}` action (string, raw-string and character literals and
|
|
68
|
+
> comments respected; pipelines split on top-level `|`; parentheses recursed). Consequences:
|
|
69
|
+
> (a) the printed pipeline is rebuilt from tokens (whitespace normalised) and matches Go's
|
|
70
|
+
> printing for ordinary pipelines, but may differ in spelling for exotic ones (numeric
|
|
71
|
+
> literals are echoed as written); (b) a `default` reached as a method or chain (`(.x).default`) is not a function call and is not flagged; (c) only the
|
|
72
|
+
> template fields the existing syntax check already walks are covered; (d) the same pipeline
|
|
73
|
+
> text appearing twice in one template is reported once, as in the reference.
|
|
8
74
|
|
|
9
75
|
> package 0.20.0 — **`compliance_residency_unenforced`**, in step with the reference
|
|
10
76
|
> (AIgentFlow v2.821.0; `specVersion` 2.821.0). A **warning**, never an error, and it
|
|
@@ -1021,40 +1087,42 @@ Comparison contract: **error `code` + `valid` verdict**, not message wording. Th
|
|
|
1021
1087
|
|
|
1022
1088
|
## Rule map
|
|
1023
1089
|
|
|
1024
|
-
| Area | Go source | JS module | Tested by
|
|
1025
|
-
| ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- |
|
|
1026
|
-
| Required fields, start-step existence, per-step executor, reserved `.` in IDs, reserved id `orchestrator` | `validateBasicStructure`, `ValidateFlow` head | `validators/basicStructure.ts` | `validate.test.ts`, `conformance.test.ts`
|
|
1027
|
-
| Executor URI shape (the ONE parser) + scheme | `ValidateFlow` executor-URL rule (parser.go), `ParseExecutorURLString` | `validators/executors.ts` | `validate.test.ts`
|
|
1028
|
-
| Query/property/array-item schema + array constraints | `validateQueryParameters`, `validateProperties`, `validateArrayItems`, `validateArrayConstraints` | `validators/querySchema.ts` | `validate.test.ts`
|
|
1029
|
-
| Response-expectation types + array items + `required` | `ValidateFlow` (response block), `validateSemantics` | `validators/responseExpectation.ts` | `validate.test.ts`
|
|
1030
|
-
| Response expectation nothing reads (`response_expectation_unread`, warning) | `validateResponseExpectationIsRead` (validation.go) | `validators/responseExpectation.ts` | `validate.test.ts`, `conformance.test.ts`
|
|
1031
|
-
| Error strategy (action, goto, max_delay, backoff, retry_on) | `validateErrorStrategy` | `validators/errorStrategy.ts` | `validate.test.ts`
|
|
1032
|
-
| Retired `budget:` / `max_retries:` (flow, step, loop sub-step) and `campaign.budget_max_per_child` → `unknown_yaml_key` | `retiredGrammarKeys` + `KnownFields(true)` (parser.go) | `validators/retiredKeys.ts` | `validate.test.ts`, `conformance.test.ts`
|
|
1033
|
-
| `next` references (save-door sentinels `null`/`orchestrator`), reachability and cycles (`end` is an ordinary step) | `validateNextLogic` (parser.go), `findReachableSteps`, `checkForCycles` | `validators/connectivity.ts` | `validate.test.ts`, `conformance.test.ts`
|
|
1034
|
-
| Unknown keys at every level (`unknown_yaml_key`) and value kinds (`invalid_type`) | `KnownFields(true)` over the reference's types (parser.go); `knownKeys` in the spec | `validators/unknownKeys.ts` | `validate.test.ts`, `conformance.test.ts`
|
|
1035
|
-
| `next.parallel` + orchestrator-next requirement | `validateNextLogic`, `validateOrchestratorNext` | `validators/nextLogic.ts` | `validate.test.ts`
|
|
1036
|
-
| Expression functions (XOR package/function) | `validateExpressionFunctions` | `validators/expressionFunctions.ts` | `validate.test.ts`
|
|
1037
|
-
| Expression-function catalog (`package:` refused, unknown `function:` refused) | `validateExpressionFunctionCatalog` (parser.go) | `validators/expressionFunctions.ts` | `validate.test.ts`, `conformance.test.ts`
|
|
1038
|
-
| Expression-function USE (`{{ fn_* }}` must be in the catalog AND declared) | `validateExpressionFunctionUsage` (parser.go) | `validators/expressionFunctions.ts` | `validate.test.ts`, `conformance.test.ts`
|
|
1039
|
-
| Loop / for_each / throttle | `validateLoop`, `validateForEach`, `validateThrottle` | `validators/loopForEachThrottle.ts` | `validate.test.ts`
|
|
1040
|
-
| Loop sub-step id that shadows a loop-result summary field (`loop_sub_step_id_reserved`, warning) | `validateLoopSubStepIDCollision` (validation.loopbody.go) | `validators/loopForEachThrottle.ts` | `validate.test.ts`, `conformance.test.ts`
|
|
1041
|
-
| Loop sub-step `next:` targets (same-loop only, sentinels, no parallel) | `validateLoopSubStepNext` (parser.go) | `validators/loopForEachThrottle.ts` | `validate.test.ts`, `conformance.test.ts`
|
|
1042
|
-
| Orchestrator structure + campaign requires orchestrator | `validateOrchestrator`, `validateAndNormalizeCampaign` | `validators/orchestratorCampaign.ts` | `validate.test.ts`
|
|
1043
|
-
| Campaign `max_credits_per_child` (decoded as int64, >= 0), `child_flows` | `CampaignConfig.Validate` (domain.campaign.go) | `validators/orchestratorCampaign.ts` | `validate.test.ts`, `conformance.test.ts`
|
|
1044
|
-
| `tool_discovery` vocabulary, mock scenario `delay`, empty `output:` entry | `validateToolDiscoveryVocabulary`, `validateMockScenarioDelays`, `validateOutputParameters` (parser.go) | `validators/saveDoor.ts` | `validate.test.ts`, `conformance.test.ts`
|
|
1045
|
-
| `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`
|
|
1046
|
-
| 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`
|
|
1047
|
-
|
|
|
1048
|
-
|
|
|
1049
|
-
|
|
|
1050
|
-
|
|
|
1051
|
-
|
|
|
1052
|
-
|
|
|
1053
|
-
| `
|
|
1054
|
-
|
|
|
1055
|
-
|
|
|
1056
|
-
|
|
|
1057
|
-
|
|
|
1090
|
+
| Area | Go source | JS module | Tested by |
|
|
1091
|
+
| ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- | ------------------------------------------------------------- |
|
|
1092
|
+
| Required fields, start-step existence, per-step executor, reserved `.` in IDs, reserved id `orchestrator` | `validateBasicStructure`, `ValidateFlow` head | `validators/basicStructure.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1093
|
+
| Executor URI shape (the ONE parser) + scheme | `ValidateFlow` executor-URL rule (parser.go), `ParseExecutorURLString` | `validators/executors.ts` | `validate.test.ts` |
|
|
1094
|
+
| Query/property/array-item schema + array constraints | `validateQueryParameters`, `validateProperties`, `validateArrayItems`, `validateArrayConstraints` | `validators/querySchema.ts` | `validate.test.ts` |
|
|
1095
|
+
| Response-expectation types + array items + `required` | `ValidateFlow` (response block), `validateSemantics` | `validators/responseExpectation.ts` | `validate.test.ts` |
|
|
1096
|
+
| Response expectation nothing reads (`response_expectation_unread`, warning) | `validateResponseExpectationIsRead` (validation.go) | `validators/responseExpectation.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1097
|
+
| Error strategy (action, goto, max_delay, backoff, retry_on) | `validateErrorStrategy` | `validators/errorStrategy.ts` | `validate.test.ts` |
|
|
1098
|
+
| Retired `budget:` / `max_retries:` (flow, step, loop sub-step) and `campaign.budget_max_per_child` → `unknown_yaml_key` | `retiredGrammarKeys` + `KnownFields(true)` (parser.go) | `validators/retiredKeys.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1099
|
+
| `next` references (save-door sentinels `null`/`orchestrator`), reachability and cycles (`end` is an ordinary step) | `validateNextLogic` (parser.go), `findReachableSteps`, `checkForCycles` | `validators/connectivity.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1100
|
+
| Unknown keys at every level (`unknown_yaml_key`) and value kinds (`invalid_type`) | `KnownFields(true)` over the reference's types (parser.go); `knownKeys` in the spec | `validators/unknownKeys.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1101
|
+
| `next.parallel` + orchestrator-next requirement | `validateNextLogic`, `validateOrchestratorNext` | `validators/nextLogic.ts` | `validate.test.ts` |
|
|
1102
|
+
| Expression functions (XOR package/function) | `validateExpressionFunctions` | `validators/expressionFunctions.ts` | `validate.test.ts` |
|
|
1103
|
+
| Expression-function catalog (`package:` refused, unknown `function:` refused) | `validateExpressionFunctionCatalog` (parser.go) | `validators/expressionFunctions.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1104
|
+
| Expression-function USE (`{{ fn_* }}` must be in the catalog AND declared) | `validateExpressionFunctionUsage` (parser.go) | `validators/expressionFunctions.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1105
|
+
| Loop / for_each / throttle | `validateLoop`, `validateForEach`, `validateThrottle` | `validators/loopForEachThrottle.ts` | `validate.test.ts` |
|
|
1106
|
+
| Loop sub-step id that shadows a loop-result summary field (`loop_sub_step_id_reserved`, warning) | `validateLoopSubStepIDCollision` (validation.loopbody.go) | `validators/loopForEachThrottle.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1107
|
+
| Loop sub-step `next:` targets (same-loop only, sentinels, no parallel) | `validateLoopSubStepNext` (parser.go) | `validators/loopForEachThrottle.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1108
|
+
| Orchestrator structure + campaign requires orchestrator | `validateOrchestrator`, `validateAndNormalizeCampaign` | `validators/orchestratorCampaign.ts` | `validate.test.ts` |
|
|
1109
|
+
| Campaign `max_credits_per_child` (decoded as int64, >= 0), `child_flows` | `CampaignConfig.Validate` (domain.campaign.go) | `validators/orchestratorCampaign.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1110
|
+
| `tool_discovery` vocabulary, mock scenario `delay`, empty `output:` entry | `validateToolDiscoveryVocabulary`, `validateMockScenarioDelays`, `validateOutputParameters` (parser.go) | `validators/saveDoor.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
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` |
|
|
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` |
|
|
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` |
|
|
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` |
|
|
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` |
|
|
1117
|
+
| Credential bindings (`stored/...`, inject_as, exclusivity) | `validateStepCredentialBindings` | `validators/credentialBindings.ts` | `validate.test.ts` |
|
|
1118
|
+
| `input_schema` definition + ordering lint | `ValidateInputSchemaDefinition`, `LintInputSchemaFieldOrdering` | `validators/inputSchema.ts` | `validate.test.ts` |
|
|
1119
|
+
| Step `output_schema` definition (reuses the input-schema subset) | `ValidateInputSchemaDefinition` (on `step.OutputSchema`, parser.go) | `validators/outputSchema.ts` (+ shared `inputSchema.ts`) | `validate.test.ts` |
|
|
1120
|
+
| `quality_gate:` block (rubric/threshold/on_fail/goto) | `FlowParser.validateQualityGate` (parser.go) | `validators/qualityGate.ts` | `validate.test.ts` |
|
|
1121
|
+
| Processing-operation type + per-operation config keys | `validateProcessingOperationShape` | `validators/processingOperations.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1122
|
+
| Step `max_duration` the engine cannot apply (`step_max_duration_ignored`, warning) | `validateStepMaxDurationIsApplied` (validation.go), `parseStepMaxDuration` (flowengine.steptimeout.go) | `validators/stepMaxDuration.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1123
|
+
| An EU-only residency beside an unpinned region-lane `ai://` call (`compliance_residency_unenforced`, warning) | `validateResidencyEnforcement` (validation.residency.go) | `validators/complianceResidency.ts` | `v0200-compliance-residency.test.ts`, `conformance.test.ts` |
|
|
1124
|
+
| Go-template syntax | `validateTemplateExpression` (Parse step) | `template/gotmpl-syntax.ts` + `validators/templates.ts` | `gotmpl-syntax.test.ts`, `validate.test.ts` |
|
|
1125
|
+
| A value piped into `default` (`template_default_piped`; error, warning in a loop body) | `pipedDefaultPipelines` (validation.templatedefault.go) | `template/gotmpl-default.ts` + `validators/templates.ts` | `v0210-template-default-piped.test.ts`, `conformance.test.ts` |
|
|
1058
1126
|
|
|
1059
1127
|
---
|
|
1060
1128
|
|
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.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`.
|
|
@@ -183,7 +184,7 @@ interface ValidationIssue {
|
|
|
183
184
|
- **`tool_discovery`** — `eager`/`lazy`/`off` on the flow, the orchestrator, and a step `query` (empty and templated values skipped).
|
|
184
185
|
- **Mock scenarios** — each `delay` must be a Go duration (`100ms`, not `100`), judged by its written spelling: `0` saves, `0.0` does not.
|
|
185
186
|
- **`output:`** — no empty-string entries.
|
|
186
|
-
- **Templates** — Go `text/template` **syntax** across query, processing, and conditions.
|
|
187
|
+
- **Templates** — Go `text/template` **syntax** across query, processing, and conditions; and a value piped into `default` (`{{ .x | default "w" }}`, error; warning in a loop body — `default` takes the value first: `{{ default .x "w" }}`).
|
|
187
188
|
|
|
188
189
|
## What it does **not** check
|
|
189
190
|
|
package/dist/cli.js
CHANGED
|
@@ -307,6 +307,17 @@ function isValidGoDuration(input) {
|
|
|
307
307
|
var RESERVED_STEP_ID_CHAR = ".";
|
|
308
308
|
var RESERVED_STEP_ID_ORCHESTRATOR = "orchestrator";
|
|
309
309
|
var DISPLAY_NAME_MAX_LEN = 80;
|
|
310
|
+
var YAML11_BOOL_WORDS = new Set(
|
|
311
|
+
["y", "yes", "on", "n", "no", "off"].flatMap((w) => [
|
|
312
|
+
w,
|
|
313
|
+
w.toUpperCase(),
|
|
314
|
+
w[0].toUpperCase() + w.slice(1)
|
|
315
|
+
])
|
|
316
|
+
);
|
|
317
|
+
function isBoolLike(v) {
|
|
318
|
+
if (v === null || typeof v === "boolean") return true;
|
|
319
|
+
return typeof v === "string" && YAML11_BOOL_WORDS.has(v);
|
|
320
|
+
}
|
|
310
321
|
function validateBasicStructure(flow, issues) {
|
|
311
322
|
if (!issues.nonEmptyStringOf(flow, "aigentflow_version", "")) {
|
|
312
323
|
issues.error({
|
|
@@ -336,6 +347,14 @@ function validateBasicStructure(flow, issues) {
|
|
|
336
347
|
});
|
|
337
348
|
}
|
|
338
349
|
}
|
|
350
|
+
if (flow.strict_templates !== void 0 && !isBoolLike(flow.strict_templates)) {
|
|
351
|
+
issues.error({
|
|
352
|
+
field: "strict_templates",
|
|
353
|
+
message: "strict_templates must be a boolean (true or false)",
|
|
354
|
+
code: "invalid_type",
|
|
355
|
+
suggestion: "Write 'strict_templates: true' or remove the key"
|
|
356
|
+
});
|
|
357
|
+
}
|
|
339
358
|
const start = issues.stringOf(flow, "start", "");
|
|
340
359
|
if (start === null || start === "") {
|
|
341
360
|
issues.error({
|
|
@@ -436,7 +455,7 @@ function validateBasicStructure(flow, issues) {
|
|
|
436
455
|
// src/spec/aigentflow-spec.json
|
|
437
456
|
var aigentflow_spec_default = {
|
|
438
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.",
|
|
439
|
-
specVersion: "2.
|
|
458
|
+
specVersion: "2.828.0",
|
|
440
459
|
inputSchemaVersion: 1,
|
|
441
460
|
executorSchemes: [
|
|
442
461
|
"a2a",
|
|
@@ -460,6 +479,7 @@ var aigentflow_spec_default = {
|
|
|
460
479
|
"geocoding",
|
|
461
480
|
"geovisualize",
|
|
462
481
|
"getmd",
|
|
482
|
+
"here",
|
|
463
483
|
"homeassistant",
|
|
464
484
|
"hosting",
|
|
465
485
|
"http",
|
|
@@ -945,6 +965,34 @@ var aigentflow_spec_default = {
|
|
|
945
965
|
stepFieldFormat: "steps.%s.query.%s",
|
|
946
966
|
loopStepFieldFormat: "steps.%s.loop.steps.%s.query.%s"
|
|
947
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
|
+
},
|
|
948
996
|
credentialEndpointPairing: {
|
|
949
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).",
|
|
950
998
|
stepFieldFormat: "steps.%s.query.%s",
|
|
@@ -1275,6 +1323,13 @@ var aigentflow_spec_default = {
|
|
|
1275
1323
|
}
|
|
1276
1324
|
}
|
|
1277
1325
|
},
|
|
1326
|
+
templateDefaultPiped: {
|
|
1327
|
+
$comment: "template_default_piped (ERROR at the top level, WARNING inside a loop body, like template_syntax_error). The template function `default` takes the value FIRST; a pipeline that passes a value into it as a non-first command hands the piped value as the LAST argument, so the fallback is always the result. Flagged: any pipeline (in any action, including if / else if / range / with / template / block conditions, parenthesised sub-pipelines and variable declarations) in which a command whose first word is the bare identifier `default` is not the FIRST command. Never flagged: a field, a variable, a string literal, a comment, `default` as the first command. The message fills %s with the pipeline.",
|
|
1328
|
+
code: "template_default_piped",
|
|
1329
|
+
function: "default",
|
|
1330
|
+
messageFormat: "`default` takes the value FIRST, and a pipe passes the piped value LAST: `%s` is `default <fallback> <value>`, so the fallback is always the result",
|
|
1331
|
+
suggestion: 'write the value first and the fallback second, as a call: `{{ default .x "fallback" }}`'
|
|
1332
|
+
},
|
|
1278
1333
|
complianceResidency: {
|
|
1279
1334
|
$comment: "compliance_residency_unenforced (WARNING, never an error; needs no catalogue). When compliance.residency is non-empty and every code (trimmed, upper-cased) is in euResidencyCodes, each top-level step and loop sub-step whose executor starts with aiProtocol+protocolSeparator, then the orchestrator (provider = its exons execution.provider), warns when its provider (literal, lower-cased; skipped when empty or containing templateActionOpen) is in regionLaneProviders and the call is not pinned. Pinned = query region (orchestrator.region) a non-empty string after trimming; OR enforce_region true (query: bool true or the strings in enforceRegionTrueStrings; orchestrator: bool true); OR the author named the endpoint: query <provider>+baseUrlParamSuffix a non-empty string after trimming, or executor_config.<provider>.base_url non-empty after trimming. The provider segment of ai://<provider>/<operation> exists only when a slash follows it. regionLaneProviders is the set of providers whose adapter routes on `region`; a provider outside it has no region lane and is never warned. Message, suggestion and site formats fill %s and %q (a Go-quoted string) in order; the message's last placeholder is the residency list as written, printed [A B]. Reference: validateResidencyEnforcement (validation.residency.go).",
|
|
1280
1335
|
code: "compliance_residency_unenforced",
|
|
@@ -1638,6 +1693,7 @@ var aigentflow_spec_default = {
|
|
|
1638
1693
|
rate_limit: "FlowRateLimitConfig",
|
|
1639
1694
|
start: "scalar",
|
|
1640
1695
|
steps: "map<StepDefinition>",
|
|
1696
|
+
strict_templates: "scalar",
|
|
1641
1697
|
tags: "list<scalar>",
|
|
1642
1698
|
template: "FlowTemplateMetadata",
|
|
1643
1699
|
tool_discovery: "scalar",
|
|
@@ -1951,6 +2007,14 @@ var SERVER_OWNED_QUERY_KEYS = {
|
|
|
1951
2007
|
stepFieldFormat: aigentflow_spec_default.serverOwnedQueryKeys.stepFieldFormat,
|
|
1952
2008
|
loopStepFieldFormat: aigentflow_spec_default.serverOwnedQueryKeys.loopStepFieldFormat
|
|
1953
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
|
+
};
|
|
1954
2018
|
var CREDENTIAL_ENDPOINT_PAIRING = {
|
|
1955
2019
|
stepFieldFormat: aigentflow_spec_default.credentialEndpointPairing.stepFieldFormat,
|
|
1956
2020
|
loopStepFieldFormat: aigentflow_spec_default.credentialEndpointPairing.loopStepFieldFormat,
|
|
@@ -2010,6 +2074,12 @@ var EXAMPLES = {
|
|
|
2010
2074
|
})),
|
|
2011
2075
|
codes: aigentflow_spec_default.examples.codes
|
|
2012
2076
|
};
|
|
2077
|
+
var TEMPLATE_DEFAULT_PIPED = {
|
|
2078
|
+
code: aigentflow_spec_default.templateDefaultPiped.code,
|
|
2079
|
+
function: aigentflow_spec_default.templateDefaultPiped.function,
|
|
2080
|
+
messageFormat: aigentflow_spec_default.templateDefaultPiped.messageFormat,
|
|
2081
|
+
suggestion: aigentflow_spec_default.templateDefaultPiped.suggestion
|
|
2082
|
+
};
|
|
2013
2083
|
|
|
2014
2084
|
// src/validators/executors.ts
|
|
2015
2085
|
var SCHEME_SEPARATOR = "://";
|
|
@@ -3867,16 +3937,16 @@ function skipSpaces(src, idx) {
|
|
|
3867
3937
|
}
|
|
3868
3938
|
return i;
|
|
3869
3939
|
}
|
|
3870
|
-
function scanString(src, start,
|
|
3940
|
+
function scanString(src, start, quote2) {
|
|
3871
3941
|
let i = start + 1;
|
|
3872
|
-
const raw =
|
|
3942
|
+
const raw = quote2 === "`";
|
|
3873
3943
|
while (i < src.length) {
|
|
3874
3944
|
const c = src[i];
|
|
3875
3945
|
if (!raw && c === "\\") {
|
|
3876
3946
|
i += 2;
|
|
3877
3947
|
continue;
|
|
3878
3948
|
}
|
|
3879
|
-
if (c ===
|
|
3949
|
+
if (c === quote2) return i;
|
|
3880
3950
|
i += 1;
|
|
3881
3951
|
}
|
|
3882
3952
|
return -1;
|
|
@@ -3973,6 +4043,117 @@ function splitPipeline(body) {
|
|
|
3973
4043
|
return stages;
|
|
3974
4044
|
}
|
|
3975
4045
|
|
|
4046
|
+
// src/template/gotmpl-default.ts
|
|
4047
|
+
var FUNCTION_NAME = "default";
|
|
4048
|
+
var PIPE = "|";
|
|
4049
|
+
var OPEN_PAREN = "(";
|
|
4050
|
+
var CLOSE_PAREN = ")";
|
|
4051
|
+
var MAX_NESTING_DEPTH = 100;
|
|
4052
|
+
var PIPELINE_KEYWORDS = /^(?:else\s+if|else\s+with|if|range|with|template|block)(?![\w$.])/;
|
|
4053
|
+
var NO_PIPELINE = /^(?:define|else|end|break|continue)(?![\w$.])/;
|
|
4054
|
+
var DECLARATION = /^\$\w*(?:\s*,\s*\$\w*)*\s*:?=/;
|
|
4055
|
+
function pipedDefaultPipelines(src) {
|
|
4056
|
+
const { actions, fatal } = lexActions(src);
|
|
4057
|
+
if (fatal) return [];
|
|
4058
|
+
const found = /* @__PURE__ */ new Set();
|
|
4059
|
+
try {
|
|
4060
|
+
for (const action of actions) {
|
|
4061
|
+
if (action.isComment || action.body === "") continue;
|
|
4062
|
+
scanAction(action.body, found);
|
|
4063
|
+
}
|
|
4064
|
+
} catch (err) {
|
|
4065
|
+
if (!(err instanceof RangeError)) throw err;
|
|
4066
|
+
return [];
|
|
4067
|
+
}
|
|
4068
|
+
return [...found].sort();
|
|
4069
|
+
}
|
|
4070
|
+
function scanAction(body, found) {
|
|
4071
|
+
let rest = body;
|
|
4072
|
+
const kw = PIPELINE_KEYWORDS.exec(rest);
|
|
4073
|
+
if (!kw && NO_PIPELINE.test(rest)) return;
|
|
4074
|
+
if (kw) {
|
|
4075
|
+
rest = rest.slice(kw[0].length).trimStart();
|
|
4076
|
+
const keyword = kw[0].replace(/\s+/g, " ");
|
|
4077
|
+
if (keyword === "template" || keyword === "block") {
|
|
4078
|
+
const q2 = rest[0];
|
|
4079
|
+
if (q2 === '"' || q2 === "`") {
|
|
4080
|
+
const end = scanString(rest, 0, q2);
|
|
4081
|
+
if (end === -1) return;
|
|
4082
|
+
rest = rest.slice(end + 1).trimStart();
|
|
4083
|
+
}
|
|
4084
|
+
}
|
|
4085
|
+
}
|
|
4086
|
+
if (rest === "") return;
|
|
4087
|
+
parsePipeline(rest, 0, false, found, 0);
|
|
4088
|
+
}
|
|
4089
|
+
function parsePipeline(src, start, nested, found, depth) {
|
|
4090
|
+
if (depth > MAX_NESTING_DEPTH) return { text: src.slice(start), end: src.length };
|
|
4091
|
+
let i = start;
|
|
4092
|
+
const n = src.length;
|
|
4093
|
+
let decl = "";
|
|
4094
|
+
const decMatch = DECLARATION.exec(src.slice(i));
|
|
4095
|
+
if (decMatch) {
|
|
4096
|
+
decl = decMatch[0].replace(/\s+/g, " ") + " ";
|
|
4097
|
+
i += decMatch[0].length;
|
|
4098
|
+
}
|
|
4099
|
+
const commands = [];
|
|
4100
|
+
let current = [];
|
|
4101
|
+
let firstWords = [];
|
|
4102
|
+
let pipedDefault = false;
|
|
4103
|
+
const pushCommand = () => {
|
|
4104
|
+
if (current.length > 0) {
|
|
4105
|
+
commands.push(current);
|
|
4106
|
+
firstWords.push(current[0]);
|
|
4107
|
+
}
|
|
4108
|
+
current = [];
|
|
4109
|
+
};
|
|
4110
|
+
while (i < n) {
|
|
4111
|
+
const c = src[i];
|
|
4112
|
+
if (c === " " || c === " " || c === "\n" || c === "\r") {
|
|
4113
|
+
i += 1;
|
|
4114
|
+
continue;
|
|
4115
|
+
}
|
|
4116
|
+
if (c === '"' || c === "`" || c === "'") {
|
|
4117
|
+
const end = scanString(src, i, c);
|
|
4118
|
+
const stop = end === -1 ? n - 1 : end;
|
|
4119
|
+
current.push(src.slice(i, stop + 1));
|
|
4120
|
+
i = stop + 1;
|
|
4121
|
+
continue;
|
|
4122
|
+
}
|
|
4123
|
+
if (c === OPEN_PAREN) {
|
|
4124
|
+
const inner = parsePipeline(src, i + 1, true, found, depth + 1);
|
|
4125
|
+
current.push(`(${inner.text})`);
|
|
4126
|
+
i = inner.end;
|
|
4127
|
+
continue;
|
|
4128
|
+
}
|
|
4129
|
+
if (c === CLOSE_PAREN) {
|
|
4130
|
+
if (nested) {
|
|
4131
|
+
i += 1;
|
|
4132
|
+
break;
|
|
4133
|
+
}
|
|
4134
|
+
i += 1;
|
|
4135
|
+
continue;
|
|
4136
|
+
}
|
|
4137
|
+
if (c === PIPE) {
|
|
4138
|
+
pushCommand();
|
|
4139
|
+
i += 1;
|
|
4140
|
+
continue;
|
|
4141
|
+
}
|
|
4142
|
+
let j = i;
|
|
4143
|
+
while (j < n && !/[\s|()"`']/.test(src[j])) j += 1;
|
|
4144
|
+
if (j === i) j = i + 1;
|
|
4145
|
+
current.push(src.slice(i, j));
|
|
4146
|
+
i = j;
|
|
4147
|
+
}
|
|
4148
|
+
pushCommand();
|
|
4149
|
+
firstWords.forEach((word, idx) => {
|
|
4150
|
+
if (idx > 0 && word === FUNCTION_NAME) pipedDefault = true;
|
|
4151
|
+
});
|
|
4152
|
+
const text = decl + commands.map((cmd) => cmd.join(" ")).join(` ${PIPE} `);
|
|
4153
|
+
if (pipedDefault) found.add(text);
|
|
4154
|
+
return { text, end: i };
|
|
4155
|
+
}
|
|
4156
|
+
|
|
3976
4157
|
// src/validators/processingOperations.ts
|
|
3977
4158
|
var PROCESSING_OP_GUARD_KEY = PROCESSING_OPERATIONS.guardKey;
|
|
3978
4159
|
var PROCESSING_PHASES = ["pre_processing", "post_processing"];
|
|
@@ -4119,6 +4300,17 @@ function checkTemplateString(value, field, stepID, issues, stats, opts, demote =
|
|
|
4119
4300
|
});
|
|
4120
4301
|
}
|
|
4121
4302
|
}
|
|
4303
|
+
if (errors.length > 0) return;
|
|
4304
|
+
for (const pipe of pipedDefaultPipelines(value)) {
|
|
4305
|
+
report({
|
|
4306
|
+
field,
|
|
4307
|
+
message: TEMPLATE_DEFAULT_PIPED.messageFormat.replace("%s", () => pipe),
|
|
4308
|
+
code: TEMPLATE_DEFAULT_PIPED.code,
|
|
4309
|
+
stepId: stepID,
|
|
4310
|
+
context: `Template: ${value}`,
|
|
4311
|
+
suggestion: TEMPLATE_DEFAULT_PIPED.suggestion
|
|
4312
|
+
});
|
|
4313
|
+
}
|
|
4122
4314
|
}
|
|
4123
4315
|
function walkStrings2(basePath, value, stepID, issues, stats, opts, check, demote = false) {
|
|
4124
4316
|
if (isString(value)) {
|
|
@@ -4357,6 +4549,35 @@ function validateRetiredKeys(flow, issues) {
|
|
|
4357
4549
|
}
|
|
4358
4550
|
}
|
|
4359
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
|
+
|
|
4360
4581
|
// src/validators/saveDoor.ts
|
|
4361
4582
|
var TOOL_DISCOVERY_MODES = /* @__PURE__ */ new Set(["eager", "lazy", "off"]);
|
|
4362
4583
|
var TEMPLATE_ACTION_OPEN2 = "{{";
|
|
@@ -6821,6 +7042,7 @@ function validateParsed(flow, opts, sources) {
|
|
|
6821
7042
|
validateSaveDoorExtras(f, issues);
|
|
6822
7043
|
validateExecutorConfigEnvScopes(f, issues);
|
|
6823
7044
|
validateServerOwnedQueryKeys(f, issues);
|
|
7045
|
+
validateFlowDataKeys(f, issues);
|
|
6824
7046
|
validateCredentialEndpointPairing(f, issues);
|
|
6825
7047
|
validateExamples(f, issues);
|
|
6826
7048
|
validateOrchestratorExons(f, issues);
|