@vaudience/aigentflow-flow-validator 0.20.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -256,6 +256,7 @@ interface Flow {
256
256
  input_schema?: InputSchema;
257
257
  classification?: Record<string, unknown>;
258
258
  is_template?: boolean;
259
+ strict_templates?: boolean;
259
260
  template?: Record<string, unknown> | null;
260
261
  [key: string]: unknown;
261
262
  }
package/dist/index.d.ts CHANGED
@@ -256,6 +256,7 @@ interface Flow {
256
256
  input_schema?: InputSchema;
257
257
  classification?: Record<string, unknown>;
258
258
  is_template?: boolean;
259
+ strict_templates?: boolean;
259
260
  template?: Record<string, unknown> | null;
260
261
  [key: string]: unknown;
261
262
  }
package/dist/index.js CHANGED
@@ -302,6 +302,17 @@ function isValidGoDuration(input) {
302
302
  var RESERVED_STEP_ID_CHAR = ".";
303
303
  var RESERVED_STEP_ID_ORCHESTRATOR = "orchestrator";
304
304
  var DISPLAY_NAME_MAX_LEN = 80;
305
+ var YAML11_BOOL_WORDS = new Set(
306
+ ["y", "yes", "on", "n", "no", "off"].flatMap((w) => [
307
+ w,
308
+ w.toUpperCase(),
309
+ w[0].toUpperCase() + w.slice(1)
310
+ ])
311
+ );
312
+ function isBoolLike(v) {
313
+ if (v === null || typeof v === "boolean") return true;
314
+ return typeof v === "string" && YAML11_BOOL_WORDS.has(v);
315
+ }
305
316
  function validateBasicStructure(flow, issues) {
306
317
  if (!issues.nonEmptyStringOf(flow, "aigentflow_version", "")) {
307
318
  issues.error({
@@ -331,6 +342,14 @@ function validateBasicStructure(flow, issues) {
331
342
  });
332
343
  }
333
344
  }
345
+ if (flow.strict_templates !== void 0 && !isBoolLike(flow.strict_templates)) {
346
+ issues.error({
347
+ field: "strict_templates",
348
+ message: "strict_templates must be a boolean (true or false)",
349
+ code: "invalid_type",
350
+ suggestion: "Write 'strict_templates: true' or remove the key"
351
+ });
352
+ }
334
353
  const start = issues.stringOf(flow, "start", "");
335
354
  if (start === null || start === "") {
336
355
  issues.error({
@@ -430,7 +449,7 @@ function validateBasicStructure(flow, issues) {
430
449
 
431
450
  // src/spec/aigentflow-spec.json
432
451
  var aigentflow_spec_default = {
433
- specVersion: "2.821.0",
452
+ specVersion: "2.828.0",
434
453
  inputSchemaVersion: 1,
435
454
  executorSchemes: [
436
455
  "a2a",
@@ -454,6 +473,7 @@ var aigentflow_spec_default = {
454
473
  "geocoding",
455
474
  "geovisualize",
456
475
  "getmd",
476
+ "here",
457
477
  "homeassistant",
458
478
  "hosting",
459
479
  "http",
@@ -926,6 +946,33 @@ var aigentflow_spec_default = {
926
946
  stepFieldFormat: "steps.%s.query.%s",
927
947
  loopStepFieldFormat: "steps.%s.loop.steps.%s.query.%s"
928
948
  },
949
+ flowDataReservedKeys: {
950
+ code: "flow_data_key_reserved",
951
+ fieldFormat: "data.%s",
952
+ messageFormat: "data: key %s is reserved: %s. It would write into the engine's own state, so it is not seeded.",
953
+ suggestion: "Rename the key (no leading `_` or `.`, and not one of the engine namespaces memory., orchestrator., campaign., voiceagent.).",
954
+ exact: {
955
+ org_id: "the mission's organization, set by the engine for Thalamus/Engram calls",
956
+ memory: "the memory scope namespace",
957
+ orchestrator: "the orchestrator's state namespace",
958
+ campaign: "the campaign's state namespace",
959
+ voiceagent: "the voice agent's state namespace",
960
+ current_flow: "engine bookkeeping (the current flow definition)",
961
+ modified_flow: "engine bookkeeping (dynamic flow modification)",
962
+ modification_history: "engine bookkeeping (dynamic flow modification)",
963
+ modification_pending: "engine bookkeeping (dynamic flow modification)",
964
+ modification_requests: "engine bookkeeping (dynamic flow modification)",
965
+ bottleneck_state: "engine bookkeeping (bottleneck analysis)"
966
+ },
967
+ prefixes: [
968
+ { prefix: "_", reason: "keys starting with `_` are engine bookkeeping (current flow, loop and for_each context, taint lists such as `__untrusted_*`)" },
969
+ { prefix: ".", reason: "keys starting with `.` are engine step-response slots" },
970
+ { prefix: "memory.", reason: "the `memory.` namespace belongs to the engine" },
971
+ { prefix: "orchestrator.", reason: "the `orchestrator.` namespace belongs to the orchestrator" },
972
+ { prefix: "campaign.", reason: "the `campaign.` namespace belongs to the campaign engine" },
973
+ { prefix: "voiceagent.", reason: "the `voiceagent.` namespace belongs to the voice agent" }
974
+ ]
975
+ },
929
976
  credentialEndpointPairing: {
930
977
  stepFieldFormat: "steps.%s.query.%s",
931
978
  loopStepFieldFormat: "steps.%s.loop.steps.%s.query.%s",
@@ -1255,6 +1302,11 @@ var aigentflow_spec_default = {
1255
1302
  }
1256
1303
  }
1257
1304
  },
1305
+ templateDefaultPiped: {
1306
+ code: "template_default_piped",
1307
+ messageFormat: "`default` takes the value FIRST, and a pipe passes the piped value LAST: `%s` is `default <fallback> <value>`, so the fallback is always the result",
1308
+ suggestion: 'write the value first and the fallback second, as a call: `{{ default .x "fallback" }}`'
1309
+ },
1258
1310
  complianceResidency: {
1259
1311
  code: "compliance_residency_unenforced",
1260
1312
  euResidencyCodes: ["EU", "DE", "FR", "IE", "NL", "IT", "ES"],
@@ -1553,6 +1605,7 @@ var aigentflow_spec_default = {
1553
1605
  rate_limit: "FlowRateLimitConfig",
1554
1606
  start: "scalar",
1555
1607
  steps: "map<StepDefinition>",
1608
+ strict_templates: "scalar",
1556
1609
  tags: "list<scalar>",
1557
1610
  template: "FlowTemplateMetadata",
1558
1611
  tool_discovery: "scalar",
@@ -1857,6 +1910,14 @@ var SERVER_OWNED_QUERY_KEYS = {
1857
1910
  stepFieldFormat: aigentflow_spec_default.serverOwnedQueryKeys.stepFieldFormat,
1858
1911
  loopStepFieldFormat: aigentflow_spec_default.serverOwnedQueryKeys.loopStepFieldFormat
1859
1912
  };
1913
+ var FLOW_DATA_RESERVED_KEYS = {
1914
+ code: aigentflow_spec_default.flowDataReservedKeys.code,
1915
+ fieldFormat: aigentflow_spec_default.flowDataReservedKeys.fieldFormat,
1916
+ messageFormat: aigentflow_spec_default.flowDataReservedKeys.messageFormat,
1917
+ suggestion: aigentflow_spec_default.flowDataReservedKeys.suggestion,
1918
+ exact: aigentflow_spec_default.flowDataReservedKeys.exact,
1919
+ prefixes: aigentflow_spec_default.flowDataReservedKeys.prefixes
1920
+ };
1860
1921
  var CREDENTIAL_ENDPOINT_PAIRING = {
1861
1922
  stepFieldFormat: aigentflow_spec_default.credentialEndpointPairing.stepFieldFormat,
1862
1923
  loopStepFieldFormat: aigentflow_spec_default.credentialEndpointPairing.loopStepFieldFormat,
@@ -1914,6 +1975,11 @@ var EXAMPLES = {
1914
1975
  name: p.name,
1915
1976
  re: new RegExp(p.expr, p.flags)
1916
1977
  }))};
1978
+ var TEMPLATE_DEFAULT_PIPED = {
1979
+ code: aigentflow_spec_default.templateDefaultPiped.code,
1980
+ messageFormat: aigentflow_spec_default.templateDefaultPiped.messageFormat,
1981
+ suggestion: aigentflow_spec_default.templateDefaultPiped.suggestion
1982
+ };
1917
1983
 
1918
1984
  // src/validators/executors.ts
1919
1985
  var SCHEME_SEPARATOR = "://";
@@ -3771,16 +3837,16 @@ function skipSpaces(src, idx) {
3771
3837
  }
3772
3838
  return i;
3773
3839
  }
3774
- function scanString(src, start, quote) {
3840
+ function scanString(src, start, quote2) {
3775
3841
  let i = start + 1;
3776
- const raw = quote === "`";
3842
+ const raw = quote2 === "`";
3777
3843
  while (i < src.length) {
3778
3844
  const c = src[i];
3779
3845
  if (!raw && c === "\\") {
3780
3846
  i += 2;
3781
3847
  continue;
3782
3848
  }
3783
- if (c === quote) return i;
3849
+ if (c === quote2) return i;
3784
3850
  i += 1;
3785
3851
  }
3786
3852
  return -1;
@@ -3877,6 +3943,117 @@ function splitPipeline(body) {
3877
3943
  return stages;
3878
3944
  }
3879
3945
 
3946
+ // src/template/gotmpl-default.ts
3947
+ var FUNCTION_NAME = "default";
3948
+ var PIPE = "|";
3949
+ var OPEN_PAREN = "(";
3950
+ var CLOSE_PAREN = ")";
3951
+ var MAX_NESTING_DEPTH = 100;
3952
+ var PIPELINE_KEYWORDS = /^(?:else\s+if|else\s+with|if|range|with|template|block)(?![\w$.])/;
3953
+ var NO_PIPELINE = /^(?:define|else|end|break|continue)(?![\w$.])/;
3954
+ var DECLARATION = /^\$\w*(?:\s*,\s*\$\w*)*\s*:?=/;
3955
+ function pipedDefaultPipelines(src) {
3956
+ const { actions, fatal } = lexActions(src);
3957
+ if (fatal) return [];
3958
+ const found = /* @__PURE__ */ new Set();
3959
+ try {
3960
+ for (const action of actions) {
3961
+ if (action.isComment || action.body === "") continue;
3962
+ scanAction(action.body, found);
3963
+ }
3964
+ } catch (err) {
3965
+ if (!(err instanceof RangeError)) throw err;
3966
+ return [];
3967
+ }
3968
+ return [...found].sort();
3969
+ }
3970
+ function scanAction(body, found) {
3971
+ let rest = body;
3972
+ const kw = PIPELINE_KEYWORDS.exec(rest);
3973
+ if (!kw && NO_PIPELINE.test(rest)) return;
3974
+ if (kw) {
3975
+ rest = rest.slice(kw[0].length).trimStart();
3976
+ const keyword = kw[0].replace(/\s+/g, " ");
3977
+ if (keyword === "template" || keyword === "block") {
3978
+ const q2 = rest[0];
3979
+ if (q2 === '"' || q2 === "`") {
3980
+ const end = scanString(rest, 0, q2);
3981
+ if (end === -1) return;
3982
+ rest = rest.slice(end + 1).trimStart();
3983
+ }
3984
+ }
3985
+ }
3986
+ if (rest === "") return;
3987
+ parsePipeline(rest, 0, false, found, 0);
3988
+ }
3989
+ function parsePipeline(src, start, nested, found, depth) {
3990
+ if (depth > MAX_NESTING_DEPTH) return { text: src.slice(start), end: src.length };
3991
+ let i = start;
3992
+ const n = src.length;
3993
+ let decl = "";
3994
+ const decMatch = DECLARATION.exec(src.slice(i));
3995
+ if (decMatch) {
3996
+ decl = decMatch[0].replace(/\s+/g, " ") + " ";
3997
+ i += decMatch[0].length;
3998
+ }
3999
+ const commands = [];
4000
+ let current = [];
4001
+ let firstWords = [];
4002
+ let pipedDefault = false;
4003
+ const pushCommand = () => {
4004
+ if (current.length > 0) {
4005
+ commands.push(current);
4006
+ firstWords.push(current[0]);
4007
+ }
4008
+ current = [];
4009
+ };
4010
+ while (i < n) {
4011
+ const c = src[i];
4012
+ if (c === " " || c === " " || c === "\n" || c === "\r") {
4013
+ i += 1;
4014
+ continue;
4015
+ }
4016
+ if (c === '"' || c === "`" || c === "'") {
4017
+ const end = scanString(src, i, c);
4018
+ const stop = end === -1 ? n - 1 : end;
4019
+ current.push(src.slice(i, stop + 1));
4020
+ i = stop + 1;
4021
+ continue;
4022
+ }
4023
+ if (c === OPEN_PAREN) {
4024
+ const inner = parsePipeline(src, i + 1, true, found, depth + 1);
4025
+ current.push(`(${inner.text})`);
4026
+ i = inner.end;
4027
+ continue;
4028
+ }
4029
+ if (c === CLOSE_PAREN) {
4030
+ if (nested) {
4031
+ i += 1;
4032
+ break;
4033
+ }
4034
+ i += 1;
4035
+ continue;
4036
+ }
4037
+ if (c === PIPE) {
4038
+ pushCommand();
4039
+ i += 1;
4040
+ continue;
4041
+ }
4042
+ let j = i;
4043
+ while (j < n && !/[\s|()"`']/.test(src[j])) j += 1;
4044
+ if (j === i) j = i + 1;
4045
+ current.push(src.slice(i, j));
4046
+ i = j;
4047
+ }
4048
+ pushCommand();
4049
+ firstWords.forEach((word, idx) => {
4050
+ if (idx > 0 && word === FUNCTION_NAME) pipedDefault = true;
4051
+ });
4052
+ const text = decl + commands.map((cmd) => cmd.join(" ")).join(` ${PIPE} `);
4053
+ if (pipedDefault) found.add(text);
4054
+ return { text, end: i };
4055
+ }
4056
+
3880
4057
  // src/validators/processingOperations.ts
3881
4058
  var PROCESSING_OP_GUARD_KEY = PROCESSING_OPERATIONS.guardKey;
3882
4059
  var PROCESSING_PHASES = ["pre_processing", "post_processing"];
@@ -4023,6 +4200,17 @@ function checkTemplateString(value, field, stepID, issues, stats, opts, demote =
4023
4200
  });
4024
4201
  }
4025
4202
  }
4203
+ if (errors.length > 0) return;
4204
+ for (const pipe of pipedDefaultPipelines(value)) {
4205
+ report({
4206
+ field,
4207
+ message: TEMPLATE_DEFAULT_PIPED.messageFormat.replace("%s", () => pipe),
4208
+ code: TEMPLATE_DEFAULT_PIPED.code,
4209
+ stepId: stepID,
4210
+ context: `Template: ${value}`,
4211
+ suggestion: TEMPLATE_DEFAULT_PIPED.suggestion
4212
+ });
4213
+ }
4026
4214
  }
4027
4215
  function walkStrings2(basePath, value, stepID, issues, stats, opts, check, demote = false) {
4028
4216
  if (isString(value)) {
@@ -4261,6 +4449,35 @@ function validateRetiredKeys(flow, issues) {
4261
4449
  }
4262
4450
  }
4263
4451
 
4452
+ // src/validators/flowDataKeys.ts
4453
+ var KEY_DATA = "data";
4454
+ function quote(key) {
4455
+ return JSON.stringify(key);
4456
+ }
4457
+ function flowDataKeyReservation(key) {
4458
+ if (Object.prototype.hasOwnProperty.call(FLOW_DATA_RESERVED_KEYS.exact, key)) {
4459
+ return FLOW_DATA_RESERVED_KEYS.exact[key];
4460
+ }
4461
+ for (const p of FLOW_DATA_RESERVED_KEYS.prefixes) {
4462
+ if (key.startsWith(p.prefix)) return p.reason;
4463
+ }
4464
+ return void 0;
4465
+ }
4466
+ function validateFlowDataKeys(flow, issues) {
4467
+ const data = flow[KEY_DATA];
4468
+ if (!isRecord(data)) return;
4469
+ for (const key of Object.keys(data).sort()) {
4470
+ const reason = flowDataKeyReservation(key);
4471
+ if (reason === void 0) continue;
4472
+ issues.error({
4473
+ field: FLOW_DATA_RESERVED_KEYS.fieldFormat.replace("%s", () => key),
4474
+ message: FLOW_DATA_RESERVED_KEYS.messageFormat.replace("%s", () => quote(key)).replace("%s", () => reason),
4475
+ code: FLOW_DATA_RESERVED_KEYS.code,
4476
+ suggestion: FLOW_DATA_RESERVED_KEYS.suggestion
4477
+ });
4478
+ }
4479
+ }
4480
+
4264
4481
  // src/validators/saveDoor.ts
4265
4482
  var TOOL_DISCOVERY_MODES = /* @__PURE__ */ new Set(["eager", "lazy", "off"]);
4266
4483
  var TEMPLATE_ACTION_OPEN2 = "{{";
@@ -6723,6 +6940,7 @@ function validateParsed(flow, opts, sources) {
6723
6940
  validateSaveDoorExtras(f, issues);
6724
6941
  validateExecutorConfigEnvScopes(f, issues);
6725
6942
  validateServerOwnedQueryKeys(f, issues);
6943
+ validateFlowDataKeys(f, issues);
6726
6944
  validateCredentialEndpointPairing(f, issues);
6727
6945
  validateExamples(f, issues);
6728
6946
  validateOrchestratorExons(f, issues);