@vaudience/aigentflow-flow-validator 0.24.0 → 0.25.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
@@ -6,6 +6,29 @@ 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.25.0 — **the `atlas://` executor scheme and seven rules**. `atlas` becomes a known
10
+ > scheme (no `unknown_executor_scheme` warning). The URL form is `atlas://<provider>/<resource>`:
11
+ > provider `agent` has the single resource `run`, provider `capability` takes a capability reference
12
+ > as its resource. Seven new **errors**, all with the step as `field` (a loop sub-step reports its
13
+ > sub-step path and the composite `parent.child` step id):
14
+ > `atlas_operation_unknown` (any other provider, or `agent` with a resource other than `run`);
15
+ > `atlas_agent_missing` (an agent run whose `query.agent` is absent, empty or not a string);
16
+ > `atlas_agent_not_literal` (`query.agent` contains `{{`: the agent is always written out, never
17
+ > computed from input); `atlas_capabilities_not_literal` (a string entry of `query.capabilities`
18
+ > contains `{{`); `atlas_prompt_missing` and `atlas_prompt_both` (an agent run takes exactly one of
19
+ > `query.prompt` and `query.prompt_template`; an empty string counts as absent);
20
+ > `atlas_capability_ref_invalid` (the reference fails `^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$`).
21
+ > Templates in `prompt`, in `inputs` values and in a capability's `args` are fine. A templated
22
+ > executor is skipped, an unparsable URL stays `invalid_executor_url`'s, and the scheme match is
23
+ > exact (`ATLAS://` is a known scheme but gets none of these rules). The shape check runs first, so
24
+ > a reference containing a character outside letters, digits, `_`, `-` and `/` (for example a dot
25
+ > or colon) is reported as `invalid_executor_url` and never reaches the reference pattern.
26
+ > `specVersion` is unchanged (`2.831.0`). Module: `validators/executors.ts`; spec block
27
+ > `atlasExecutor`. Gates: thirteen `invalid-atlas-*` / `valid-atlas-*` fixtures (the valid ones
28
+ > forbid every atlas code and `unknown_executor_scheme`), shared byte for byte with the Go port, and
29
+ > `test/v0250-atlas-executor.test.ts`. On the 227 bundled example flows both ports report no new
30
+ > findings.
31
+
9
32
  > package 0.24.0 — **`potential_infinite_loop` fires only on a cycle with no way out**, in step
10
33
  > with the reference. It used to fire on every cycle reachable from the start step, so a deliberate
11
34
  > 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,49 @@ 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 in query.capabilities may not contain templateActionOpen, while prompt, inputs and capability args may. A message format takes the quoted step id first.",
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 or empty.",
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
+ },
1009
1053
  flowDataReservedKeys: {
1010
1054
  $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
1055
  code: "flow_data_key_reserved",
@@ -2129,6 +2173,21 @@ var FUNCTION_OPERATIONS = {
2129
2173
  operations: new Set(aigentflow_spec_default.functionOperations.operations),
2130
2174
  sorted: aigentflow_spec_default.functionOperations.operations
2131
2175
  };
2176
+ var atlas = aigentflow_spec_default.atlasExecutor;
2177
+ var ATLAS_EXECUTOR = {
2178
+ scheme: atlas.scheme,
2179
+ providerAgent: atlas.providerAgent,
2180
+ providerCapability: atlas.providerCapability,
2181
+ agentResource: atlas.agentResource,
2182
+ capabilityRefPattern: new RegExp(atlas.capabilityRefPattern),
2183
+ operationUnknown: atlas.operationUnknown,
2184
+ agentMissing: atlas.agentMissing,
2185
+ agentNotLiteral: atlas.agentNotLiteral,
2186
+ capabilitiesNotLiteral: atlas.capabilitiesNotLiteral,
2187
+ promptMissing: atlas.promptMissing,
2188
+ promptBoth: atlas.promptBoth,
2189
+ capabilityRefInvalid: atlas.capabilityRefInvalid
2190
+ };
2132
2191
 
2133
2192
  // src/validators/executors.ts
2134
2193
  var SCHEME_SEPARATOR = "://";
@@ -2192,6 +2251,51 @@ function checkExecutor(executor, field, stepID, issues) {
2192
2251
  }
2193
2252
  checkFunctionOperation(executor, field, stepID, templated, issues);
2194
2253
  }
