@vaudience/aigentflow-flow-validator 0.24.0 → 0.26.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 +60 -0
- package/dist/cli.js +215 -11
- package/dist/index.cjs +213 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +213 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/PARITY.md
CHANGED
|
@@ -6,6 +6,66 @@ discipline for keeping the two in sync.
|
|
|
6
6
|
|
|
7
7
|
**Tracks AIgentFlow flow schema: `v2.831.0`** (`SPEC_VERSION` in [`src/spec/aigentflow-spec.json`](./src/spec/aigentflow-spec.json)), which adds the rule `unknown_function_operation` and a clearer `unknown_executor_scheme` message (package 0.23.0, below). Earlier: the rule `flow_data_key_reserved` (package 0.22.0). Earlier: the `here` executor scheme, the `strict_templates` flow key and the rule `template_default_piped` (package 0.21.0). Earlier: the warning `compliance_residency_unenforced` (package 0.20.0). `orchestrator.region` / `orchestrator.enforce_region` (package 0.19.0). Earlier: the AigentChat executor was removed (package 0.18.0). Earlier additions: the `examples:` block (package 0.17.0), `server_owned_query_key` (package 0.15.1) from the AIgentFlow release after v2.790.0, `credential_endpoint_unpaired` (package 0.15.2) from AIgentFlow v2.793.0 and the release after it, and `display_name_too_long` (package 0.15.3) from AIgentFlow v2.802.0.
|
|
8
8
|
|
|
9
|
+
> package 0.26.0 — **three new rules, and the atlas rules brought in line with the reference.**
|
|
10
|
+
> `reserved_input_name` (error): an input the flow declares under the top-level `query:` map, or as an
|
|
11
|
+
> `input_schema` field name, that starts with two underscores. That namespace belongs to the platform
|
|
12
|
+
> (the acting person, the credential manifest, the untrusted-input marker), and a value or default
|
|
13
|
+
> written under such a name is never delivered. Every name the platform reserves today starts with
|
|
14
|
+
> `__`; one leading underscore (`_private`) or an inner `__` (`a__b`) is an ordinary name. Fields:
|
|
15
|
+
> `query.<name>` (names sorted) and `input_schema.fields.<name>` (file order), no step id. The schema
|
|
16
|
+
> field is addressed by **name**, not by index like this port's other `input_schema` findings, because
|
|
17
|
+
> the reference reports it that way and a host that merges several verdicts drops a finding only when
|
|
18
|
+
> code and field both match. `atlas_model_not_literal` (error): an `atlas://agent/run` step whose
|
|
19
|
+
> `query.model` holds a string containing `{{` anywhere (provider, model, reasoning_effort, or any
|
|
20
|
+
> nested value). The model choice is fixed in the flow, never computed from input.
|
|
21
|
+
> `atlas_param_refused` (error): **one** finding per atlas step listing, sorted, the `query` keys the
|
|
22
|
+
> platform owns. An agent run refuses `api_key`, `attribution`, `base_url`, `host`, `idempotency_key`,
|
|
23
|
+
> `org_id`, `person`, `untrusted_input`, `untrusted_input_override`, `url`, `user_id`; a capability step
|
|
24
|
+
> refuses only `attribution`, `idempotency_key`, `untrusted_input`, `untrusted_input_override` (its
|
|
25
|
+
> query is the tool's own arguments, so a `url` is legitimate there). The key sets live in the spec
|
|
26
|
+
> block `atlasExecutor.paramRefused`; the reserved-name prefix in the new top-level block
|
|
27
|
+
> `reservedInputNames`.
|
|
28
|
+
>
|
|
29
|
+
> _Alignments (the reference is authoritative; the old behaviour was looser)._ `agent`, `prompt` and
|
|
30
|
+
> `prompt_template` are present only when they are non-empty strings **after trimming whitespace**: an
|
|
31
|
+
> `agent` that is not a string (`42`) is missing, `prompt: " "` is absent, and `prompt: ""` next to a
|
|
32
|
+
> `prompt_template` is **not** `atlas_prompt_both`. The `{{` check on `capabilities` is deep (any
|
|
33
|
+
> string entry or nested string) and applies to every `atlas://` step with a valid operation. The
|
|
34
|
+
> capability reference pattern is now `^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$` (a dot or colon is refused).
|
|
35
|
+
> An `atlas://` URL that the shared executor-URL shape refuses is no longer skipped by the atlas
|
|
36
|
+
> rules: it is split at the first slash after `atlas://` and judged (a reference with a dot is
|
|
37
|
+
> `atlas_capability_ref_invalid`, `atlas://agent/run/extra` is `atlas_operation_unknown`); the shape's own
|
|
38
|
+
> `invalid_executor_url` finding is still reported beside it. The scheme match stays exact. The
|
|
39
|
+
> `atlas_agent_missing` message now ends "missing, empty or not a string". `specVersion` is unchanged
|
|
40
|
+
> (`2.831.0`). Modules: `validators/executors.ts`, `validators/reservedInputs.ts`. Gates: thirteen new
|
|
41
|
+
> shared fixtures (one invalid per code, the valid ones forbid every atlas code, `reserved_input_name`
|
|
42
|
+
> and `unknown_executor_scheme`), byte-identical with the Go port, and
|
|
43
|
+
> `test/v0260-atlas-contract.test.ts`. On the 227 bundled example flows plus the six atlas examples,
|
|
44
|
+
> both ports report zero new findings and agree finding for finding (0 errors, 36 warnings).
|
|
45
|
+
|
|
46
|
+
> package 0.25.0 — **the `atlas://` executor scheme and seven rules**. `atlas` becomes a known
|
|
47
|
+
> scheme (no `unknown_executor_scheme` warning). The URL form is `atlas://<provider>/<resource>`:
|
|
48
|
+
> provider `agent` has the single resource `run`, provider `capability` takes a capability reference
|
|
49
|
+
> as its resource. Seven new **errors**, all with the step as `field` (a loop sub-step reports its
|
|
50
|
+
> sub-step path and the composite `parent.child` step id):
|
|
51
|
+
> `atlas_operation_unknown` (any other provider, or `agent` with a resource other than `run`);
|
|
52
|
+
> `atlas_agent_missing` (an agent run whose `query.agent` is absent, empty or not a string);
|
|
53
|
+
> `atlas_agent_not_literal` (`query.agent` contains `{{`: the agent is always written out, never
|
|
54
|
+
> computed from input); `atlas_capabilities_not_literal` (a string entry of `query.capabilities`
|
|
55
|
+
> contains `{{`); `atlas_prompt_missing` and `atlas_prompt_both` (an agent run takes exactly one of
|
|
56
|
+
> `query.prompt` and `query.prompt_template`; an empty string counts as absent);
|
|
57
|
+
> `atlas_capability_ref_invalid` (the reference fails `^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$`).
|
|
58
|
+
> Templates in `prompt`, in `inputs` values and in a capability's `args` are fine. A templated
|
|
59
|
+
> executor is skipped, an unparsable URL stays `invalid_executor_url`'s, and the scheme match is
|
|
60
|
+
> exact (`ATLAS://` is a known scheme but gets none of these rules). The shape check runs first, so
|
|
61
|
+
> a reference containing a character outside letters, digits, `_`, `-` and `/` (for example a dot
|
|
62
|
+
> or colon) is reported as `invalid_executor_url` and never reaches the reference pattern.
|
|
63
|
+
> `specVersion` is unchanged (`2.831.0`). Module: `validators/executors.ts`; spec block
|
|
64
|
+
> `atlasExecutor`. Gates: thirteen `invalid-atlas-*` / `valid-atlas-*` fixtures (the valid ones
|
|
65
|
+
> forbid every atlas code and `unknown_executor_scheme`), shared byte for byte with the Go port, and
|
|
66
|
+
> `test/v0250-atlas-executor.test.ts`. On the 227 bundled example flows both ports report no new
|
|
67
|
+
> findings.
|
|
68
|
+
|
|
9
69
|
> package 0.24.0 — **`potential_infinite_loop` fires only on a cycle with no way out**, in step
|
|
10
70
|
> with the reference. It used to fire on every cycle reachable from the start step, so a deliberate
|
|
11
71
|
> ask-again loop (a step that routes back until a condition sends it onward) carried a warning that
|
package/dist/cli.js
CHANGED
|
@@ -463,6 +463,7 @@ var aigentflow_spec_default = {
|
|
|
463
463
|
"ai",
|
|
464
464
|
"aiv",
|
|
465
465
|
"async",
|
|
466
|
+
"atlas",
|
|
466
467
|
"browser",
|
|
467
468
|
"chart",
|
|
468
469
|
"db",
|
|
@@ -1006,6 +1007,87 @@ var aigentflow_spec_default = {
|
|
|
1006
1007
|
"utils/parse-date"
|
|
1007
1008
|
]
|
|
1008
1009
|
},
|
|
1010
|
+
atlasExecutor: {
|
|
1011
|
+
$comment: "atlas://<provider>/<resource>: calls into the Atlas platform. Providers are agent (the one resource is run) and capability (the resource is a capability reference). All findings are ERRORS, field = the step (loop sub-steps use the composite step id and the sub-step path). Templated executors, unparsable URLs and other schemes are skipped. A literal agent and literal capabilities keep a step from being steered by data: query.agent and every string anywhere in query.capabilities and query.model may not contain templateActionOpen, while prompt, inputs and capability args may. agent, prompt and prompt_template count as present only when they are non-empty strings after trimming whitespace. A message format takes the quoted step id first. A URL the shared executor-URL shape refuses (a dot or colon in a reference, say) is still judged here, split at the first slash after atlas://.",
|
|
1012
|
+
scheme: "atlas",
|
|
1013
|
+
providerAgent: "agent",
|
|
1014
|
+
providerCapability: "capability",
|
|
1015
|
+
agentResource: "run",
|
|
1016
|
+
capabilityRefPattern: "^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$",
|
|
1017
|
+
operationUnknown: {
|
|
1018
|
+
code: "atlas_operation_unknown",
|
|
1019
|
+
messageFormat: "step %s uses %s, which is not an Atlas operation. The providers are agent (resource run) and capability (resource: a capability reference).",
|
|
1020
|
+
suggestion: "Use atlas://agent/run or atlas://capability/<capability-ref>."
|
|
1021
|
+
},
|
|
1022
|
+
agentMissing: {
|
|
1023
|
+
code: "atlas_agent_missing",
|
|
1024
|
+
messageFormat: "step %s runs an Atlas agent but query.agent is missing, empty or not a string.",
|
|
1025
|
+
suggestion: "Set query.agent to the agent to run, written out as a literal such as @acme/reporter."
|
|
1026
|
+
},
|
|
1027
|
+
agentNotLiteral: {
|
|
1028
|
+
code: "atlas_agent_not_literal",
|
|
1029
|
+
messageFormat: "step %s computes query.agent from a template. The agent must be a fixed literal, never derived from input.",
|
|
1030
|
+
suggestion: "Write the agent out as a literal such as @acme/reporter. Data may go into the prompt or the inputs instead."
|
|
1031
|
+
},
|
|
1032
|
+
capabilitiesNotLiteral: {
|
|
1033
|
+
code: "atlas_capabilities_not_literal",
|
|
1034
|
+
messageFormat: "step %s computes an entry of query.capabilities from a template. Capabilities must be fixed literals, never derived from input.",
|
|
1035
|
+
suggestion: "Write each capability out as a literal. Data may go into the prompt or the inputs instead."
|
|
1036
|
+
},
|
|
1037
|
+
promptMissing: {
|
|
1038
|
+
code: "atlas_prompt_missing",
|
|
1039
|
+
messageFormat: "step %s runs an Atlas agent with neither query.prompt nor query.prompt_template.",
|
|
1040
|
+
suggestion: "Set exactly one of query.prompt or query.prompt_template."
|
|
1041
|
+
},
|
|
1042
|
+
promptBoth: {
|
|
1043
|
+
code: "atlas_prompt_both",
|
|
1044
|
+
messageFormat: "step %s sets both query.prompt and query.prompt_template.",
|
|
1045
|
+
suggestion: "Keep exactly one of query.prompt or query.prompt_template."
|
|
1046
|
+
},
|
|
1047
|
+
capabilityRefInvalid: {
|
|
1048
|
+
code: "atlas_capability_ref_invalid",
|
|
1049
|
+
messageFormat: "step %s uses atlas://capability/%s, which is not a valid capability reference.",
|
|
1050
|
+
suggestion: "A capability reference starts with a letter or digit and continues with letters, digits, _ and - up to 128 characters in all."
|
|
1051
|
+
},
|
|
1052
|
+
modelNotLiteral: {
|
|
1053
|
+
code: "atlas_model_not_literal",
|
|
1054
|
+
messageFormat: "step %s computes a value of query.model from a template. The provider, model and reasoning_effort must be fixed literals, never derived from input.",
|
|
1055
|
+
suggestion: "Write the model choice out as literals. Data may go into the prompt or the inputs instead."
|
|
1056
|
+
},
|
|
1057
|
+
paramRefused: {
|
|
1058
|
+
code: "atlas_param_refused",
|
|
1059
|
+
messageFormat: "step %s sets %s under query. Atlas decides these itself, so a step may not set them.",
|
|
1060
|
+
suggestion: "Remove these keys from the query.",
|
|
1061
|
+
agentKeys: [
|
|
1062
|
+
"api_key",
|
|
1063
|
+
"attribution",
|
|
1064
|
+
"base_url",
|
|
1065
|
+
"host",
|
|
1066
|
+
"idempotency_key",
|
|
1067
|
+
"org_id",
|
|
1068
|
+
"person",
|
|
1069
|
+
"untrusted_input",
|
|
1070
|
+
"untrusted_input_override",
|
|
1071
|
+
"url",
|
|
1072
|
+
"user_id"
|
|
1073
|
+
],
|
|
1074
|
+
capabilityKeys: [
|
|
1075
|
+
"attribution",
|
|
1076
|
+
"idempotency_key",
|
|
1077
|
+
"untrusted_input",
|
|
1078
|
+
"untrusted_input_override"
|
|
1079
|
+
]
|
|
1080
|
+
}
|
|
1081
|
+
},
|
|
1082
|
+
reservedInputNames: {
|
|
1083
|
+
$comment: "reserved_input_name (ERROR): an input the flow declares under the top-level query: map, or as an input_schema field name, that starts with prefix. The platform keeps that namespace for values it writes itself (the acting person, the credential manifest, the untrusted-input marker); a value or default written under such a name is never delivered. Every name the platform reserves today starts with the prefix. Matching is exact and case-sensitive: one leading underscore is an ordinary name. Findings carry no step id. queryFieldFormat takes the input name; schemaFieldFormat takes the field name as written (a non-string name is skipped). Query names are reported sorted, then schema fields in file order.",
|
|
1084
|
+
prefix: "__",
|
|
1085
|
+
code: "reserved_input_name",
|
|
1086
|
+
messageFormat: "the input %s starts with two underscores. That namespace belongs to the platform (the acting person, the credential manifest, the untrusted-input marker): a flow cannot declare an input there, and a value or default written under such a name is never delivered.",
|
|
1087
|
+
suggestion: 'Rename the input so it does not start with "__".',
|
|
1088
|
+
queryFieldFormat: "query.%s",
|
|
1089
|
+
schemaFieldFormat: "input_schema.fields.%s"
|
|
1090
|
+
},
|
|
1009
1091
|
flowDataReservedKeys: {
|
|
1010
1092
|
$comment: "flow_data_key_reserved (ERROR, no loop demotion): a key of the top-level data: map that names engine state. data: is seeded into the mission's .data namespace, where the engine keeps its own bookkeeping, so a flow declaring such a key would forge it. Matching is exact and case-sensitive. A key is reserved when it is one of exact (each with its reason), or starts with one of prefixes (checked in order, first wins; each with its reason). The finding's field is fieldFormat with the key; the message is messageFormat with the key (JSON-quoted, as Go's %q) then the reason; the suggestion is verbatim. Findings are sorted by key. Reference: FlowDataKeyReservation, validateFlowDataKeysAreNotReserved (aigentflow#247 review).",
|
|
1011
1093
|
code: "flow_data_key_reserved",
|
|
@@ -2129,6 +2211,37 @@ var FUNCTION_OPERATIONS = {
|
|
|
2129
2211
|
operations: new Set(aigentflow_spec_default.functionOperations.operations),
|
|
2130
2212
|
sorted: aigentflow_spec_default.functionOperations.operations
|
|
2131
2213
|
};
|
|
2214
|
+
var atlas = aigentflow_spec_default.atlasExecutor;
|
|
2215
|
+
var ATLAS_EXECUTOR = {
|
|
2216
|
+
scheme: atlas.scheme,
|
|
2217
|
+
providerAgent: atlas.providerAgent,
|
|
2218
|
+
providerCapability: atlas.providerCapability,
|
|
2219
|
+
agentResource: atlas.agentResource,
|
|
2220
|
+
capabilityRefPattern: new RegExp(atlas.capabilityRefPattern),
|
|
2221
|
+
operationUnknown: atlas.operationUnknown,
|
|
2222
|
+
agentMissing: atlas.agentMissing,
|
|
2223
|
+
agentNotLiteral: atlas.agentNotLiteral,
|
|
2224
|
+
capabilitiesNotLiteral: atlas.capabilitiesNotLiteral,
|
|
2225
|
+
promptMissing: atlas.promptMissing,
|
|
2226
|
+
promptBoth: atlas.promptBoth,
|
|
2227
|
+
capabilityRefInvalid: atlas.capabilityRefInvalid,
|
|
2228
|
+
modelNotLiteral: atlas.modelNotLiteral,
|
|
2229
|
+
paramRefused: {
|
|
2230
|
+
code: atlas.paramRefused.code,
|
|
2231
|
+
messageFormat: atlas.paramRefused.messageFormat,
|
|
2232
|
+
suggestion: atlas.paramRefused.suggestion,
|
|
2233
|
+
agentKeys: atlas.paramRefused.agentKeys,
|
|
2234
|
+
capabilityKeys: atlas.paramRefused.capabilityKeys
|
|
2235
|
+
}
|
|
2236
|
+
};
|
|
2237
|
+
var RESERVED_INPUT_NAMES = {
|
|
2238
|
+
prefix: aigentflow_spec_default.reservedInputNames.prefix,
|
|
2239
|
+
code: aigentflow_spec_default.reservedInputNames.code,
|
|
2240
|
+
messageFormat: aigentflow_spec_default.reservedInputNames.messageFormat,
|
|
2241
|
+
suggestion: aigentflow_spec_default.reservedInputNames.suggestion,
|
|
2242
|
+
queryFieldFormat: aigentflow_spec_default.reservedInputNames.queryFieldFormat,
|
|
2243
|
+
schemaFieldFormat: aigentflow_spec_default.reservedInputNames.schemaFieldFormat
|
|
2244
|
+
};
|
|
2132
2245
|
|
|
2133
2246
|
// src/validators/executors.ts
|
|
2134
2247
|
var SCHEME_SEPARATOR = "://";
|
|
@@ -2192,6 +2305,63 @@ function checkExecutor(executor, field, stepID, issues) {
|
|
|
2192
2305
|
}
|
|
2193
2306
|
checkFunctionOperation(executor, field, stepID, templated, issues);
|
|
2194
2307
|
}
|
|
2308
|
+
function checkAtlasStep(executor, query, stepField, stepID, issues) {
|
|
2309
|
+
if (executor.includes(TEMPLATE_ACTION_OPEN)) return;
|
|
2310
|
+
const prefix = `${ATLAS_EXECUTOR.scheme}://`;
|
|
2311
|
+
if (!executor.startsWith(prefix)) return;
|
|
2312
|
+
const rest = executor.slice(prefix.length);
|
|
2313
|
+
const slash = rest.indexOf("/");
|
|
2314
|
+
const provider = slash < 0 ? rest : rest.slice(0, slash);
|
|
2315
|
+
const resource = slash < 0 ? "" : rest.slice(slash + 1);
|
|
2316
|
+
const quoted = JSON.stringify(stepID);
|
|
2317
|
+
const report = (rule, ...args) => {
|
|
2318
|
+
let message = rule.messageFormat.replace("%s", () => quoted);
|
|
2319
|
+
for (const a of args) message = message.replace("%s", () => a);
|
|
2320
|
+
issues.error({
|
|
2321
|
+
field: stepField,
|
|
2322
|
+
message,
|
|
2323
|
+
code: rule.code,
|
|
2324
|
+
stepId: stepID,
|
|
2325
|
+
suggestion: rule.suggestion
|
|
2326
|
+
});
|
|
2327
|
+
};
|
|
2328
|
+
const q2 = isRecord(query) ? query : {};
|
|
2329
|
+
let refusedKeys = ATLAS_EXECUTOR.paramRefused.capabilityKeys;
|
|
2330
|
+
if (provider === ATLAS_EXECUTOR.providerCapability) {
|
|
2331
|
+
if (!ATLAS_EXECUTOR.capabilityRefPattern.test(resource)) {
|
|
2332
|
+
report(ATLAS_EXECUTOR.capabilityRefInvalid, resource);
|
|
2333
|
+
}
|
|
2334
|
+
} else if (provider === ATLAS_EXECUTOR.providerAgent && resource === ATLAS_EXECUTOR.agentResource) {
|
|
2335
|
+
refusedKeys = ATLAS_EXECUTOR.paramRefused.agentKeys;
|
|
2336
|
+
const agent = q2.agent;
|
|
2337
|
+
if (typeof agent !== "string" || agent.trim() === "") {
|
|
2338
|
+
report(ATLAS_EXECUTOR.agentMissing);
|
|
2339
|
+
} else if (agent.includes(TEMPLATE_ACTION_OPEN)) {
|
|
2340
|
+
report(ATLAS_EXECUTOR.agentNotLiteral);
|
|
2341
|
+
}
|
|
2342
|
+
const hasPrompt = atlasTextPresent(q2.prompt);
|
|
2343
|
+
const hasTemplate = atlasTextPresent(q2.prompt_template);
|
|
2344
|
+
if (!hasPrompt && !hasTemplate) report(ATLAS_EXECUTOR.promptMissing);
|
|
2345
|
+
else if (hasPrompt && hasTemplate) report(ATLAS_EXECUTOR.promptBoth);
|
|
2346
|
+
if (anyTemplate(q2.model)) report(ATLAS_EXECUTOR.modelNotLiteral);
|
|
2347
|
+
} else {
|
|
2348
|
+
report(ATLAS_EXECUTOR.operationUnknown, executor);
|
|
2349
|
+
return;
|
|
2350
|
+
}
|
|
2351
|
+
if (anyTemplate(q2.capabilities)) report(ATLAS_EXECUTOR.capabilitiesNotLiteral);
|
|
2352
|
+
const refused = refusedKeys.filter((key) => Object.prototype.hasOwnProperty.call(q2, key)).sort();
|
|
2353
|
+
if (refused.length > 0) report(ATLAS_EXECUTOR.paramRefused, refused.join(", "));
|
|
2354
|
+
}
|
|
2355
|
+
function anyTemplate(v) {
|
|
2356
|
+
if (typeof v === "string") return v.includes(TEMPLATE_ACTION_OPEN);
|
|
2357
|
+
if (Array.isArray(v)) return v.some(anyTemplate);
|
|
2358
|
+
if (isRecord(v)) return Object.values(v).some(anyTemplate);
|
|
2359
|
+
return false;
|
|
2360
|
+
}
|
|
2361
|
+
function atlasTextPresent(v) {
|
|
2362
|
+
if (v === void 0 || v === null) return false;
|
|
2363
|
+
return typeof v !== "string" || v.trim() !== "";
|
|
2364
|
+
}
|
|
2195
2365
|
function checkFunctionOperation(executor, field, stepID, templated, issues) {
|
|
2196
2366
|
if (templated) return;
|
|
2197
2367
|
const m = EXECUTOR_URL_PATTERN.exec(executor);
|
|
@@ -2216,6 +2386,7 @@ function validateExecutors(flow, issues) {
|
|
|
2216
2386
|
const executor = issues.stringOf(step, "executor", `steps.${stepID}`);
|
|
2217
2387
|
if (executor !== null && executor !== "") {
|
|
2218
2388
|
checkExecutor(executor, `steps.${stepID}.executor`, stepID, issues);
|
|
2389
|
+
checkAtlasStep(executor, step.query, `steps.${stepID}`, stepID, issues);
|
|
2219
2390
|
}
|
|
2220
2391
|
const loop = step.loop;
|
|
2221
2392
|
if (isRecord(loop) && Array.isArray(loop.steps)) {
|
|
@@ -2232,6 +2403,7 @@ function validateExecutors(flow, issues) {
|
|
|
2232
2403
|
`${stepID}.${subID}`,
|
|
2233
2404
|
issues
|
|
2234
2405
|
);
|
|
2406
|
+
checkAtlasStep(subExecutor, subStep.query, subPath, `${stepID}.${subID}`, issues);
|
|
2235
2407
|
}
|
|
2236
2408
|
});
|
|
2237
2409
|
}
|
|
@@ -3874,6 +4046,37 @@ function validateInputSchema(flow, issues) {
|
|
|
3874
4046
|
validateSchemaDefinition(flow.input_schema, "input_schema", issues);
|
|
3875
4047
|
}
|
|
3876
4048
|
|
|
4049
|
+
// src/validators/reservedInputs.ts
|
|
4050
|
+
function formatField(format, name) {
|
|
4051
|
+
return format.replace("%s", () => name);
|
|
4052
|
+
}
|
|
4053
|
+
function validateReservedInputNames(flow, issues) {
|
|
4054
|
+
const rule = RESERVED_INPUT_NAMES;
|
|
4055
|
+
const report = (name, fieldFormat) => {
|
|
4056
|
+
issues.error({
|
|
4057
|
+
field: formatField(fieldFormat, name),
|
|
4058
|
+
message: rule.messageFormat.replace("%s", () => JSON.stringify(name)),
|
|
4059
|
+
code: rule.code,
|
|
4060
|
+
suggestion: rule.suggestion
|
|
4061
|
+
});
|
|
4062
|
+
};
|
|
4063
|
+
const query = flow.query;
|
|
4064
|
+
if (isRecord(query)) {
|
|
4065
|
+
for (const name of Object.keys(query).sort()) {
|
|
4066
|
+
if (name.startsWith(rule.prefix)) report(name, rule.queryFieldFormat);
|
|
4067
|
+
}
|
|
4068
|
+
}
|
|
4069
|
+
const schema = flow.input_schema;
|
|
4070
|
+
const fields = isRecord(schema) ? schema.fields : void 0;
|
|
4071
|
+
if (!Array.isArray(fields)) return;
|
|
4072
|
+
for (const f of fields) {
|
|
4073
|
+
if (!isRecord(f)) continue;
|
|
4074
|
+
if (typeof f.name === "string" && f.name.startsWith(rule.prefix)) {
|
|
4075
|
+
report(f.name, rule.schemaFieldFormat);
|
|
4076
|
+
}
|
|
4077
|
+
}
|
|
4078
|
+
}
|
|
4079
|
+
|
|
3877
4080
|
// src/validators/outputSchema.ts
|
|
3878
4081
|
function validateOutputSchemas(flow, issues) {
|
|
3879
4082
|
const steps = flow.steps;
|
|
@@ -4988,7 +5191,7 @@ var CODE2 = "server_owned_query_key";
|
|
|
4988
5191
|
var KEY_QUERY = "query";
|
|
4989
5192
|
var KEY_ID = "id";
|
|
4990
5193
|
var SERVER_OWNED = new Set(SERVER_OWNED_QUERY_KEYS.keys);
|
|
4991
|
-
function
|
|
5194
|
+
function formatField2(format, ...args) {
|
|
4992
5195
|
let i = 0;
|
|
4993
5196
|
return format.replace(/%s/g, () => args[i++] ?? "");
|
|
4994
5197
|
}
|
|
@@ -5006,7 +5209,7 @@ function validateServerOwnedQueryKeys(flow, issues) {
|
|
|
5006
5209
|
issues,
|
|
5007
5210
|
stepID,
|
|
5008
5211
|
key,
|
|
5009
|
-
|
|
5212
|
+
formatField2(SERVER_OWNED_QUERY_KEYS.stepFieldFormat, stepID, key)
|
|
5010
5213
|
);
|
|
5011
5214
|
}
|
|
5012
5215
|
}
|
|
@@ -5020,7 +5223,7 @@ function validateServerOwnedQueryKeys(flow, issues) {
|
|
|
5020
5223
|
issues,
|
|
5021
5224
|
stepID,
|
|
5022
5225
|
key,
|
|
5023
|
-
|
|
5226
|
+
formatField2(SERVER_OWNED_QUERY_KEYS.loopStepFieldFormat, stepID, subID, key)
|
|
5024
5227
|
);
|
|
5025
5228
|
}
|
|
5026
5229
|
}
|
|
@@ -5289,7 +5492,7 @@ function own(m, key) {
|
|
|
5289
5492
|
if (!isRecord(m) || !Object.prototype.hasOwnProperty.call(m, key)) return void 0;
|
|
5290
5493
|
return m[key];
|
|
5291
5494
|
}
|
|
5292
|
-
function
|
|
5495
|
+
function formatField3(format, ...args) {
|
|
5293
5496
|
let i = 0;
|
|
5294
5497
|
return format.replace(/%s/g, () => args[i++] ?? "");
|
|
5295
5498
|
}
|
|
@@ -5311,14 +5514,14 @@ function credentialEndpointSteps(flow, issues) {
|
|
|
5311
5514
|
out.push({
|
|
5312
5515
|
executor: issues.stringOf(step, KEY_EXECUTOR, `steps.${stepId}`) ?? "",
|
|
5313
5516
|
query: own(step, KEY_QUERY2),
|
|
5314
|
-
field: (key) =>
|
|
5517
|
+
field: (key) => formatField3(CREDENTIAL_ENDPOINT_PAIRING.stepFieldFormat, stepId, key)
|
|
5315
5518
|
});
|
|
5316
5519
|
for (const sub of loopSubStepsOfStep(stepId, step)) {
|
|
5317
5520
|
const subId = issues.stringOf(sub.raw, KEY_ID2, sub.basePath) ?? "";
|
|
5318
5521
|
out.push({
|
|
5319
5522
|
executor: issues.stringOf(sub.raw, KEY_EXECUTOR, sub.basePath) ?? "",
|
|
5320
5523
|
query: own(sub.raw, KEY_QUERY2),
|
|
5321
|
-
field: (key) =>
|
|
5524
|
+
field: (key) => formatField3(CREDENTIAL_ENDPOINT_PAIRING.loopStepFieldFormat, stepId, subId, key)
|
|
5322
5525
|
});
|
|
5323
5526
|
}
|
|
5324
5527
|
}
|
|
@@ -5382,7 +5585,7 @@ function validateAI(flow, steps, issues) {
|
|
|
5382
5585
|
if ((stepsUsing.get(provider) ?? 0) > 0 && (stepsWithoutOwnKey.get(provider) ?? 0) === 0) {
|
|
5383
5586
|
continue;
|
|
5384
5587
|
}
|
|
5385
|
-
warnStoredShape(issues,
|
|
5588
|
+
warnStoredShape(issues, formatField3(CREDENTIAL_ENDPOINT_PAIRING.executorConfigBaseUrlFieldFormat, provider), provider);
|
|
5386
5589
|
}
|
|
5387
5590
|
}
|
|
5388
5591
|
function warnStoredShape(issues, field, provider) {
|
|
@@ -5429,7 +5632,7 @@ function familySecret(flow, family, step, issues) {
|
|
|
5429
5632
|
if (apiKey !== "" && listHas(family.secretParams, family.configApiKeyParam)) {
|
|
5430
5633
|
return {
|
|
5431
5634
|
envName: serverEnvRef(family, apiKey),
|
|
5432
|
-
field:
|
|
5635
|
+
field: formatField3(
|
|
5433
5636
|
CREDENTIAL_ENDPOINT_PAIRING.executorConfigKeyFieldFormat,
|
|
5434
5637
|
configKey,
|
|
5435
5638
|
CREDENTIAL_ENDPOINT_PAIRING.executorConfigApiKeyField
|
|
@@ -5442,7 +5645,7 @@ function familySecret(flow, family, step, issues) {
|
|
|
5442
5645
|
if (v !== null) {
|
|
5443
5646
|
return {
|
|
5444
5647
|
envName: serverEnvRef(family, v),
|
|
5445
|
-
field:
|
|
5648
|
+
field: formatField3(CREDENTIAL_ENDPOINT_PAIRING.executorConfigExtraFieldFormat, configKey, key)
|
|
5446
5649
|
};
|
|
5447
5650
|
}
|
|
5448
5651
|
}
|
|
@@ -5468,13 +5671,13 @@ function familyAuthorEndpoint(flow, family, step, issues) {
|
|
|
5468
5671
|
if (inst === null) continue;
|
|
5469
5672
|
const baseUrl = configStringField(inst, configKey, CREDENTIAL_ENDPOINT_PAIRING.executorConfigBaseUrlField, issues);
|
|
5470
5673
|
if (baseUrl !== "" && listHas(family.endpointParams, family.configBaseUrlParam)) {
|
|
5471
|
-
return decide(baseUrl,
|
|
5674
|
+
return decide(baseUrl, formatField3(CREDENTIAL_ENDPOINT_PAIRING.executorConfigBaseUrlFieldFormat, configKey));
|
|
5472
5675
|
}
|
|
5473
5676
|
const extra = own(inst, EXECUTOR_CONFIG_ENV_SCOPES.extraKey);
|
|
5474
5677
|
for (const key of family.endpointParams) {
|
|
5475
5678
|
const v = nonEmptyEndpoint(extra, key);
|
|
5476
5679
|
if (v !== null)
|
|
5477
|
-
return decide(v,
|
|
5680
|
+
return decide(v, formatField3(CREDENTIAL_ENDPOINT_PAIRING.executorConfigExtraFieldFormat, configKey, key));
|
|
5478
5681
|
}
|
|
5479
5682
|
}
|
|
5480
5683
|
return null;
|
|
@@ -7185,6 +7388,7 @@ function validateParsed(flow, opts, sources) {
|
|
|
7185
7388
|
validateOrchestratorCampaign(f, issues, opts);
|
|
7186
7389
|
validateCredentialBindings(f, issues);
|
|
7187
7390
|
validateInputSchema(f, issues);
|
|
7391
|
+
validateReservedInputNames(f, issues);
|
|
7188
7392
|
validateOutputSchemas(f, issues);
|
|
7189
7393
|
validateQualityGates(f, issues);
|
|
7190
7394
|
validateProcessingOperations(f, issues);
|