@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/PARITY.md +103 -35
- package/README.md +3 -2
- package/dist/cli.js +226 -4
- package/dist/index.cjs +222 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +222 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
package/dist/index.d.ts
CHANGED
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.
|
|
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,
|
|
3840
|
+
function scanString(src, start, quote2) {
|
|
3775
3841
|
let i = start + 1;
|
|
3776
|
-
const raw =
|
|
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 ===
|
|
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);
|