2254
+ function checkAtlasStep(executor, query, stepField, stepID, issues) {
2255
+ if (executor.includes(TEMPLATE_ACTION_OPEN)) return;
2256
+ const m = EXECUTOR_URL_PATTERN.exec(executor);
2257
+ if (m === null || m[1] !== ATLAS_EXECUTOR.scheme) return;
2258
+ const provider = m[2];
2259
+ const resource = m[3];
2260
+ const quoted = JSON.stringify(stepID);
2261
+ const report = (rule, ...args) => {
2262
+ let message = rule.messageFormat.replace("%s", () => quoted);
2263
+ for (const a of args) message = message.replace("%s", () => a);
2264
+ issues.error({
2265
+ field: stepField,
2266
+ message,
2267
+ code: rule.code,
2268
+ stepId: stepID,
2269
+ suggestion: rule.suggestion
2270
+ });
2271
+ };
2272
+ const q2 = isRecord(query) ? query : {};
2273
+ if (provider === ATLAS_EXECUTOR.providerCapability) {
2274
+ if (!ATLAS_EXECUTOR.capabilityRefPattern.test(resource)) {
2275
+ report(ATLAS_EXECUTOR.capabilityRefInvalid, resource);
2276
+ }
2277
+ } else if (provider === ATLAS_EXECUTOR.providerAgent && resource === ATLAS_EXECUTOR.agentResource) {
2278
+ const agent = q2.agent;
2279
+ if (typeof agent !== "string" || agent === "") {
2280
+ report(ATLAS_EXECUTOR.agentMissing);
2281
+ } else if (agent.includes(TEMPLATE_ACTION_OPEN)) {
2282
+ report(ATLAS_EXECUTOR.agentNotLiteral);
2283
+ }
2284
+ const prompt = q2.prompt;
2285
+ const promptTemplate = q2.prompt_template;
2286
+ const hasPrompt = prompt !== void 0 && prompt !== null && prompt !== "";
2287
+ const hasTemplate = promptTemplate !== void 0 && promptTemplate !== null && promptTemplate !== "";
2288
+ if (!hasPrompt && !hasTemplate) report(ATLAS_EXECUTOR.promptMissing);
2289
+ else if (hasPrompt && hasTemplate) report(ATLAS_EXECUTOR.promptBoth);
2290
+ } else {
2291
+ report(ATLAS_EXECUTOR.operationUnknown, executor);
2292
+ return;
2293
+ }
2294
+ const caps = q2.capabilities;
2295
+ if (Array.isArray(caps) && caps.some((c) => typeof c === "string" && c.includes(TEMPLATE_ACTION_OPEN))) {
2296
+ report(ATLAS_EXECUTOR.capabilitiesNotLiteral);
2297
+ }
2298
+ }
2195
2299
  function checkFunctionOperation(executor, field, stepID, templated, issues) {
2196
2300
  if (templated) return;
2197
2301
  const m = EXECUTOR_URL_PATTERN.exec(executor);
@@ -2216,6 +2320,7 @@ function validateExecutors(flow, issues) {
2216
2320
  const executor = issues.stringOf(step, "executor", `steps.${stepID}`);
2217
2321
  if (executor !== null && executor !== "") {
2218
2322
  checkExecutor(executor, `steps.${stepID}.executor`, stepID, issues);
2323
+ checkAtlasStep(executor, step.query, `steps.${stepID}`, stepID, issues);
2219
2324
  }
2220
2325
  const loop = step.loop;
2221
2326
  if (isRecord(loop) && Array.isArray(loop.steps)) {
@@ -2232,6 +2337,7 @@ function validateExecutors(flow, issues) {
2232
2337
  `${stepID}.${subID}`,
2233
2338
  issues
2234
2339
  );
2340
+ checkAtlasStep(subExecutor, subStep.query, subPath, `${stepID}.${subID}`, issues);
2235
2341
  }
2236
2342
  });
2237
2343
  }
