@vaudience/aigentflow-flow-validator 0.15.3 → 0.19.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,62 @@ 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.802.0`** (`SPEC_VERSION` in [`src/spec/aigentflow-spec.json`](./src/spec/aigentflow-spec.json)), plus `server_owned_query_key` (package 0.15.1, below) from the AIgentFlow release after v2.790.0, and `credential_endpoint_unpaired` (package 0.15.2, below) from AIgentFlow v2.793.0 and the release after it, and `display_name_too_long` (package 0.15.3, below) from AIgentFlow v2.802.0.
7
+ **Tracks AIgentFlow flow schema: `v2.820.0`** (`SPEC_VERSION` in [`src/spec/aigentflow-spec.json`](./src/spec/aigentflow-spec.json)), which adds `orchestrator.region` / `orchestrator.enforce_region` (package 0.19.0, below). 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.19.0 — **`orchestrator.region` and `orchestrator.enforce_region`**, in step
10
+ > with the reference (AIgentFlow v2.820.0; `specVersion` 2.820.0). Both are new keys of
11
+ > the `orchestrator:` block (a `string` and a `bool`), added to the known-key table.
12
+ > One rule ported: `enforce_region: true` with an absent, null, empty or
13
+ > whitespace-only `region` is an **error**, `orchestrator_enforce_region_needs_region`
14
+ > (field `orchestrator.enforce_region`), because the provider would refuse every turn.
15
+ > The reference raises it from its parser's orchestrator validation as a plain
16
+ > flow-validation error with no machine-readable code; this package's code and field
17
+ > are its own naming, the same as for `orchestrator_mode_invalid`. Only a YAML boolean
18
+ > `true` arms the rule. The runtime effect (the region reaching the provider request) is
19
+ > engine behaviour and out of scope. New conformance fixtures:
20
+ > `valid-orchestrator-region.yaml`, `invalid-orchestrator-enforce-region-no-region.yaml`.
21
+ >
22
+ > package 0.18.0 — **the AigentChat executor is removed**, in step with the reference
23
+ > (AIgentFlow v2.819.0; `specVersion` 2.819.0). The reference no longer registers the
24
+ > `nexus://` protocol or its `aigentchat://` alias, so the spec drops: `nexus` from
25
+ > `executorSchemes` (42 protocols); the `nexus` and `aigentchat` keys of
26
+ > `executorConfigEnvScopes.scopes`; the `nexus` row of `credentialEndpointPairing.families`
27
+ > together with `protocolAliases` and the row-level `storedKeyShape` shape (no row uses
28
+ > them now, so the validator code for them is gone too). A `nexus://` or `aigentchat://`
29
+ > step is now an ordinary unknown scheme: `unknown_executor_scheme` (warning), no
30
+ > credential/endpoint pairing, and a `${AIGENTFLOW_NEXUS_*}` reference under an
31
+ > `executor_config.nexus` key is judged like any key with an empty scope
32
+ > (`executor_config_env_scope`). `executor_config.*.service_channel` and
33
+ > `enable_streaming` stay known keys; the reference keeps them declared but inert.
34
+ > Fixtures: `warn-credential-endpoint-nexus.yaml` is deleted, and the nexus steps and
35
+ > scope entry are removed from `valid-credential-endpoint-own-key-own-url.yaml` and
36
+ > `valid-executor-config-env-scope.yaml`.
37
+
38
+ > package 0.17.0 — **the `examples:` block**, in step with the reference. The spec is
39
+ > regenerated (`specVersion` 2.811.0): `knownKeys` gains `ExampleDefinition`,
40
+ > `Expectation`, `Checkpoint`, `FieldMatcher`, `FileRef`, `ExampleReference` and
41
+ > `ExampleVariant`, `Flow.examples`, and the `trusted_output` / `allow_untrusted` /
42
+ > `untrusted` keys the previous spec lacked; and a new top-level `examples` section
43
+ > carries every limit, vocabulary, pattern, the credential-shaped-literal set and each
44
+ > code's severity. Rules and divergences are under [The examples block](#the-examples-block).
45
+ >
46
+ > | Rule (reference) | Codes | Severity | Module |
47
+ > | ---------------------------------------------------------- | ----------------------------------------------- | -------------- | ----------------------------------------------------- |
48
+ > | the `examples:` walker (every rule, one pass over a block) | 58 `example_*` / `examples_*` codes, spec table | error, warning | `validators/examples.ts`, spec `examples`, `goUrl.ts` |
49
+ > | the reference's blocked-host rule | `example_ref_blocked_host` | error | **not ported** (divergence #19) |
50
+
51
+ > package 0.16.0 — **the untrusted-input marker keys** (AIgentFlow v2.811.0).
52
+ > The spec's `knownKeys` gain `untrusted` on `QueryDefinition` and `InputSchemaField`, and
53
+ > `trusted_output` / `allow_untrusted` on `StepDefinition` and `LoopStepDefinition`;
54
+ > `specVersion` becomes `2.811.0`. No new rule: a misspelling of any of them is
55
+ > `unknown_yaml_key`, as for every other key. What the keys DO — a marked input is sealed
56
+ > in model-facing steps and placed only fenced in the user message, a tainted value in a
57
+ > sensitive data-executor parameter refuses the step unless `allow_untrusted` names it,
58
+ > `trusted_output` declassifies a step's output — is engine behaviour at run time and out
59
+ > of scope for static validation. Fixtures: `valid-untrusted-inputs.yaml`,
60
+ > `invalid-untrusted-misspelt.yaml`; focused test `v0160-untrusted-inputs.test.ts`.
61
+ > ||||||| /tmp/b.txt
62
+ > **Tracks AIgentFlow flow schema: `v2.802.0`** (`SPEC_VERSION` in [`src/spec/aigentflow-spec.json`](./src/spec/aigentflow-spec.json)), plus `server_owned_query_key` (package 0.15.1, below) from the AIgentFlow release after v2.790.0, and `credential_endpoint_unpaired` (package 0.15.2, below) from AIgentFlow v2.793.0 and the release after it, and `display_name_too_long` (package 0.15.3, below) from AIgentFlow v2.802.0.
8
63
 
