@vaudience/aigentflow-flow-validator 0.18.0 → 0.20.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 +86 -35
- package/README.md +2 -2
- package/dist/cli.js +270 -73
- package/dist/index.cjs +269 -73
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +269 -73
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -436,7 +436,7 @@ function validateBasicStructure(flow, issues) {
|
|
|
436
436
|
// src/spec/aigentflow-spec.json
|
|
437
437
|
var aigentflow_spec_default = {
|
|
438
438
|
$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.
|
|
439
|
+
specVersion: "2.821.0",
|
|
440
440
|
inputSchemaVersion: 1,
|
|
441
441
|
executorSchemes: [
|
|
442
442
|
"a2a",
|
|
@@ -1275,6 +1275,35 @@ var aigentflow_spec_default = {
|
|
|
1275
1275
|
}
|
|
1276
1276
|
}
|
|
1277
1277
|
},
|
|
1278
|
+
complianceResidency: {
|
|
1279
|
+
$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
|
+
code: "compliance_residency_unenforced",
|
|
1281
|
+
euResidencyCodes: ["EU", "DE", "FR", "IE", "NL", "IT", "ES"],
|
|
1282
|
+
regionLaneProviders: [
|
|
1283
|
+
"bedrock",
|
|
1284
|
+
"google",
|
|
1285
|
+
"azure",
|
|
1286
|
+
"openai",
|
|
1287
|
+
"assemblyai",
|
|
1288
|
+
"speechmatics",
|
|
1289
|
+
"deepgram",
|
|
1290
|
+
"mistral"
|
|
1291
|
+
],
|
|
1292
|
+
aiProtocol: "ai",
|
|
1293
|
+
protocolSeparator: "://",
|
|
1294
|
+
regionParam: "region",
|
|
1295
|
+
enforceRegionParam: "enforce_region",
|
|
1296
|
+
baseUrlParamSuffix: "_base_url",
|
|
1297
|
+
enforceRegionTrueStrings: ["true", "1", "yes"],
|
|
1298
|
+
stepFieldFormat: "steps.%s.executor",
|
|
1299
|
+
loopStepFieldFormat: "steps.%s.loop.steps.%s.executor",
|
|
1300
|
+
orchestratorField: "orchestrator.exons",
|
|
1301
|
+
siteStepFormat: "step %q",
|
|
1302
|
+
siteLoopStepFormat: "loop sub-step %q of step %q",
|
|
1303
|
+
siteOrchestrator: "the orchestrator",
|
|
1304
|
+
messageFormat: "%s calls provider %q, whose default endpoint commits to no jurisdiction, but sets neither region nor enforce_region, so nothing pins it to the declared residency %v. compliance.residency is a catalogue eligibility check, not endpoint evidence",
|
|
1305
|
+
suggestion: "Add `region: eu` and `enforce_region: true` to the call (an orchestrator: the same two keys on the orchestrator block). Prove it afterwards: the step record's diagnostics.region is stamped from the host actually dialled."
|
|
1306
|
+
},
|
|
1278
1307
|
examples: {
|
|
1279
1308
|
$comment: "The examples: block. Derived from the reference's constants, never hand-typed: limits, vocabularies, the credential-shaped-literal pattern set and each code's severity. example_ref_blocked_host is listed for completeness but is raised by the reference implementation only; the ports never raise it. See PARITY.md, 'The examples block'.",
|
|
1280
1309
|
limits: {
|
|
@@ -1726,6 +1755,7 @@ var aigentflow_spec_default = {
|
|
|
1726
1755
|
deny_tools: "list<scalar>",
|
|
1727
1756
|
discoverable_tools: "list<scalar>",
|
|
1728
1757
|
enable_signals: "scalar",
|
|
1758
|
+
enforce_region: "scalar",
|
|
1729
1759
|
engram_scope: "scalar",
|
|
1730
1760
|
exons: "scalar",
|
|
1731
1761
|
human_question_timeout: "scalar",
|
|
@@ -1734,6 +1764,7 @@ var aigentflow_spec_default = {
|
|
|
1734
1764
|
max_tool_turns: "scalar",
|
|
1735
1765
|
max_turns: "scalar",
|
|
1736
1766
|
mode: "scalar",
|
|
1767
|
+
region: "scalar",
|
|
1737
1768
|
require_approval_for: "list<scalar>",
|
|
1738
1769
|
timeout_seconds: "scalar",
|
|
1739
1770
|
tool_discovery: "scalar",
|
|
@@ -1941,6 +1972,25 @@ var CREDENTIAL_ENDPOINT_PAIRING = {
|
|
|
1941
1972
|
familyKeySeparator: aigentflow_spec_default.credentialEndpointPairing.familyKeySeparator,
|
|
1942
1973
|
families: aigentflow_spec_default.credentialEndpointPairing.families
|
|
1943
1974
|
};
|
|
1975
|
+
var COMPLIANCE_RESIDENCY = {
|
|
1976
|
+
code: aigentflow_spec_default.complianceResidency.code,
|
|
1977
|
+
euResidencyCodes: aigentflow_spec_default.complianceResidency.euResidencyCodes,
|
|
1978
|
+
regionLaneProviders: aigentflow_spec_default.complianceResidency.regionLaneProviders,
|
|
1979
|
+
aiProtocol: aigentflow_spec_default.complianceResidency.aiProtocol,
|
|
1980
|
+
protocolSeparator: aigentflow_spec_default.complianceResidency.protocolSeparator,
|
|
1981
|
+
regionParam: aigentflow_spec_default.complianceResidency.regionParam,
|
|
1982
|
+
enforceRegionParam: aigentflow_spec_default.complianceResidency.enforceRegionParam,
|
|
1983
|
+
baseUrlParamSuffix: aigentflow_spec_default.complianceResidency.baseUrlParamSuffix,
|
|
1984
|
+
enforceRegionTrueStrings: aigentflow_spec_default.complianceResidency.enforceRegionTrueStrings,
|
|
1985
|
+
stepFieldFormat: aigentflow_spec_default.complianceResidency.stepFieldFormat,
|
|
1986
|
+
loopStepFieldFormat: aigentflow_spec_default.complianceResidency.loopStepFieldFormat,
|
|
1987
|
+
orchestratorField: aigentflow_spec_default.complianceResidency.orchestratorField,
|
|
1988
|
+
siteStepFormat: aigentflow_spec_default.complianceResidency.siteStepFormat,
|
|
1989
|
+
siteLoopStepFormat: aigentflow_spec_default.complianceResidency.siteLoopStepFormat,
|
|
1990
|
+
siteOrchestrator: aigentflow_spec_default.complianceResidency.siteOrchestrator,
|
|
1991
|
+
messageFormat: aigentflow_spec_default.complianceResidency.messageFormat,
|
|
1992
|
+
suggestion: aigentflow_spec_default.complianceResidency.suggestion
|
|
1993
|
+
};
|
|
1944
1994
|
var EXAMPLES = {
|
|
1945
1995
|
limits: aigentflow_spec_default.examples.limits,
|
|
1946
1996
|
idPattern: new RegExp(aigentflow_spec_default.examples.idPattern),
|
|
@@ -3157,6 +3207,17 @@ function validateOrchestratorCampaign(flow, issues, opts) {
|
|
|
3157
3207
|
code: "orchestrator_owner_needs_yield"
|
|
3158
3208
|
});
|
|
3159
3209
|
}
|
|
3210
|
+
if (orch.enforce_region === true) {
|
|
3211
|
+
const region = issues.stringOf(orch, "region", "orchestrator");
|
|
3212
|
+
if (region === null || region.trim() === "") {
|
|
3213
|
+
issues.error({
|
|
3214
|
+
field: "orchestrator.enforce_region",
|
|
3215
|
+
message: "orchestrator: enforce_region requires region (name the region the model must run in, e.g. region: eu)",
|
|
3216
|
+
code: "orchestrator_enforce_region_needs_region",
|
|
3217
|
+
suggestion: "Add `region: eu` (or the region the model must run in), or drop enforce_region"
|
|
3218
|
+
});
|
|
3219
|
+
}
|
|
3220
|
+
}
|
|
3160
3221
|
if (Array.isArray(orch.triggers)) {
|
|
3161
3222
|
orch.triggers.forEach((rawTrigger, i) => {
|
|
3162
3223
|
if (!isRecord(rawTrigger)) return;
|
|
@@ -5289,6 +5350,141 @@ function warnOrchestratorToolWithheld(flow, orch, report, issues) {
|
|
|
5289
5350
|
}
|
|
5290
5351
|
}
|
|
5291
5352
|
|
|
5353
|
+
// src/validators/complianceResidency.ts
|
|
5354
|
+
function own2(m, key) {
|
|
5355
|
+
if (!isRecord(m) || !Object.prototype.hasOwnProperty.call(m, key)) return void 0;
|
|
5356
|
+
return m[key];
|
|
5357
|
+
}
|
|
5358
|
+
function sprintf(format, ...args) {
|
|
5359
|
+
let i = 0;
|
|
5360
|
+
return format.replace(/%[sqv]/g, (verb) => {
|
|
5361
|
+
const arg = args[i++] ?? "";
|
|
5362
|
+
return verb === "%q" ? JSON.stringify(arg) : arg;
|
|
5363
|
+
});
|
|
5364
|
+
}
|
|
5365
|
+
function literalOrEmpty(s) {
|
|
5366
|
+
const t = goTrimSpace2(s);
|
|
5367
|
+
return t.includes(TEMPLATE_ACTION_OPEN) ? "" : t;
|
|
5368
|
+
}
|
|
5369
|
+
function euOnlyResidency(flow, issues) {
|
|
5370
|
+
const raw = own2(own2(flow, "compliance"), "residency");
|
|
5371
|
+
if (!isArray(raw)) return null;
|
|
5372
|
+
const codes = [];
|
|
5373
|
+
for (let i = 0; i < raw.length; i++) {
|
|
5374
|
+
if (raw[i] === null) continue;
|
|
5375
|
+
const code = issues.stringAt(raw[i], `compliance.residency[${i}]`);
|
|
5376
|
+
if (code === null) return null;
|
|
5377
|
+
codes.push(code);
|
|
5378
|
+
}
|
|
5379
|
+
if (codes.length === 0) return null;
|
|
5380
|
+
const eu = new Set(COMPLIANCE_RESIDENCY.euResidencyCodes);
|
|
5381
|
+
return codes.every((c) => eu.has(goTrimSpace2(c).toUpperCase())) ? codes : null;
|
|
5382
|
+
}
|
|
5383
|
+
function aiProvider(executor) {
|
|
5384
|
+
const prefix = COMPLIANCE_RESIDENCY.aiProtocol + COMPLIANCE_RESIDENCY.protocolSeparator;
|
|
5385
|
+
if (!executor.startsWith(prefix)) return "";
|
|
5386
|
+
const rest = executor.slice(prefix.length);
|
|
5387
|
+
const slash = rest.indexOf("/");
|
|
5388
|
+
const provider = slash > 0 ? rest.slice(0, slash) : "";
|
|
5389
|
+
return provider.includes(TEMPLATE_ACTION_OPEN) ? "" : provider;
|
|
5390
|
+
}
|
|
5391
|
+
function isRegionLane(provider) {
|
|
5392
|
+
const literal = literalOrEmpty(provider);
|
|
5393
|
+
return literal !== "" && COMPLIANCE_RESIDENCY.regionLaneProviders.includes(literal.toLowerCase());
|
|
5394
|
+
}
|
|
5395
|
+
function nonBlankString(m, key) {
|
|
5396
|
+
const v = own2(m, key);
|
|
5397
|
+
return typeof v === "string" && goTrimSpace2(v) !== "";
|
|
5398
|
+
}
|
|
5399
|
+
function queryEnforcesRegion(query) {
|
|
5400
|
+
const v = own2(query, COMPLIANCE_RESIDENCY.enforceRegionParam);
|
|
5401
|
+
if (typeof v === "boolean") return v;
|
|
5402
|
+
return typeof v === "string" && COMPLIANCE_RESIDENCY.enforceRegionTrueStrings.includes(v);
|
|
5403
|
+
}
|
|
5404
|
+
function flowDirectsEndpoint(flow, provider, issues) {
|
|
5405
|
+
const key = provider.toLowerCase();
|
|
5406
|
+
const inst = own2(own2(flow, "executor_config"), key);
|
|
5407
|
+
if (!isRecord(inst)) return false;
|
|
5408
|
+
const baseUrl = issues.stringOf(inst, "base_url", `executor_config.${key}`);
|
|
5409
|
+
return baseUrl !== null && goTrimSpace2(baseUrl) !== "";
|
|
5410
|
+
}
|
|
5411
|
+
function callPinsEndpoint(flow, provider, query, issues) {
|
|
5412
|
+
if (nonBlankString(query, COMPLIANCE_RESIDENCY.regionParam)) return true;
|
|
5413
|
+
if (queryEnforcesRegion(query)) return true;
|
|
5414
|
+
if (nonBlankString(query, provider.toLowerCase() + COMPLIANCE_RESIDENCY.baseUrlParamSuffix)) return true;
|
|
5415
|
+
return flowDirectsEndpoint(flow, provider, issues);
|
|
5416
|
+
}
|
|
5417
|
+
function validateResidencyEnforcement(flow, issues) {
|
|
5418
|
+
const residency = euOnlyResidency(flow, issues);
|
|
5419
|
+
if (residency === null) return;
|
|
5420
|
+
const warn = (site, field, stepId, provider) => {
|
|
5421
|
+
issues.warn({
|
|
5422
|
+
field,
|
|
5423
|
+
...stepId === void 0 ? {} : { stepId },
|
|
5424
|
+
code: COMPLIANCE_RESIDENCY.code,
|
|
5425
|
+
message: sprintf(COMPLIANCE_RESIDENCY.messageFormat, site, provider, `[${residency.join(" ")}]`),
|
|
5426
|
+
suggestion: COMPLIANCE_RESIDENCY.suggestion
|
|
5427
|
+
});
|
|
5428
|
+
};
|
|
5429
|
+
const steps = flow.steps;
|
|
5430
|
+
if (isRecord(steps)) {
|
|
5431
|
+
for (const stepId of Object.keys(steps).sort()) {
|
|
5432
|
+
const step = steps[stepId];
|
|
5433
|
+
if (!isRecord(step)) continue;
|
|
5434
|
+
const calls = [
|
|
5435
|
+
{
|
|
5436
|
+
executor: issues.stringOf(step, "executor", `steps.${stepId}`) ?? "",
|
|
5437
|
+
query: own2(step, "query"),
|
|
5438
|
+
subId: null
|
|
5439
|
+
}
|
|
5440
|
+
];
|
|
5441
|
+
for (const sub of loopSubStepsOfStep(stepId, step)) {
|
|
5442
|
+
calls.push({
|
|
5443
|
+
executor: issues.stringOf(sub.raw, "executor", sub.basePath) ?? "",
|
|
5444
|
+
query: own2(sub.raw, "query"),
|
|
5445
|
+
subId: issues.stringOf(sub.raw, "id", sub.basePath) ?? ""
|
|
5446
|
+
});
|
|
5447
|
+
}
|
|
5448
|
+
for (const call of calls) {
|
|
5449
|
+
const provider = aiProvider(call.executor);
|
|
5450
|
+
if (!isRegionLane(provider)) continue;
|
|
5451
|
+
if (callPinsEndpoint(flow, provider, call.query, issues)) continue;
|
|
5452
|
+
if (call.subId === null) {
|
|
5453
|
+
warn(
|
|
5454
|
+
sprintf(COMPLIANCE_RESIDENCY.siteStepFormat, stepId),
|
|
5455
|
+
sprintf(COMPLIANCE_RESIDENCY.stepFieldFormat, stepId),
|
|
5456
|
+
stepId,
|
|
5457
|
+
provider
|
|
5458
|
+
);
|
|
5459
|
+
} else {
|
|
5460
|
+
warn(
|
|
5461
|
+
sprintf(COMPLIANCE_RESIDENCY.siteLoopStepFormat, call.subId, stepId),
|
|
5462
|
+
sprintf(COMPLIANCE_RESIDENCY.loopStepFieldFormat, stepId, call.subId),
|
|
5463
|
+
stepId,
|
|
5464
|
+
provider
|
|
5465
|
+
);
|
|
5466
|
+
}
|
|
5467
|
+
}
|
|
5468
|
+
}
|
|
5469
|
+
}
|
|
5470
|
+
warnOrchestrator(flow, issues, warn);
|
|
5471
|
+
}
|
|
5472
|
+
function warnOrchestrator(flow, issues, warn) {
|
|
5473
|
+
const orch = flow.orchestrator;
|
|
5474
|
+
if (!isRecord(orch)) return;
|
|
5475
|
+
const source = issues.stringOf(orch, "exons", "orchestrator");
|
|
5476
|
+
if (source === null || source === "") return;
|
|
5477
|
+
const report = inspectExonsFrontmatter(source);
|
|
5478
|
+
if (report.parseError !== null || !report.specRead || !report.hasSpec) return;
|
|
5479
|
+
const provider = literalOrEmpty(report.provider);
|
|
5480
|
+
if (!isRegionLane(provider)) return;
|
|
5481
|
+
const region = issues.stringOf(orch, "region", "orchestrator");
|
|
5482
|
+
if (region !== null && goTrimSpace2(region) !== "") return;
|
|
5483
|
+
if (orch.enforce_region === true) return;
|
|
5484
|
+
if (flowDirectsEndpoint(flow, provider, issues)) return;
|
|
5485
|
+
warn(COMPLIANCE_RESIDENCY.siteOrchestrator, COMPLIANCE_RESIDENCY.orchestratorField, void 0, provider);
|
|
5486
|
+
}
|
|
5487
|
+
|
|
5292
5488
|
// src/validators/examples.ts
|
|
5293
5489
|
var KEY_EXAMPLES = "examples";
|
|
5294
5490
|
var VISIBILITY_PUBLIC = "public";
|
|
@@ -5366,7 +5562,7 @@ function num(n) {
|
|
|
5366
5562
|
function has(o, key) {
|
|
5367
5563
|
return Object.prototype.hasOwnProperty.call(o, key) && o[key] !== null && o[key] !== void 0;
|
|
5368
5564
|
}
|
|
5369
|
-
function
|
|
5565
|
+
function own3(o, key) {
|
|
5370
5566
|
return Object.prototype.hasOwnProperty.call(o, key) ? o[key] : void 0;
|
|
5371
5567
|
}
|
|
5372
5568
|
function setOwn(o, key, value) {
|
|
@@ -5418,7 +5614,7 @@ function mergePatch(target, patch, depth) {
|
|
|
5418
5614
|
continue;
|
|
5419
5615
|
}
|
|
5420
5616
|
if (isRecord(pv)) {
|
|
5421
|
-
const current =
|
|
5617
|
+
const current = own3(target, k);
|
|
5422
5618
|
const existing = isRecord(current) ? deepCopy(current, 0) : {};
|
|
5423
5619
|
mergePatch(existing, pv, depth + 1);
|
|
5424
5620
|
setOwn(target, k, existing);
|
|
@@ -5438,7 +5634,7 @@ function asFileRef(v) {
|
|
|
5438
5634
|
if (keys.length === 0) return null;
|
|
5439
5635
|
for (const k of keys) if (!FILE_REF_KEYS.has(k)) return null;
|
|
5440
5636
|
const str = (k) => {
|
|
5441
|
-
const x =
|
|
5637
|
+
const x = own3(v, k);
|
|
5442
5638
|
return typeof x === "string" ? x : "";
|
|
5443
5639
|
};
|
|
5444
5640
|
return {
|
|
@@ -5506,7 +5702,7 @@ var Walker = class _Walker {
|
|
|
5506
5702
|
`the examples block is ${size} bytes; the limit is ${L.maxBlockBytes}. Move large inputs or target outputs to a file reference (https URL).`
|
|
5507
5703
|
);
|
|
5508
5704
|
}
|
|
5509
|
-
const allHeld = examples.every((e) => isRecord(e) &&
|
|
5705
|
+
const allHeld = examples.every((e) => isRecord(e) && own3(e, "hold_out") === true);
|
|
5510
5706
|
if (allHeld) {
|
|
5511
5707
|
this.warn(
|
|
5512
5708
|
"examples_all_held_out",
|
|
@@ -5594,9 +5790,9 @@ var Walker = class _Walker {
|
|
|
5594
5790
|
`example ${q(label)} notes are ${notesN} characters; the limit is ${L.maxNotesRunes}`
|
|
5595
5791
|
);
|
|
5596
5792
|
}
|
|
5597
|
-
this.validateTags(path, label,
|
|
5598
|
-
this.validateWeight(`${path}.weight`, label, numberOf(
|
|
5599
|
-
const minScore = numberOf(
|
|
5793
|
+
this.validateTags(path, label, own3(ex, "tags"));
|
|
5794
|
+
this.validateWeight(`${path}.weight`, label, numberOf(own3(ex, "weight")));
|
|
5795
|
+
const minScore = numberOf(own3(ex, "min_score"));
|
|
5600
5796
|
if (minScore !== void 0 && !scoreInRange(minScore)) {
|
|
5601
5797
|
this.err(
|
|
5602
5798
|
"example_min_score_range",
|
|
@@ -5612,7 +5808,7 @@ var Walker = class _Walker {
|
|
|
5612
5808
|
`example ${q(label)} has side_effects ${q(sideEffects)}; use refuse or allow`
|
|
5613
5809
|
);
|
|
5614
5810
|
}
|
|
5615
|
-
const holdOut =
|
|
5811
|
+
const holdOut = own3(ex, "hold_out") === true;
|
|
5616
5812
|
if (holdOut && this.isPublic()) {
|
|
5617
5813
|
this.err(
|
|
5618
5814
|
"example_holdout_in_public_flow",
|
|
@@ -5635,17 +5831,17 @@ var Walker = class _Walker {
|
|
|
5635
5831
|
`example ${q(label)} has neither input nor input_ref; use input: {} for a flow that takes no input`
|
|
5636
5832
|
);
|
|
5637
5833
|
}
|
|
5638
|
-
const rawInput =
|
|
5834
|
+
const rawInput = own3(ex, "input");
|
|
5639
5835
|
const baseInput = isRecord(rawInput) ? rawInput : null;
|
|
5640
5836
|
if (baseInput !== null) {
|
|
5641
5837
|
this.checkInput(`${path}.input`, label, baseInput, "example_input_invalid");
|
|
5642
5838
|
}
|
|
5643
|
-
const rawInputRef =
|
|
5839
|
+
const rawInputRef = own3(ex, "input_ref");
|
|
5644
5840
|
if (isRecord(rawInputRef)) {
|
|
5645
5841
|
this.validateFileRef(`${path}.input_ref`, this.fileRefOf(rawInputRef, `${path}.input_ref`));
|
|
5646
5842
|
}
|
|
5647
5843
|
let binaryOnly = false;
|
|
5648
|
-
const expected =
|
|
5844
|
+
const expected = own3(ex, "expected");
|
|
5649
5845
|
if (!has(ex, "expected")) {
|
|
5650
5846
|
this.err(
|
|
5651
5847
|
"example_expected_missing",
|
|
@@ -5664,7 +5860,7 @@ var Walker = class _Walker {
|
|
|
5664
5860
|
"this example can be run but not scored"
|
|
5665
5861
|
);
|
|
5666
5862
|
}
|
|
5667
|
-
this.validateCheckpoints(path, label,
|
|
5863
|
+
this.validateCheckpoints(path, label, own3(ex, "checkpoints"));
|
|
5668
5864
|
this.validateVariants(i, path, label, ex, hasInput, baseInput);
|
|
5669
5865
|
this.scanSecretLike(i, path, ex);
|
|
5670
5866
|
}
|
|
@@ -5724,8 +5920,8 @@ var Walker = class _Walker {
|
|
|
5724
5920
|
);
|
|
5725
5921
|
return;
|
|
5726
5922
|
}
|
|
5727
|
-
const schema =
|
|
5728
|
-
const query =
|
|
5923
|
+
const schema = own3(this.flow, "input_schema");
|
|
5924
|
+
const query = own3(this.flow, "query");
|
|
5729
5925
|
if (isRecord(schema)) {
|
|
5730
5926
|
this.checkInputAgainstSchema(field, label, input, schema, code);
|
|
5731
5927
|
} else if (isRecord(query) && Object.keys(query).length > 0) {
|
|
@@ -5793,29 +5989,29 @@ var Walker = class _Walker {
|
|
|
5793
5989
|
}
|
|
5794
5990
|
}
|
|
5795
5991
|
schemaFields(schema) {
|
|
5796
|
-
const raw =
|
|
5992
|
+
const raw = own3(schema, "fields");
|
|
5797
5993
|
if (!Array.isArray(raw)) return [];
|
|
5798
5994
|
const out = [];
|
|
5799
5995
|
raw.forEach((f, i) => {
|
|
5800
5996
|
if (!isRecord(f)) return;
|
|
5801
5997
|
const base = `input_schema.fields[${i}]`;
|
|
5802
|
-
const enumRaw =
|
|
5998
|
+
const enumRaw = own3(f, "enum");
|
|
5803
5999
|
const enumValues = [];
|
|
5804
6000
|
if (Array.isArray(enumRaw)) {
|
|
5805
6001
|
enumRaw.forEach((e, j) => {
|
|
5806
6002
|
enumValues.push(e === null ? "" : this.issues.stringAt(e, `${base}.enum[${j}]`) ?? "");
|
|
5807
6003
|
});
|
|
5808
6004
|
}
|
|
5809
|
-
const vw =
|
|
6005
|
+
const vw = own3(f, "visible_when");
|
|
5810
6006
|
out.push({
|
|
5811
6007
|
name: this.s(f, "name", base),
|
|
5812
6008
|
type: this.s(f, "type", base),
|
|
5813
|
-
required:
|
|
6009
|
+
required: own3(f, "required") === true,
|
|
5814
6010
|
enumValues,
|
|
5815
6011
|
visibleWhen: isRecord(vw) ? {
|
|
5816
6012
|
field: this.s(vw, "field", `${base}.visible_when`),
|
|
5817
|
-
equals:
|
|
5818
|
-
in: Array.isArray(
|
|
6013
|
+
equals: own3(vw, "equals"),
|
|
6014
|
+
in: Array.isArray(own3(vw, "in")) ? own3(vw, "in") : []
|
|
5819
6015
|
} : null
|
|
5820
6016
|
});
|
|
5821
6017
|
});
|
|
@@ -5826,7 +6022,7 @@ var Walker = class _Walker {
|
|
|
5826
6022
|
const failures = [];
|
|
5827
6023
|
for (const f of fields) {
|
|
5828
6024
|
if (!isVisible(f, runInput)) continue;
|
|
5829
|
-
const raw =
|
|
6025
|
+
const raw = own3(runInput, f.name);
|
|
5830
6026
|
if (raw === void 0 || raw === null) {
|
|
5831
6027
|
if (f.required) {
|
|
5832
6028
|
failures.push({
|
|
@@ -5937,7 +6133,7 @@ var Walker = class _Walker {
|
|
|
5937
6133
|
/** The names `fields` / `exact` may use, and whether such a list is declared at all. */
|
|
5938
6134
|
outputNamesFor(kind, stepId) {
|
|
5939
6135
|
if (kind === "final") {
|
|
5940
|
-
const out =
|
|
6136
|
+
const out = own3(this.flow, "output");
|
|
5941
6137
|
const names2 = [];
|
|
5942
6138
|
if (Array.isArray(out)) {
|
|
5943
6139
|
out.forEach((item, i) => {
|
|
@@ -5946,10 +6142,10 @@ var Walker = class _Walker {
|
|
|
5946
6142
|
}
|
|
5947
6143
|
return { names: names2, declared: names2.length > 0 };
|
|
5948
6144
|
}
|
|
5949
|
-
const steps =
|
|
5950
|
-
const step = isRecord(steps) ?
|
|
5951
|
-
const schema = isRecord(step) ?
|
|
5952
|
-
const fields = isRecord(schema) ?
|
|
6145
|
+
const steps = own3(this.flow, "steps");
|
|
6146
|
+
const step = isRecord(steps) ? own3(steps, stepId) : void 0;
|
|
6147
|
+
const schema = isRecord(step) ? own3(step, "output_schema") : void 0;
|
|
6148
|
+
const fields = isRecord(schema) ? own3(schema, "fields") : void 0;
|
|
5953
6149
|
if (!Array.isArray(fields) || fields.length === 0) return { names: [], declared: false };
|
|
5954
6150
|
const names = [];
|
|
5955
6151
|
fields.forEach((f, i) => {
|
|
@@ -5968,8 +6164,8 @@ var Walker = class _Walker {
|
|
|
5968
6164
|
);
|
|
5969
6165
|
}
|
|
5970
6166
|
const noOutput = status === STATUS_FAILED || status === STATUS_PAUSED_FOR_HUMAN;
|
|
5971
|
-
const fieldsRec =
|
|
5972
|
-
const exactRec =
|
|
6167
|
+
const fieldsRec = own3(e, "fields");
|
|
6168
|
+
const exactRec = own3(e, "exact");
|
|
5973
6169
|
const fieldKeys = isRecord(fieldsRec) ? sortedKeys(fieldsRec) : [];
|
|
5974
6170
|
const exactKeys = isRecord(exactRec) ? sortedKeys(exactRec) : [];
|
|
5975
6171
|
const hasFields = fieldKeys.length > 0;
|
|
@@ -5987,7 +6183,7 @@ var Walker = class _Walker {
|
|
|
5987
6183
|
`a case that ends in ${status} has no output to match; remove fields and exact, or expect status completed`
|
|
5988
6184
|
);
|
|
5989
6185
|
}
|
|
5990
|
-
const minScore = numberOf(
|
|
6186
|
+
const minScore = numberOf(own3(e, "min_score"));
|
|
5991
6187
|
if (minScore !== void 0 && !scoreInRange(minScore)) {
|
|
5992
6188
|
this.err(
|
|
5993
6189
|
"example_min_score_range",
|
|
@@ -6014,7 +6210,7 @@ var Walker = class _Walker {
|
|
|
6014
6210
|
}
|
|
6015
6211
|
}
|
|
6016
6212
|
}
|
|
6017
|
-
const reference =
|
|
6213
|
+
const reference = own3(e, "reference");
|
|
6018
6214
|
if (isRecord(reference)) this.validateReference(`${path}.reference`, reference);
|
|
6019
6215
|
const { names, declared } = this.outputNamesFor(kind, stepId);
|
|
6020
6216
|
const checkKey = (field, key) => {
|
|
@@ -6041,12 +6237,12 @@ var Walker = class _Walker {
|
|
|
6041
6237
|
for (const key of fieldKeys) {
|
|
6042
6238
|
const fpath = `${path}.fields.${key}`;
|
|
6043
6239
|
checkKey(fpath, key);
|
|
6044
|
-
const matcher =
|
|
6240
|
+
const matcher = own3(fieldsRec, key);
|
|
6045
6241
|
if (isRecord(matcher)) this.validateFieldMatcher(fpath, matcher);
|
|
6046
6242
|
}
|
|
6047
6243
|
for (const key of exactKeys) checkKey(`${path}.exact.${key}`, key);
|
|
6048
|
-
const mustNot = this.stringList(
|
|
6049
|
-
const mustNotContain = this.stringList(
|
|
6244
|
+
const mustNot = this.stringList(own3(e, "must_not"), `${path}.must_not`);
|
|
6245
|
+
const mustNotContain = this.stringList(own3(e, "must_not_contain"), `${path}.must_not_contain`);
|
|
6050
6246
|
const mustNotMsg = `must_not takes up to ${L.maxMustNot} statements of at most ${L.maxMustNotRunes} characters each, none empty`;
|
|
6051
6247
|
if (mustNot.length > L.maxMustNot) {
|
|
6052
6248
|
this.err("example_must_not_invalid", `${path}.must_not`, mustNotMsg);
|
|
@@ -6067,9 +6263,9 @@ var Walker = class _Walker {
|
|
|
6067
6263
|
}
|
|
6068
6264
|
});
|
|
6069
6265
|
for (const key of fieldKeys) {
|
|
6070
|
-
const matcher =
|
|
6266
|
+
const matcher = own3(fieldsRec, key);
|
|
6071
6267
|
if (!isRecord(matcher)) continue;
|
|
6072
|
-
const eq =
|
|
6268
|
+
const eq = own3(matcher, "equals");
|
|
6073
6269
|
if (typeof eq !== "string" || eq === "") continue;
|
|
6074
6270
|
for (const banned of mustNotContain) {
|
|
6075
6271
|
if (banned !== "" && equalFold(banned, eq)) {
|
|
@@ -6129,7 +6325,7 @@ var Walker = class _Walker {
|
|
|
6129
6325
|
}
|
|
6130
6326
|
return;
|
|
6131
6327
|
}
|
|
6132
|
-
const refRec =
|
|
6328
|
+
const refRec = own3(r, "ref");
|
|
6133
6329
|
if (!isRecord(refRec)) return;
|
|
6134
6330
|
const ref = this.fileRefOf(refRec, `${path}.ref`);
|
|
6135
6331
|
this.validateFileRef(`${path}.ref`, ref);
|
|
@@ -6142,22 +6338,22 @@ var Walker = class _Walker {
|
|
|
6142
6338
|
}
|
|
6143
6339
|
}
|
|
6144
6340
|
expectationIsBinaryReferenceOnly(e, path) {
|
|
6145
|
-
const reference =
|
|
6146
|
-
const refRec = isRecord(reference) ?
|
|
6341
|
+
const reference = own3(e, "reference");
|
|
6342
|
+
const refRec = isRecord(reference) ? own3(reference, "ref") : void 0;
|
|
6147
6343
|
if (!isRecord(refRec)) return false;
|
|
6148
6344
|
const mediaType = this.s(refRec, "media_type", `${path}.reference.ref`);
|
|
6149
6345
|
if (mediaType === "" || isTextualMediaType(mediaType)) return false;
|
|
6150
6346
|
const status = this.s(e, "status", path);
|
|
6151
|
-
const fields =
|
|
6152
|
-
const exact =
|
|
6153
|
-
return this.s(e, "rubric", path) === "" && !(isRecord(fields) && Object.keys(fields).length > 0) && !(isRecord(exact) && Object.keys(exact).length > 0) && this.stringList(
|
|
6347
|
+
const fields = own3(e, "fields");
|
|
6348
|
+
const exact = own3(e, "exact");
|
|
6349
|
+
return this.s(e, "rubric", path) === "" && !(isRecord(fields) && Object.keys(fields).length > 0) && !(isRecord(exact) && Object.keys(exact).length > 0) && this.stringList(own3(e, "must_not"), `${path}.must_not`).length === 0 && this.stringList(own3(e, "must_not_contain"), `${path}.must_not_contain`).length === 0 && (status === "" || status === STATUS_COMPLETED);
|
|
6154
6350
|
}
|
|
6155
6351
|
validateFieldMatcher(path, m) {
|
|
6156
6352
|
const L = EXAMPLES.limits;
|
|
6157
6353
|
const matches = this.s(m, "matches", path);
|
|
6158
|
-
const oneOf =
|
|
6159
|
-
const approx = numberOf(
|
|
6160
|
-
const tolerance = numberOf(
|
|
6354
|
+
const oneOf = own3(m, "one_of");
|
|
6355
|
+
const approx = numberOf(own3(m, "approx"));
|
|
6356
|
+
const tolerance = numberOf(own3(m, "tolerance"));
|
|
6161
6357
|
let operators = 0;
|
|
6162
6358
|
if (has(m, "equals")) operators++;
|
|
6163
6359
|
if (this.s(m, "contains", path) !== "") operators++;
|
|
@@ -6165,7 +6361,7 @@ var Walker = class _Walker {
|
|
|
6165
6361
|
if (matches !== "") operators++;
|
|
6166
6362
|
if (Array.isArray(oneOf) && oneOf.length > 0) operators++;
|
|
6167
6363
|
if (approx !== void 0) operators++;
|
|
6168
|
-
if (typeof
|
|
6364
|
+
if (typeof own3(m, "present") === "boolean") operators++;
|
|
6169
6365
|
if (operators !== 1) {
|
|
6170
6366
|
this.err(
|
|
6171
6367
|
"example_field_matcher_invalid",
|
|
@@ -6192,12 +6388,12 @@ var Walker = class _Walker {
|
|
|
6192
6388
|
// -- checkpoints ------------------------------------------------------
|
|
6193
6389
|
validateCheckpoints(path, label, cps) {
|
|
6194
6390
|
if (!isRecord(cps) || Object.keys(cps).length === 0) return;
|
|
6195
|
-
const stepsRaw =
|
|
6391
|
+
const stepsRaw = own3(this.flow, "steps");
|
|
6196
6392
|
const steps = isRecord(stepsRaw) ? stepsRaw : {};
|
|
6197
6393
|
const stepIds = sortedKeys(steps);
|
|
6198
6394
|
for (const id of sortedKeys(cps)) {
|
|
6199
6395
|
const cpath = `${path}.checkpoints.${id}`;
|
|
6200
|
-
const step =
|
|
6396
|
+
const step = own3(steps, id);
|
|
6201
6397
|
if (!isRecord(step)) {
|
|
6202
6398
|
this.err(
|
|
6203
6399
|
"example_checkpoint_step_unknown",
|
|
@@ -6207,7 +6403,7 @@ var Walker = class _Walker {
|
|
|
6207
6403
|
);
|
|
6208
6404
|
continue;
|
|
6209
6405
|
}
|
|
6210
|
-
const next =
|
|
6406
|
+
const next = own3(step, "next");
|
|
6211
6407
|
if (has(step, "for_each") || has(step, "loop") || isRecord(next) && has(next, "parallel")) {
|
|
6212
6408
|
this.err(
|
|
6213
6409
|
"example_checkpoint_step_composite",
|
|
@@ -6216,7 +6412,7 @@ var Walker = class _Walker {
|
|
|
6216
6412
|
);
|
|
6217
6413
|
continue;
|
|
6218
6414
|
}
|
|
6219
|
-
const cp =
|
|
6415
|
+
const cp = own3(cps, id);
|
|
6220
6416
|
if (cp === null || cp === void 0) {
|
|
6221
6417
|
this.validateExpectation(cpath, label, {}, "checkpoint", id);
|
|
6222
6418
|
} else if (isRecord(cp)) {
|
|
@@ -6227,7 +6423,7 @@ var Walker = class _Walker {
|
|
|
6227
6423
|
// -- variants ---------------------------------------------------------
|
|
6228
6424
|
validateVariants(i, path, label, ex, hasInput, baseInput) {
|
|
6229
6425
|
const L = EXAMPLES.limits;
|
|
6230
|
-
const variants =
|
|
6426
|
+
const variants = own3(ex, "variants");
|
|
6231
6427
|
if (!Array.isArray(variants) || variants.length === 0) return;
|
|
6232
6428
|
if (variants.length > L.maxVariants) {
|
|
6233
6429
|
this.err(
|
|
@@ -6290,8 +6486,8 @@ var Walker = class _Walker {
|
|
|
6290
6486
|
`example ${q(both)} notes are ${provN} characters; the limit is ${L.maxProvenanceRunes}`
|
|
6291
6487
|
);
|
|
6292
6488
|
}
|
|
6293
|
-
this.validateWeight(`${vpath}.weight`, both, numberOf(
|
|
6294
|
-
const patch =
|
|
6489
|
+
this.validateWeight(`${vpath}.weight`, both, numberOf(own3(v, "weight")));
|
|
6490
|
+
const patch = own3(v, "input_patch");
|
|
6295
6491
|
const patchRec = isRecord(patch) ? patch : null;
|
|
6296
6492
|
const hasPatch = patchRec !== null && Object.keys(patchRec).length > 0;
|
|
6297
6493
|
const hasRef = has(v, "input_ref");
|
|
@@ -6308,7 +6504,7 @@ var Walker = class _Walker {
|
|
|
6308
6504
|
`variant ${q(vlabel)} of example ${q(label)} changes neither the input nor the expectation`
|
|
6309
6505
|
);
|
|
6310
6506
|
}
|
|
6311
|
-
const refRec =
|
|
6507
|
+
const refRec = own3(v, "input_ref");
|
|
6312
6508
|
if (isRecord(refRec)) {
|
|
6313
6509
|
this.validateFileRef(`${vpath}.input_ref`, this.fileRefOf(refRec, `${vpath}.input_ref`));
|
|
6314
6510
|
}
|
|
@@ -6323,7 +6519,7 @@ var Walker = class _Walker {
|
|
|
6323
6519
|
this.checkVariantPatch(vpath, label, vlabel, baseInput, patchRec);
|
|
6324
6520
|
}
|
|
6325
6521
|
}
|
|
6326
|
-
const expected =
|
|
6522
|
+
const expected = own3(v, "expected");
|
|
6327
6523
|
if (isRecord(expected)) {
|
|
6328
6524
|
this.validateExpectation(`${vpath}.expected`, both, expected, "final", "");
|
|
6329
6525
|
}
|
|
@@ -6334,11 +6530,11 @@ var Walker = class _Walker {
|
|
|
6334
6530
|
* itself supplies. A null patch value deleting a secret is fine.
|
|
6335
6531
|
*/
|
|
6336
6532
|
checkVariantPatch(vpath, label, vlabel, base, patch) {
|
|
6337
|
-
const schema =
|
|
6533
|
+
const schema = own3(this.flow, "input_schema");
|
|
6338
6534
|
if (isRecord(schema)) {
|
|
6339
6535
|
for (const f of this.schemaFields(schema)) {
|
|
6340
6536
|
if (f.type !== TYPE_SECRET) continue;
|
|
6341
|
-
const val =
|
|
6537
|
+
const val = own3(patch, f.name);
|
|
6342
6538
|
if (val !== void 0 && val !== null) {
|
|
6343
6539
|
this.err(
|
|
6344
6540
|
"example_secret_value",
|
|
@@ -6378,18 +6574,18 @@ var Walker = class _Walker {
|
|
|
6378
6574
|
emit(`${path}.title`, this.s(ex, "title", path));
|
|
6379
6575
|
emit(`${path}.guidance`, this.s(ex, "guidance", path));
|
|
6380
6576
|
emit(`${path}.notes`, this.s(ex, "notes", path));
|
|
6381
|
-
const input =
|
|
6577
|
+
const input = own3(ex, "input");
|
|
6382
6578
|
if (isRecord(input)) scanLeaves(`${path}.input`, input, 0, emit);
|
|
6383
|
-
const expected =
|
|
6579
|
+
const expected = own3(ex, "expected");
|
|
6384
6580
|
if (isRecord(expected)) this.scanExpectationLeaves(`${path}.expected`, expected, emit);
|
|
6385
|
-
const cps =
|
|
6581
|
+
const cps = own3(ex, "checkpoints");
|
|
6386
6582
|
if (isRecord(cps)) {
|
|
6387
6583
|
for (const id of sortedKeys(cps)) {
|
|
6388
|
-
const cp =
|
|
6584
|
+
const cp = own3(cps, id);
|
|
6389
6585
|
if (isRecord(cp)) this.scanExpectationLeaves(`${path}.checkpoints.${id}`, cp, emit);
|
|
6390
6586
|
}
|
|
6391
6587
|
}
|
|
6392
|
-
const variants =
|
|
6588
|
+
const variants = own3(ex, "variants");
|
|
6393
6589
|
if (Array.isArray(variants)) {
|
|
6394
6590
|
variants.forEach((v, j) => {
|
|
6395
6591
|
if (!isRecord(v)) return;
|
|
@@ -6397,34 +6593,34 @@ var Walker = class _Walker {
|
|
|
6397
6593
|
emit(`${vpath}.title`, this.s(v, "title", vpath));
|
|
6398
6594
|
emit(`${vpath}.guidance`, this.s(v, "guidance", vpath));
|
|
6399
6595
|
emit(`${vpath}.provenance`, this.s(v, "provenance", vpath));
|
|
6400
|
-
scanLeaves(`${vpath}.input_patch`,
|
|
6401
|
-
const vexp =
|
|
6596
|
+
scanLeaves(`${vpath}.input_patch`, own3(v, "input_patch"), 0, emit);
|
|
6597
|
+
const vexp = own3(v, "expected");
|
|
6402
6598
|
if (isRecord(vexp)) this.scanExpectationLeaves(`${vpath}.expected`, vexp, emit);
|
|
6403
6599
|
});
|
|
6404
6600
|
}
|
|
6405
6601
|
}
|
|
6406
6602
|
scanExpectationLeaves(path, e, emit) {
|
|
6407
6603
|
emit(`${path}.rubric`, this.s(e, "rubric", path));
|
|
6408
|
-
const reference =
|
|
6604
|
+
const reference = own3(e, "reference");
|
|
6409
6605
|
if (isRecord(reference)) {
|
|
6410
6606
|
emit(`${path}.reference.text`, this.s(reference, "text", `${path}.reference`));
|
|
6411
6607
|
}
|
|
6412
|
-
this.stringList(
|
|
6608
|
+
this.stringList(own3(e, "must_not"), `${path}.must_not`).forEach(
|
|
6413
6609
|
(s, j) => emit(`${path}.must_not[${j}]`, s)
|
|
6414
6610
|
);
|
|
6415
|
-
this.stringList(
|
|
6611
|
+
this.stringList(own3(e, "must_not_contain"), `${path}.must_not_contain`).forEach(
|
|
6416
6612
|
(s, j) => emit(`${path}.must_not_contain[${j}]`, s)
|
|
6417
6613
|
);
|
|
6418
|
-
const fields =
|
|
6614
|
+
const fields = own3(e, "fields");
|
|
6419
6615
|
if (isRecord(fields)) {
|
|
6420
6616
|
for (const key of sortedKeys(fields)) {
|
|
6421
|
-
const m =
|
|
6617
|
+
const m = own3(fields, key);
|
|
6422
6618
|
if (!isRecord(m)) continue;
|
|
6423
6619
|
const fpath = `${path}.fields.${key}`;
|
|
6424
|
-
scanLeaves(`${fpath}.equals`,
|
|
6620
|
+
scanLeaves(`${fpath}.equals`, own3(m, "equals"), 0, emit);
|
|
6425
6621
|
emit(`${fpath}.contains`, this.s(m, "contains", fpath));
|
|
6426
6622
|
emit(`${fpath}.not_contains`, this.s(m, "not_contains", fpath));
|
|
6427
|
-
const oneOf =
|
|
6623
|
+
const oneOf = own3(m, "one_of");
|
|
6428
6624
|
if (Array.isArray(oneOf)) {
|
|
6429
6625
|
oneOf.forEach(
|
|
6430
6626
|
(one, j) => scanLeaves(`${fpath}.one_of[${j}]`, one, 0, emit)
|
|
@@ -6432,7 +6628,7 @@ var Walker = class _Walker {
|
|
|
6432
6628
|
}
|
|
6433
6629
|
}
|
|
6434
6630
|
}
|
|
6435
|
-
scanLeaves(`${path}.exact`,
|
|
6631
|
+
scanLeaves(`${path}.exact`, own3(e, "exact"), 0, emit);
|
|
6436
6632
|
}
|
|
6437
6633
|
};
|
|
6438
6634
|
function scoreInRange(v) {
|
|
@@ -6548,7 +6744,7 @@ function inputValueFailures(f, raw) {
|
|
|
6548
6744
|
case TYPE_FILE2: {
|
|
6549
6745
|
if (typeof raw === "string") return raw === "" ? [mismatch(f, TYPE_FILE2)] : [];
|
|
6550
6746
|
if (isRecord(raw)) {
|
|
6551
|
-
const ref =
|
|
6747
|
+
const ref = own3(raw, FILE_REF_KEY);
|
|
6552
6748
|
return typeof ref === "string" && ref !== "" ? [] : [mismatch(f, TYPE_FILE2)];
|
|
6553
6749
|
}
|
|
6554
6750
|
return [mismatch(f, TYPE_FILE2)];
|
|
@@ -6628,6 +6824,7 @@ function validateParsed(flow, opts, sources) {
|
|
|
6628
6824
|
validateCredentialEndpointPairing(f, issues);
|
|
6629
6825
|
validateExamples(f, issues);
|
|
6630
6826
|
validateOrchestratorExons(f, issues);
|
|
6827
|
+
validateResidencyEnforcement(f, issues);
|
|
6631
6828
|
validateUnknownKeys(f, issues);
|
|
6632
6829
|
const templateStats = validateTemplates(f, issues, opts);
|
|
6633
6830
|
const totalSteps = f.steps && typeof f.steps === "object" && !Array.isArray(f.steps) ? Object.keys(f.steps).length : 0;
|