package/dist/index.cjs CHANGED
@@ -459,6 +459,7 @@ var aigentflow_spec_default = {
459
459
  "ai",
460
460
  "aiv",
461
461
  "async",
462
+ "atlas",
462
463
  "browser",
463
464
  "chart",
464
465
  "db",
@@ -988,6 +989,48 @@ var aigentflow_spec_default = {
988
989
  "utils/parse-date"
989
990
  ]
990
991
  },
992
+ atlasExecutor: {
993
+ scheme: "atlas",
994
+ providerAgent: "agent",
995
+ providerCapability: "capability",
996
+ agentResource: "run",
997
+ capabilityRefPattern: "^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$",
998
+ operationUnknown: {
999
+ code: "atlas_operation_unknown",
1000
+ messageFormat: "step %s uses %s, which is not an Atlas operation. The providers are agent (resource run) and capability (resource: a capability reference).",
1001
+ suggestion: "Use atlas://agent/run or atlas://capability/<capability-ref>."
1002
+ },
1003
+ agentMissing: {
1004
+ code: "atlas_agent_missing",
1005
+ messageFormat: "step %s runs an Atlas agent but query.agent is missing or empty.",
1006
+ suggestion: "Set query.agent to the agent to run, written out as a literal such as @acme/reporter."
1007
+ },
1008
+ agentNotLiteral: {
1009
+ code: "atlas_agent_not_literal",
1010
+ messageFormat: "step %s computes query.agent from a template. The agent must be a fixed literal, never derived from input.",
1011
+ suggestion: "Write the agent out as a literal such as @acme/reporter. Data may go into the prompt or the inputs instead."
1012
+ },
1013
+ capabilitiesNotLiteral: {
1014
+ code: "atlas_capabilities_not_literal",
1015
+ messageFormat: "step %s computes an entry of query.capabilities from a template. Capabilities must be fixed literals, never derived from input.",
1016
+ suggestion: "Write each capability out as a literal. Data may go into the prompt or the inputs instead."
1017
+ },
1018
+ promptMissing: {
1019
+ code: "atlas_prompt_missing",
1020
+ messageFormat: "step %s runs an Atlas agent with neither query.prompt nor query.prompt_template.",
1021
+ suggestion: "Set exactly one of query.prompt or query.prompt_template."
1022
+ },
1023
+ promptBoth: {
1024
+ code: "atlas_prompt_both",
1025
+ messageFormat: "step %s sets both query.prompt and query.prompt_template.",
1026
+ suggestion: "Keep exactly one of query.prompt or query.prompt_template."
1027
+ },
1028
+ capabilityRefInvalid: {
1029
+ code: "atlas_capability_ref_invalid",
1030
+ messageFormat: "step %s uses atlas://capability/%s, which is not a valid capability reference.",
1031
+ suggestion: "A capability reference starts with a letter or digit and continues with letters, digits and _ . : - up to 128 characters in all."
1032
+ }
1033
+ },
991
1034
  flowDataReservedKeys: {
992
1035
  code: "flow_data_key_reserved",
993
1036
  fieldFormat: "data.%s",
@@ -2030,6 +2073,21 @@ var FUNCTION_OPERATIONS = {
2030
2073
  operations: new Set(aigentflow_spec_default.functionOperations.operations),
2031
2074
  sorted: aigentflow_spec_default.functionOperations.operations
2032
2075
  };
2076
+ var atlas = aigentflow_spec_default.atlasExecutor;
2077
+ var ATLAS_EXECUTOR = {
2078
+ scheme: atlas.scheme,
2079
+ providerAgent: atlas.providerAgent,
2080
+ providerCapability: atlas.providerCapability,
2081
+ agentResource: atlas.agentResource,
2082
+ capabilityRefPattern: new RegExp(atlas.capabilityRefPattern),
2083
+ operationUnknown: atlas.operationUnknown,
2084
+ agentMissing: atlas.agentMissing,
2085
+ agentNotLiteral: atlas.agentNotLiteral,
2086
+ capabilitiesNotLiteral: atlas.capabilitiesNotLiteral,
2087
+ promptMissing: atlas.promptMissing,
2088
+ promptBoth: atlas.promptBoth,
2089
+ capabilityRefInvalid: atlas.capabilityRefInvalid
2090
+ };
2033
2091
 
2034
2092
  // src/validators/executors.ts
2035
2093
  var SCHEME_SEPARATOR = "://";
@@ -2093,6 +2151,51 @@ function checkExecutor(executor, field, stepID, issues) {
2093
2151
  }
2094
2152
  checkFunctionOperation(executor, field, stepID, templated, issues);
2095
2153
  }