9
64
  > package 0.15.3 — **the optional top-level `display_name`** (AIgentFlow #187). The spec
10
65
  > gains `display_name: scalar` on `Flow` and `specVersion` becomes `2.802.0`.
@@ -46,7 +101,7 @@ discipline for keeping the two in sync.
46
101
  > is exactly `${NAME}` for one of the row's server variables on a row whose plugin
47
102
  > expands references, and its endpoint (same order) is a literal: not templated, not a
48
103
  > reference, and not of the same origin as the row's default. Implicit-credential
49
- > families are not judged; `nexus://` and `aigentchat://` get the ai:// shape.
104
+ > families are not judged.
50
105
  > - **The default-origin comparison needs Go's URL semantics**, so `validators/goUrl.ts`
51
106
  > ports `net/url.Parse` (what it refuses, and the scheme and host it extracts),
52
107
  > `strings.TrimSpace` (Go trims U+0085 and keeps U+FEFF) and `strings.ToLower` (simple
@@ -856,7 +911,7 @@ max_retries` and `QualityGateDefinition.max_retries` are unchanged. Removing
856
911
  > separate target functions; widening `hasCycle` too would invent a divergence
857
912
  > and emit a spurious `potential_infinite_loop` on any rendezvous or error
858
913
  > redirect back to an earlier step.
859
- > 4. **`executorSchemes` is AIgentFlow's registered set exactly** (43 protocols,
914
+ > 4. **`executorSchemes` is AIgentFlow's registered set exactly** (43 protocols at v2.608.0, 42 since the AigentChat executor was removed,
860
915
  > enumerated from `NewExecutorSchemaRegistry`). `web://` was missing, so a
861
916
  > correct step warned; nine schemes AIgentFlow does not register (`openai`,
862
917
  > `anthropic`, `perplexity`, `vertexai`, `ollama`, `vllm`, `aigentchat`,
@@ -953,6 +1008,7 @@ Comparison contract: **error `code` + `valid` verdict**, not message wording. Th
953
1008
  | `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` |
954
1009
  | 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` |
955
1010
  | 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` |
1011
+ | 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` |
956
1012
  | 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` |
957
1013
  | Credential bindings (`stored/...`, inject_as, exclusivity) | `validateStepCredentialBindings` | `validators/credentialBindings.ts` | `validate.test.ts` |
958
1014
  | `input_schema` definition + ordering lint | `ValidateInputSchemaDefinition`, `LintInputSchemaFieldOrdering` | `validators/inputSchema.ts` | `validate.test.ts` |
@@ -1155,10 +1211,10 @@ number their shared divergences alike._
1155
1211
  base URL), the service configuration an executor fills, and whether a client attaches
1156
1212
  the deployment's service credential. Neither side reads them statically.
1157
1213
  - **Default endpoints of rows the rule never reads are not carried** (`getmd://`, which
1158
- is never judged, and `nexus://`, whose shape consults no default). Verdict-neutral.
1214
+ is never judged). Verdict-neutral.
1159
1215
  - **A `credentials:` mapping with a non-string key (looser, shared).** The reference
1160
- decodes it into `map[any]any`, which its nexus own-key check does not accept, so the
1161
- endpoint warns there; both ports stringify mapping keys and see a key of the step's own.
1216
+ decoded it into `map[any]any` for the since-removed `nexus://` own-key check; with that
1217
+ family gone the shared looseness no longer has a rule to act on.
1162
1218
  - **JS only, looser: an endpoint shaped like a YAML timestamp is not read as a string.**
1163
1219
  yaml.v3 decodes a PLAIN `2024-01-01` into `time.Time`, which the reference's
1164
1220
  `.(string)` rejects; the `yaml` package gives a string and the source's quoting is not
@@ -1168,6 +1224,135 @@ number their shared divergences alike._
1168
1224
  - **Not ported, verdict-neutral:** Go validates a bracketed IPv6 host with
1169
1225
  `netip.ParseAddr`; a bracketed host is never a default's host either way.
1170
1226
 
1227
+ 19. **`example_ref_blocked_host` is reference-only (0.16.0).** The reference refuses a file
1228
+ reference whose host is private or local (`localhost`, loopback, link-local and cloud
1229
+ metadata addresses, RFC 1918 and carrier-grade ranges, the unspecified address, and any
1230
+ name ending `.internal`, `.local` or `.localhost`). It reuses the server's own address
1231
+ tables, so a second literal table here would be another copy to keep in step; it is also
1232
+ advice, because the control is the run-time dialer. `https://localhost/a.json` therefore
1233
+ passes here (looser). The code is still listed in the spec's `examples.codes` table (it is
1234
+ the reference's code), and no fixture in this repository raises it.
1235
+
1236
+ 20. **The example's inline input is judged by a NARROWED check (0.16.0; looser).** The
1237
+ reference judges an example's `input` (and a variant's patched input) with its full
1238
+ run-time input validator. This port has no value validator, so it judges only: an unknown
1239
+ field against `input_schema` (or against the declared `query` keys when there is no
1240
+ schema), a required field that is missing (honouring `visible_when`, and skipping a
1241
+ required `secret` field, which the person running the evaluation supplies), a value of the
1242
+ wrong KIND (`string`, `multiline`, `secret`, `enum` and `date` are strings; `number` is any
1243
+ YAML number, `.nan` and the infinities included, as the reference admits them; `bool` is a
1244
+ boolean; `array_of_strings` is a list of strings; `file` is a mapping whose keys are only
1245
+ `url`, `sha256`, `media_type` and `note`, else `example_file_input_needs_ref`) and an enum
1246
+ value outside its list. Not judged: `min_length` / `max_length`, `min` / `max`,
1247
+ `min_items` / `max_items`, `pattern`, the `YYYY-MM-DD` shape and calendar of a `date`, the
1248
+ 256-key cap and the string-length cap. A document the reference accepts is never refused
1249
+ here; one it refuses only for those constraints is accepted here. The message wording of
1250
+ the four judged failures is the reference's, and the same `(code, field)` is reported.
1251
+ A date or timestamp a loader resolved to a date object (`validateFlowObject`) is read as the
1252
+ string a JSON body would carry; the `yaml` package this validator parses with leaves such a
1253
+ scalar a string, so for parsed text this is the identity.
1254
+
1255
+ 21. **A field matcher's `matches` pattern is judged by length only (0.16.0; looser).** The
1256
+ reference compiles the pattern with its regular-expression engine (RE2), and RE2 and a
1257
+ JavaScript `RegExp` disagree about what is valid (`(?P<n>x)` is valid RE2 and a JS syntax
1258
+ error), so a compile check here could refuse what the reference accepts. This port judges
1259
+ the length (the spec's `limits.maxPatternLength`, counted in UTF-8 bytes as Go counts it)
1260
+ and the "exactly one operator" rule, and nothing about the pattern's syntax: `(` passes.
1261
+
1262
+ 22. **Counts and wording that differ in the margin (0.16.0).**
1263
+ - The byte counts behind `example_input_too_large` and `examples_block_too_large` are
1264
+ measured over the JSON of the parsed document (for the block, with empty values
1265
+ omitted, as the reference's typed structs omit them), not over the reference's typed
1266
+ marshalling, which also escapes `<`, `>` and `&`. The count here is never larger than the
1267
+ reference's, so the verdict can differ only within a few bytes of the limit, and only
1268
+ looser; the number printed in the message can differ by those bytes.
1269
+ - `example_expectation_contradiction` compares with simple case folding per code point,
1270
+ as `strings.EqualFold` does; a code point whose fold orbit has three members is not
1271
+ followed.
1272
+ - A float in a message (`weight`, `min_score`) is printed as Go prints a `float64` with
1273
+ `%v` (shortest form, an exponent below `1e-4` and from `1e6`).
1274
+ - The recursion bound of 32 on a nested value is the reference's own and is not in the
1275
+ spec.
1276
+
1277
+ ---
1278
+
1279
+ ## The examples block
1280
+
1281
+ An `examples:` list records concrete cases inside the flow document: an input, what a good
1282
+ result looks like, why the case matters. The engine never reads it; the validator is the only
1283
+ reader here, so every rule is a judgement of the DOCUMENT at save time, pure and offline (no
1284
+ reference is fetched and no host is resolved). One walker, `validators/examples.ts`, reports
1285
+ every finding of a block in one pass; each finding's `field` is an index path
1286
+ (`examples[2].expected.fields.total`, `examples[0].variants[1].id`), never an id-based one,
1287
+ because an id can be invalid or duplicate while it is being reported.
1288
+
1289
+ **What is judged**, in the order the walker reads it:
1290
+
1291
+ - **The set.** At most 50 examples (`examples_too_many`); the block's JSON at most 256 KiB
1292
+ (`examples_block_too_large`); a warning when every example is held out
1293
+ (`examples_all_held_out`) and when the flow is public (`examples_published_with_public_flow`).
1294
+ - **Identity and prose.** `id` required, matching the spec's `idPattern`, unique
1295
+ (`example_id_missing` / `_invalid` / `_duplicate`); `title` non-blank, at most 80 code points;
1296
+ `guidance` at most 2000 code points, and a warning when it is absent
1297
+ (`example_guidance_missing`); `notes` at most 2000; `tags` at most 8, each matching
1298
+ `tagPattern`, none repeated (`example_tag_invalid`); `weight` above 0 and at most 10;
1299
+ `min_score` from 0 to 1; `side_effects` `refuse` or `allow`; `hold_out` refused on a public
1300
+ flow (`example_holdout_in_public_flow`).
1301
+ - **Input.** Exactly one of `input` / `input_ref` (`example_input_conflict`,
1302
+ `example_input_missing`; `input: {}` is a present, empty input); an inline input at most 32 KiB
1303
+ and judged as divergence #20 describes (`example_input_invalid`, `example_file_input_needs_ref`,
1304
+ `example_input_unvalidated` when the flow declares neither `input_schema` nor `query`).
1305
+ A value for a `secret` field is refused (`example_secret_value`), because examples are saved
1306
+ inside the flow.
1307
+ - **File references** (`input_ref`, a file-typed input, `expected.reference.ref`): `url` at most
1308
+ 2048 bytes and parseable as Go parses a URL; a reserved scheme (`artifact`, `trove`, `aiv`,
1309
+ `s3`, `gs`) is refused by name (`example_ref_scheme_reserved`), any other non-`https` scheme
1310
+ too (`example_ref_scheme`); a login, a fragment or an empty host is `example_ref_invalid`;
1311
+ `sha256` is 64 lower-case hex digits, `media_type` is `type/subtype`, `note` at most 300;
1312
+ warnings for a reference with no `sha256` (`example_ref_unpinned`) and for a signed-link query
1313
+ key (`example_ref_signed_url`).
1314
+ - **Expectations** (`expected`, a checkpoint, a variant's replacing `expected`): `status` one of
1315
+ `completed`, `failed`, `paused_for_human`; `fields` and `exact` are mutually exclusive and
1316
+ absent when the case is meant to end `failed` or `paused_for_human`
1317
+ (`example_expected_conflict`); every `fields` / `exact` key is an output of the flow (a
1318
+ checkpoint: a field of the step's `output_schema`), or a warning when no list is declared;
1319
+ each field matcher has exactly one operator and `approx` pairs with a non-negative
1320
+ `tolerance` (`example_field_matcher_invalid`); `rubric` non-blank and at most 4000;
1321
+ `reference` has exactly one of `text` / `ref`, text at most 32 KiB, and a warning for a
1322
+ reference that is not text; `must_not` at most 12 statements of at most 300, none blank;
1323
+ `must_not_contain` at most 20 literals of 1 to 200; a warning when a field's `equals` is also
1324
+ forbidden by `must_not_contain`; an expectation must check something
1325
+ (`example_expected_empty`; `status: completed` alone checks nothing), and a missing
1326
+ `expected` is `example_expected_missing`. A reference the judge cannot read, alone, is the
1327
+ warning `examples_unreadable_by_judge`.
1328
+ - **Checkpoints.** The key must be a step of the flow, and not a `for_each`, `loop` or
1329
+ `next.parallel` step (`example_checkpoint_step_unknown`, `_composite`); a checkpoint cannot
1330
+ name a `status` (`example_checkpoint_status`).
1331
+ - **Variants.** At most 10; `id` matching `idPattern` and unique within the example;
1332
+ `origin` required with no default, `author` or `synthetic`; a variant must change the input
1333
+ or the expectation, and sets `input_patch` or `input_ref`, not both. The patch is an RFC 7386
1334
+ merge patch applied to the parent's inline input (a mapping merges, `null` deletes, anything
1335
+ else replaces; the input is never mutated) and the patched input is judged like the example's
1336
+ own (`example_variant_input_invalid`); a patch giving a secret a value is refused
1337
+ (`example_secret_value`), one deleting it with `null` is fine; a patch on a parent whose
1338
+ input is `input_ref` cannot be checked (`example_variant_unchecked`).
1339
+ - **Credential-shaped literals.** Every string leaf of an example, a checkpoint and a variant
1340
+ (title, guidance, notes, input, rubric, reference text, must_not, matcher operands, `exact`,
1341
+ patches) is matched against the spec's eight patterns (`example_secret_like_value`). The
1342
+ patterns are data (`examples.secretPatterns`, each with its flags); ordinary hyphenated slugs
1343
+ and prose must not match, and a test holds that.
1344
+ - **Unknown keys.** A typo at example, expectation, matcher, file-reference or variant depth is
1345
+ `unknown_yaml_key`, from the regenerated `knownKeys`: nothing is hand-listed.
1346
+
1347
+ Severities are the spec's `examples.codes` table, held to the walker by a test (each code's
1348
+ fixture asserts the walker's severity equals the table's).
1349
+
1350
+ **Measured.** The conformance corpus is shared byte for byte with the Go port; every fixture
1351
+ that carries an expected `(code, field, message)` agrees here, but the one divergence #21 names.
1352
+ Mutants of the rules (a secret pattern dropped, the id check disabled, the duplicate check,
1353
+ the weight bound, the origin vocabulary, the unknown-checkpoint code, the https-only scheme
1354
+ rule, the empty-expectation rule, the merge patch's null-delete) are each killed by a test.
1355
+
1171
1356
  ---
1172
1357
 
1173
1358
  ## Unknown keys and value kinds
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.802.0"`).
119
+ The AIgentFlow flow-schema version this build tracks (e.g. `"2.820.0"`).
120
120
 
121
121
  ### Options
122
122
 
@@ -174,7 +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
- - **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, a `nexus://` endpoint with no key of its own.
177
+ - **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
178
  - **Credential bindings** — `stored/{provider}/{name}` format, `inject_as`, `credential`/`credentials` mutual exclusion.
179
179
  - **Expression functions** — exactly one of `package`/`function`.
180
180
  - **`input_schema`** — version, field-name pattern, type enum, per-type constraints, `pattern` compilation, `visible_when` predicate + reference resolution, duplicate-name detection, file-ordering lint.