2154
+ function checkAtlasStep(executor, query, stepField, stepID, issues) {
2155
+ if (executor.includes(TEMPLATE_ACTION_OPEN)) return;
2156
+ const m = EXECUTOR_URL_PATTERN.exec(executor);
2157
+ if (m === null || m[1] !== ATLAS_EXECUTOR.scheme) return;
2158
+ const provider = m[2];
2159
+ const resource = m[3];
2160
+ const quoted = JSON.stringify(stepID);
2161
+ const report = (rule, ...args) => {
2162
+ let message = rule.messageFormat.replace("%s", () => quoted);
2163
+ for (const a of args) message = message.replace("%s", () => a);
2164
+ issues.error({
2165
+ field: stepField,
2166
+ message,
2167
+ code: rule.code,
2168
+ stepId: stepID,
2169
+ suggestion: rule.suggestion
2170
+ });
2171
+ };
2172
+ const q2 = isRecord(query) ? query : {};
2173
+ if (provider === ATLAS_EXECUTOR.providerCapability) {
2174
+ if (!ATLAS_EXECUTOR.capabilityRefPattern.test(resource)) {
2175
+ report(ATLAS_EXECUTOR.capabilityRefInvalid, resource);
2176
+ }
2177
+ } else if (provider === ATLAS_EXECUTOR.providerAgent && resource === ATLAS_EXECUTOR.agentResource) {
2178
+ const agent = q2.agent;
2179
+ if (typeof agent !== "string" || agent === "") {
2180
+ report(ATLAS_EXECUTOR.agentMissing);
2181
+ } else if (agent.includes(TEMPLATE_ACTION_OPEN)) {
2182
+ report(ATLAS_EXECUTOR.agentNotLiteral);
2183
+ }
2184
+ const prompt = q2.prompt;
2185
+ const promptTemplate = q2.prompt_template;
2186
+ const hasPrompt = prompt !== void 0 && prompt !== null && prompt !== "";
2187
+ const hasTemplate = promptTemplate !== void 0 && promptTemplate !== null && promptTemplate !== "";
2188
+ if (!hasPrompt && !hasTemplate) report(ATLAS_EXECUTOR.promptMissing);
2189
+ else if (hasPrompt && hasTemplate) report(ATLAS_EXECUTOR.promptBoth);
2190
+ } else {
2191
+ report(ATLAS_EXECUTOR.operationUnknown, executor);
2192
+ return;
2193
+ }
2194
+ const caps = q2.capabilities;
2195
+ if (Array.isArray(caps) && caps.some((c) => typeof c === "string" && c.includes(TEMPLATE_ACTION_OPEN))) {
2196
+ report(ATLAS_EXECUTOR.capabilitiesNotLiteral);
2197
+ }
2198
+ }
2096
2199
  function checkFunctionOperation(executor, field, stepID, templated, issues) {
2097
2200
  if (templated) return;
2098
2201
  const m = EXECUTOR_URL_PATTERN.exec(executor);
@@ -2117,6 +2220,7 @@ function validateExecutors(flow, issues) {
2117
2220
  const executor = issues.stringOf(step, "executor", `steps.${stepID}`);
2118
2221
  if (executor !== null && executor !== "") {
2119
2222
  checkExecutor(executor, `steps.${stepID}.executor`, stepID, issues);
2223
+ checkAtlasStep(executor, step.query, `steps.${stepID}`, stepID, issues);
2120
2224
  }
2121
2225
  const loop = step.loop;
2122
2226
  if (isRecord(loop) && Array.isArray(loop.steps)) {
@@ -2133,6 +2237,7 @@ function validateExecutors(flow, issues) {
2133
2237
  `${stepID}.${subID}`,
2134
2238
  issues
2135
2239
  );
2240
+ checkAtlasStep(subExecutor, subStep.query, subPath, `${stepID}.${subID}`, issues);
2136
2241
  }
2137
2242
  });
2138
2243
  }