@vaudience/aigentflow-flow-validator 0.25.0 → 0.27.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 +69 -3
- package/README.md +2 -1
- package/dist/cli.js +469 -284
- package/dist/index.cjs +462 -273
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +462 -273
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -449,11 +449,10 @@ function validateBasicStructure(flow, issues) {
|
|
|
449
449
|
|
|
450
450
|
// src/spec/aigentflow-spec.json
|
|
451
451
|
var aigentflow_spec_default = {
|
|
452
|
-
specVersion: "2.
|
|
452
|
+
specVersion: "2.833.0",
|
|
453
453
|
inputSchemaVersion: 1,
|
|
454
454
|
executorSchemes: [
|
|
455
455
|
"a2a",
|
|
456
|
-
"agent",
|
|
457
456
|
"ai",
|
|
458
457
|
"aiv",
|
|
459
458
|
"async",
|
|
@@ -467,7 +466,6 @@ var aigentflow_spec_default = {
|
|
|
467
466
|
"email",
|
|
468
467
|
"eurostat",
|
|
469
468
|
"eval",
|
|
470
|
-
"exons",
|
|
471
469
|
"flow",
|
|
472
470
|
"function",
|
|
473
471
|
"gdelt",
|
|
@@ -766,6 +764,32 @@ var aigentflow_spec_default = {
|
|
|
766
764
|
exons: {
|
|
767
765
|
frontmatterDelimiter: "---"
|
|
768
766
|
},
|
|
767
|
+
removedAgentSurfaces: {
|
|
768
|
+
removedSchemes: ["exons", "agent"],
|
|
769
|
+
paramScheme: "ai",
|
|
770
|
+
aiParams: [
|
|
771
|
+
"agentic",
|
|
772
|
+
"deny_tools",
|
|
773
|
+
"discoverable_tools",
|
|
774
|
+
"enable_signals",
|
|
775
|
+
"guardrails",
|
|
776
|
+
"max_tool_calls",
|
|
777
|
+
"max_tool_turns",
|
|
778
|
+
"mcp_server_url",
|
|
779
|
+
"parallel_tool_calls",
|
|
780
|
+
"require_confirmation",
|
|
781
|
+
"timeout_seconds",
|
|
782
|
+
"tool_auth_token",
|
|
783
|
+
"tool_discovery"
|
|
784
|
+
],
|
|
785
|
+
flowKeys: ["exons_uploads"],
|
|
786
|
+
executorRemovedCode: "executor_removed",
|
|
787
|
+
agenticParamRemovedCode: "agentic_param_removed",
|
|
788
|
+
schemeMessageFormat: "step %q uses %s://, which AIgentFlow no longer runs: its own agent chains were removed",
|
|
789
|
+
paramsMessageFormat: "step %q sets %s on ai://: AIgentFlow's own agent loop (agentic mode) was removed, so these parameters no longer exist",
|
|
790
|
+
flowKeyMessageFormat: "the flow-level key %q belonged to a removed agent surface and is no longer read",
|
|
791
|
+
suggestion: 'Rebuild the step: a tool-using agent becomes atlas://agent/run (agent: "@org/name", prompt, capabilities); one tool becomes atlas://capability/<ref>; a plain prompt becomes ai://<provider>/chat or a prompt-only aiv://<org>/<name>. A flow that cannot be converted (started by a webhook, a schedule, a share link, a campaign or a flow:// sub-flow, none of which has a person for atlas to act for; or one that needs an inline .exons spec, an arbitrary MCP server URL or signals) has no replacement here: start it from a person, or use a routine in atlas'
|
|
792
|
+
},
|
|
769
793
|
executorConfigEnvScopes: {
|
|
770
794
|
referencePrefix: "${",
|
|
771
795
|
referenceSuffix: "}",
|
|
@@ -992,7 +1016,7 @@ var aigentflow_spec_default = {
|
|
|
992
1016
|
providerAgent: "agent",
|
|
993
1017
|
providerCapability: "capability",
|
|
994
1018
|
agentResource: "run",
|
|
995
|
-
capabilityRefPattern: "^[A-Za-z0-9][A-Za-z0-9_
|
|
1019
|
+
capabilityRefPattern: "^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$",
|
|
996
1020
|
operationUnknown: {
|
|
997
1021
|
code: "atlas_operation_unknown",
|
|
998
1022
|
messageFormat: "step %s uses %s, which is not an Atlas operation. The providers are agent (resource run) and capability (resource: a capability reference).",
|
|
@@ -1000,7 +1024,7 @@ var aigentflow_spec_default = {
|
|
|
1000
1024
|
},
|
|
1001
1025
|
agentMissing: {
|
|
1002
1026
|
code: "atlas_agent_missing",
|
|
1003
|
-
messageFormat: "step %s runs an Atlas agent but query.agent is missing or
|
|
1027
|
+
messageFormat: "step %s runs an Atlas agent but query.agent is missing, empty or not a string.",
|
|
1004
1028
|
suggestion: "Set query.agent to the agent to run, written out as a literal such as @acme/reporter."
|
|
1005
1029
|
},
|
|
1006
1030
|
agentNotLiteral: {
|
|
@@ -1026,9 +1050,46 @@ var aigentflow_spec_default = {
|
|
|
1026
1050
|
capabilityRefInvalid: {
|
|
1027
1051
|
code: "atlas_capability_ref_invalid",
|
|
1028
1052
|
messageFormat: "step %s uses atlas://capability/%s, which is not a valid capability reference.",
|
|
1029
|
-
suggestion: "A capability reference starts with a letter or digit and continues with letters, digits
|
|
1053
|
+
suggestion: "A capability reference starts with a letter or digit and continues with letters, digits, _ and - up to 128 characters in all."
|
|
1054
|
+
},
|
|
1055
|
+
modelNotLiteral: {
|
|
1056
|
+
code: "atlas_model_not_literal",
|
|
1057
|
+
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.",
|
|
1058
|
+
suggestion: "Write the model choice out as literals. Data may go into the prompt or the inputs instead."
|
|
1059
|
+
},
|
|
1060
|
+
paramRefused: {
|
|
1061
|
+
code: "atlas_param_refused",
|
|
1062
|
+
messageFormat: "step %s sets %s under query. Atlas decides these itself, so a step may not set them.",
|
|
1063
|
+
suggestion: "Remove these keys from the query.",
|
|
1064
|
+
agentKeys: [
|
|
1065
|
+
"api_key",
|
|
1066
|
+
"attribution",
|
|
1067
|
+
"base_url",
|
|
1068
|
+
"host",
|
|
1069
|
+
"idempotency_key",
|
|
1070
|
+
"org_id",
|
|
1071
|
+
"person",
|
|
1072
|
+
"untrusted_input",
|
|
1073
|
+
"untrusted_input_override",
|
|
1074
|
+
"url",
|
|
1075
|
+
"user_id"
|
|
1076
|
+
],
|
|
1077
|
+
capabilityKeys: [
|
|
1078
|
+
"attribution",
|
|
1079
|
+
"idempotency_key",
|
|
1080
|
+
"untrusted_input",
|
|
1081
|
+
"untrusted_input_override"
|
|
1082
|
+
]
|
|
1030
1083
|
}
|
|
1031
1084
|
},
|
|
1085
|
+
reservedInputNames: {
|
|
1086
|
+
prefix: "__",
|
|
1087
|
+
code: "reserved_input_name",
|
|
1088
|
+
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.",
|
|
1089
|
+
suggestion: 'Rename the input so it does not start with "__".',
|
|
1090
|
+
queryFieldFormat: "query.%s",
|
|
1091
|
+
schemaFieldFormat: "input_schema.fields.%s"
|
|
1092
|
+
},
|
|
1032
1093
|
flowDataReservedKeys: {
|
|
1033
1094
|
code: "flow_data_key_reserved",
|
|
1034
1095
|
fieldFormat: "data.%s",
|
|
@@ -1981,6 +2042,19 @@ var EXONS = {
|
|
|
1981
2042
|
/** The YAML frontmatter delimiter line. */
|
|
1982
2043
|
frontmatterDelimiter: aigentflow_spec_default.exons.frontmatterDelimiter
|
|
1983
2044
|
};
|
|
2045
|
+
var removed = aigentflow_spec_default.removedAgentSurfaces;
|
|
2046
|
+
var REMOVED_AGENT_SURFACES = {
|
|
2047
|
+
schemes: new Set(removed.removedSchemes),
|
|
2048
|
+
paramScheme: removed.paramScheme,
|
|
2049
|
+
aiParams: new Set(removed.aiParams),
|
|
2050
|
+
flowKeys: removed.flowKeys,
|
|
2051
|
+
executorRemovedCode: removed.executorRemovedCode,
|
|
2052
|
+
agenticParamRemovedCode: removed.agenticParamRemovedCode,
|
|
2053
|
+
schemeMessageFormat: removed.schemeMessageFormat,
|
|
2054
|
+
paramsMessageFormat: removed.paramsMessageFormat,
|
|
2055
|
+
flowKeyMessageFormat: removed.flowKeyMessageFormat,
|
|
2056
|
+
suggestion: removed.suggestion
|
|
2057
|
+
};
|
|
1984
2058
|
var EXECUTOR_CONFIG_ENV_SCOPES = {
|
|
1985
2059
|
referencePrefix: aigentflow_spec_default.executorConfigEnvScopes.referencePrefix,
|
|
1986
2060
|
referenceSuffix: aigentflow_spec_default.executorConfigEnvScopes.referenceSuffix,
|
|
@@ -2084,8 +2158,245 @@ var ATLAS_EXECUTOR = {
|
|
|
2084
2158
|
capabilitiesNotLiteral: atlas.capabilitiesNotLiteral,
|
|
2085
2159
|
promptMissing: atlas.promptMissing,
|
|
2086
2160
|
promptBoth: atlas.promptBoth,
|
|
2087
|
-
capabilityRefInvalid: atlas.capabilityRefInvalid
|
|
2161
|
+
capabilityRefInvalid: atlas.capabilityRefInvalid,
|
|
2162
|
+
modelNotLiteral: atlas.modelNotLiteral,
|
|
2163
|
+
paramRefused: {
|
|
2164
|
+
code: atlas.paramRefused.code,
|
|
2165
|
+
messageFormat: atlas.paramRefused.messageFormat,
|
|
2166
|
+
suggestion: atlas.paramRefused.suggestion,
|
|
2167
|
+
agentKeys: atlas.paramRefused.agentKeys,
|
|
2168
|
+
capabilityKeys: atlas.paramRefused.capabilityKeys
|
|
2169
|
+
}
|
|
2170
|
+
};
|
|
2171
|
+
var RESERVED_INPUT_NAMES = {
|
|
2172
|
+
prefix: aigentflow_spec_default.reservedInputNames.prefix,
|
|
2173
|
+
code: aigentflow_spec_default.reservedInputNames.code,
|
|
2174
|
+
messageFormat: aigentflow_spec_default.reservedInputNames.messageFormat,
|
|
2175
|
+
suggestion: aigentflow_spec_default.reservedInputNames.suggestion,
|
|
2176
|
+
queryFieldFormat: aigentflow_spec_default.reservedInputNames.queryFieldFormat,
|
|
2177
|
+
schemaFieldFormat: aigentflow_spec_default.reservedInputNames.schemaFieldFormat
|
|
2178
|
+
};
|
|
2179
|
+
var CODE_PARSE_FAILED = "orchestrator_exons_parse_failed";
|
|
2180
|
+
var CODE_NO_PROVIDER = "orchestrator_exons_no_provider";
|
|
2181
|
+
var CODE_RESOURCES = "exons_resources_unhonoured";
|
|
2182
|
+
var CODE_TOOL_WITHHELD = "orchestrator_tool_withheld";
|
|
2183
|
+
var EXONS_OPEN_DELIM = "{~";
|
|
2184
|
+
var EXONS_LEGACY_CONFIG_OPEN = "{~exons.config~}";
|
|
2185
|
+
var UTF8_BOM = "\uFEFF";
|
|
2186
|
+
var MAX_ALIAS_COUNT2 = 100;
|
|
2187
|
+
var UNREAD = {
|
|
2188
|
+
parseError: null,
|
|
2189
|
+
specRead: false,
|
|
2190
|
+
hasSpec: false,
|
|
2191
|
+
provider: "",
|
|
2192
|
+
toolsAllowDeclared: false,
|
|
2193
|
+
toolsAllow: [],
|
|
2194
|
+
resources: []
|
|
2195
|
+
};
|
|
2196
|
+
var GO_SPACE = "\\t\\n\\v\\f\\r \\u0085\\u00a0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000";
|
|
2197
|
+
var GO_TRIM_SPACE = new RegExp(`^[${GO_SPACE}]+|[${GO_SPACE}]+$`, "g");
|
|
2198
|
+
function goTrimSpace(s) {
|
|
2199
|
+
return s.replace(GO_TRIM_SPACE, "");
|
|
2200
|
+
}
|
|
2201
|
+
function extractExonsFrontmatter(source) {
|
|
2202
|
+
const delim = EXONS.frontmatterDelimiter;
|
|
2203
|
+
let start = source.startsWith(UTF8_BOM) ? source.slice(UTF8_BOM.length) : source;
|
|
2204
|
+
start = start.replace(/^[ \t]+/, "");
|
|
2205
|
+
if (start.startsWith(EXONS_LEGACY_CONFIG_OPEN)) {
|
|
2206
|
+
return {
|
|
2207
|
+
frontmatter: "",
|
|
2208
|
+
hasFrontmatter: false,
|
|
2209
|
+
parseError: "legacy JSON config block detected; use YAML frontmatter"
|
|
2210
|
+
};
|
|
2211
|
+
}
|
|
2212
|
+
if (!start.startsWith(delim)) return { frontmatter: "", hasFrontmatter: false, parseError: null };
|
|
2213
|
+
let rest = start.slice(delim.length);
|
|
2214
|
+
if (rest.startsWith("\n")) rest = rest.slice(1);
|
|
2215
|
+
else if (rest.startsWith("\r\n")) rest = rest.slice(2);
|
|
2216
|
+
else return { frontmatter: "", hasFrontmatter: false, parseError: null };
|
|
2217
|
+
let pos = 0;
|
|
2218
|
+
while (pos <= rest.length) {
|
|
2219
|
+
const line = rest.slice(pos);
|
|
2220
|
+
if (line.startsWith(delim)) {
|
|
2221
|
+
const after = line.slice(delim.length);
|
|
2222
|
+
if (after === "" || after[0] === "\n" || after[0] === "\r") {
|
|
2223
|
+
return {
|
|
2224
|
+
frontmatter: goTrimSpace(rest.slice(0, pos)),
|
|
2225
|
+
hasFrontmatter: true,
|
|
2226
|
+
parseError: null
|
|
2227
|
+
};
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
const nl = line.indexOf("\n");
|
|
2231
|
+
if (nl < 0) break;
|
|
2232
|
+
pos += nl + 1;
|
|
2233
|
+
}
|
|
2234
|
+
return {
|
|
2235
|
+
frontmatter: "",
|
|
2236
|
+
hasFrontmatter: true,
|
|
2237
|
+
parseError: "YAML frontmatter not properly closed"
|
|
2238
|
+
};
|
|
2239
|
+
}
|
|
2240
|
+
function retagScalars(node) {
|
|
2241
|
+
if (isMap(node)) {
|
|
2242
|
+
for (const pair of node.items) {
|
|
2243
|
+
retagScalars(pair.key);
|
|
2244
|
+
retagScalars(pair.value);
|
|
2245
|
+
}
|
|
2246
|
+
} else if (isSeq(node)) {
|
|
2247
|
+
for (const item of node.items) retagScalars(item);
|
|
2248
|
+
} else if (isScalar(node) && (typeof node.value === "number" || typeof node.value === "boolean") && typeof node.source === "string") {
|
|
2249
|
+
node.value = node.source;
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
var DecodeError = class extends Error {
|
|
2088
2253
|
};
|
|
2254
|
+
function goString(v) {
|
|
2255
|
+
if (v === null || v === void 0) return "";
|
|
2256
|
+
if (isString(v)) return v;
|
|
2257
|
+
throw new DecodeError("not a string");
|
|
2258
|
+
}
|
|
2259
|
+
function goStringList(v) {
|
|
2260
|
+
if (v === null || v === void 0) return null;
|
|
2261
|
+
if (!isArray(v)) throw new DecodeError("not a list");
|
|
2262
|
+
return v.filter((item) => item !== null).map(goString);
|
|
2263
|
+
}
|
|
2264
|
+
function goStruct(v) {
|
|
2265
|
+
if (v === null || v === void 0) return null;
|
|
2266
|
+
if (!isRecord(v)) throw new DecodeError("not a mapping");
|
|
2267
|
+
return v;
|
|
2268
|
+
}
|
|
2269
|
+
function decodeSpecFields(frontmatter) {
|
|
2270
|
+
let value;
|
|
2271
|
+
try {
|
|
2272
|
+
const docs = parseAllDocuments(frontmatter, { merge: true });
|
|
2273
|
+
const doc = Array.isArray(docs) ? docs[0] : void 0;
|
|
2274
|
+
if (doc === void 0) {
|
|
2275
|
+
value = null;
|
|
2276
|
+
} else {
|
|
2277
|
+
if (doc.errors.length > 0) return null;
|
|
2278
|
+
retagScalars(doc.contents);
|
|
2279
|
+
value = doc.toJS({ maxAliasCount: MAX_ALIAS_COUNT2 });
|
|
2280
|
+
}
|
|
2281
|
+
} catch {
|
|
2282
|
+
return null;
|
|
2283
|
+
}
|
|
2284
|
+
try {
|
|
2285
|
+
const root = goStruct(value) ?? {};
|
|
2286
|
+
const execution = goStruct(root.execution);
|
|
2287
|
+
const tools = goStruct(root.tools);
|
|
2288
|
+
const requirements = goStruct(root.requirements);
|
|
2289
|
+
const allow = tools === null ? null : goStringList(tools.allow);
|
|
2290
|
+
let resources = [];
|
|
2291
|
+
if (requirements !== null) {
|
|
2292
|
+
const raw = requirements.resources;
|
|
2293
|
+
if (raw !== null && raw !== void 0) {
|
|
2294
|
+
if (!isArray(raw)) throw new DecodeError("not a list");
|
|
2295
|
+
resources = raw.filter((entry) => entry !== null).map((entry) => {
|
|
2296
|
+
const r = goStruct(entry) ?? {};
|
|
2297
|
+
return { ref: goString(r.ref), kind: goString(r.kind), scope: goString(r.scope) };
|
|
2298
|
+
});
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
return {
|
|
2302
|
+
provider: execution === null ? "" : goString(execution.provider),
|
|
2303
|
+
toolsAllowDeclared: allow !== null,
|
|
2304
|
+
toolsAllow: allow ?? [],
|
|
2305
|
+
resources
|
|
2306
|
+
};
|
|
2307
|
+
} catch (e) {
|
|
2308
|
+
if (e instanceof DecodeError) return null;
|
|
2309
|
+
throw e;
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
function inspectExonsFrontmatter(source) {
|
|
2313
|
+
const fm = extractExonsFrontmatter(source);
|
|
2314
|
+
if (fm.parseError !== null) return { ...UNREAD, parseError: fm.parseError };
|
|
2315
|
+
if (!fm.hasFrontmatter || fm.frontmatter === "") return { ...UNREAD, specRead: true };
|
|
2316
|
+
if (fm.frontmatter.includes(EXONS_OPEN_DELIM)) return { ...UNREAD };
|
|
2317
|
+
const fields = decodeSpecFields(fm.frontmatter);
|
|
2318
|
+
if (fields === null) return { ...UNREAD };
|
|
2319
|
+
return { parseError: null, specRead: true, hasSpec: true, ...fields };
|
|
2320
|
+
}
|
|
2321
|
+
function validateOrchestratorExons(flow, issues) {
|
|
2322
|
+
const orch = flow.orchestrator;
|
|
2323
|
+
if (!isRecord(orch)) return;
|
|
2324
|
+
const source = issues.stringOf(orch, "exons", "orchestrator");
|
|
2325
|
+
if (source === null || source === "") return;
|
|
2326
|
+
const field = "orchestrator.exons";
|
|
2327
|
+
const report = inspectExonsFrontmatter(source);
|
|
2328
|
+
if (report.parseError !== null) {
|
|
2329
|
+
issues.error({
|
|
2330
|
+
field,
|
|
2331
|
+
code: CODE_PARSE_FAILED,
|
|
2332
|
+
message: `orchestrator: failed to parse exons spec: ${report.parseError}`
|
|
2333
|
+
});
|
|
2334
|
+
return;
|
|
2335
|
+
}
|
|
2336
|
+
if (!report.specRead) return;
|
|
2337
|
+
if (!report.hasSpec) {
|
|
2338
|
+
issues.error({
|
|
2339
|
+
field,
|
|
2340
|
+
code: CODE_PARSE_FAILED,
|
|
2341
|
+
message: "orchestrator: failed to parse exons spec: the document has no frontmatter spec",
|
|
2342
|
+
suggestion: "Start the document with a --- frontmatter block that sets execution.provider"
|
|
2343
|
+
});
|
|
2344
|
+
return;
|
|
2345
|
+
}
|
|
2346
|
+
if (report.provider === "") {
|
|
2347
|
+
issues.error({
|
|
2348
|
+
field,
|
|
2349
|
+
code: CODE_NO_PROVIDER,
|
|
2350
|
+
message: "orchestrator: exons spec must have execution.provider set"
|
|
2351
|
+
});
|
|
2352
|
+
}
|
|
2353
|
+
if (report.resources.length > 0) {
|
|
2354
|
+
const refs = report.resources.map((r) => `${JSON.stringify(r.ref)} (kind ${r.kind})`);
|
|
2355
|
+
issues.error({
|
|
2356
|
+
field,
|
|
2357
|
+
code: CODE_RESOURCES,
|
|
2358
|
+
message: "the orchestrator's exons spec declares requirements.resources that AIgentFlow cannot honour, so it is refused rather than run without the narrowing it asks for: " + refs.join("; "),
|
|
2359
|
+
suggestion: "Remove requirements.resources, and narrow the agent with tools.mcp_servers and tools.allow"
|
|
2360
|
+
});
|
|
2361
|
+
}
|
|
2362
|
+
warnOrchestratorToolWithheld(flow, orch, report, issues);
|
|
2363
|
+
}
|
|
2364
|
+
function warnOrchestratorToolWithheld(flow, orch, report, issues) {
|
|
2365
|
+
if (!report.toolsAllowDeclared) return;
|
|
2366
|
+
const cfg = ORCHESTRATOR_TOOL_ALLOW;
|
|
2367
|
+
const exempt = new Set(cfg.lifecycleTools);
|
|
2368
|
+
const signals = typeof orch.enable_signals === "boolean" ? orch.enable_signals : cfg.enableSignalsDefault;
|
|
2369
|
+
if (signals) for (const t of cfg.signalTools) exempt.add(t);
|
|
2370
|
+
const allow = new Set(report.toolsAllow);
|
|
2371
|
+
const withheld = (name) => !allow.has(name) && !exempt.has(name);
|
|
2372
|
+
const warn = (message) => issues.warn({ field: cfg.warningField, code: CODE_TOOL_WITHHELD, message });
|
|
2373
|
+
const named = [];
|
|
2374
|
+
if (isArray(orch.tools)) {
|
|
2375
|
+
orch.tools.forEach((raw, i) => {
|
|
2376
|
+
const name = issues.stringAt(raw, `orchestrator.tools[${i}]`);
|
|
2377
|
+
if (name !== null) named.push(name);
|
|
2378
|
+
});
|
|
2379
|
+
}
|
|
2380
|
+
if (named.length > 0) {
|
|
2381
|
+
for (const name of named) {
|
|
2382
|
+
if (!withheld(name)) continue;
|
|
2383
|
+
warn(
|
|
2384
|
+
`orchestrator tool '${name}' is named in orchestrator.tools but the orchestrator's exons definition's tools.allow does not name it, so it is never offered: a tool must pass both lists. Add '${name}' to tools.allow, or remove it from orchestrator.tools.`
|
|
2385
|
+
);
|
|
2386
|
+
}
|
|
2387
|
+
return;
|
|
2388
|
+
}
|
|
2389
|
+
if (withheld(cfg.askHumanTool)) {
|
|
2390
|
+
warn(
|
|
2391
|
+
`the orchestrator's exons definition has a tools.allow list that does not name ${cfg.askHumanTool}, so this orchestrator can never ask a person a question: human intervention is off. Add ${cfg.askHumanTool} to tools.allow if a human should be reachable, or ignore this if the orchestrator is meant to run unattended.`
|
|
2392
|
+
);
|
|
2393
|
+
}
|
|
2394
|
+
if (isRecord(flow.campaign) && !cfg.campaignTools.some((name) => !withheld(name))) {
|
|
2395
|
+
warn(
|
|
2396
|
+
`this flow declares a campaign, but the orchestrator's exons definition has a tools.allow list that names none of the campaign tools (${cfg.campaignTools.join(", ")}), so no child mission can be spawned or tracked. Add the campaign tools the orchestrator needs to tools.allow.`
|
|
2397
|
+
);
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2089
2400
|
|
|
2090
2401
|
// src/validators/executors.ts
|
|
2091
2402
|
var SCHEME_SEPARATOR = "://";
|
|
@@ -2149,12 +2460,64 @@ function checkExecutor(executor, field, stepID, issues) {
|
|
|
2149
2460
|
}
|
|
2150
2461
|
checkFunctionOperation(executor, field, stepID, templated, issues);
|
|
2151
2462
|
}
|
|
2463
|
+
function removedExecutorScheme(executor) {
|
|
2464
|
+
if (executor.includes(TEMPLATE_ACTION_OPEN)) return "";
|
|
2465
|
+
const trimmed = goTrimSpace(executor);
|
|
2466
|
+
const at = trimmed.indexOf(SCHEME_SEPARATOR);
|
|
2467
|
+
return at < 0 ? "" : trimmed.slice(0, at).toLowerCase();
|
|
2468
|
+
}
|
|
2469
|
+
function checkRemovedSurfaces(executor, query, field, stepID, issues) {
|
|
2470
|
+
const cfg = REMOVED_AGENT_SURFACES;
|
|
2471
|
+
const quoted = JSON.stringify(stepID);
|
|
2472
|
+
const scheme = removedExecutorScheme(executor);
|
|
2473
|
+
if (cfg.schemes.has(scheme)) {
|
|
2474
|
+
issues.error({
|
|
2475
|
+
field,
|
|
2476
|
+
message: fillRemovedMessage(cfg.schemeMessageFormat, quoted, scheme),
|
|
2477
|
+
code: cfg.executorRemovedCode,
|
|
2478
|
+
stepId: stepID,
|
|
2479
|
+
suggestion: cfg.suggestion
|
|
2480
|
+
});
|
|
2481
|
+
return true;
|
|
2482
|
+
}
|
|
2483
|
+
if (scheme !== cfg.paramScheme || !isRecord(query)) return false;
|
|
2484
|
+
const found = Object.keys(query).filter((key) => cfg.aiParams.has(key)).sort();
|
|
2485
|
+
if (found.length > 0) {
|
|
2486
|
+
issues.error({
|
|
2487
|
+
field,
|
|
2488
|
+
message: fillRemovedMessage(cfg.paramsMessageFormat, quoted, found.join(", ")),
|
|
2489
|
+
code: cfg.agenticParamRemovedCode,
|
|
2490
|
+
stepId: stepID,
|
|
2491
|
+
suggestion: cfg.suggestion
|
|
2492
|
+
});
|
|
2493
|
+
}
|
|
2494
|
+
return false;
|
|
2495
|
+
}
|
|
2496
|
+
function fillRemovedMessage(format, ...args) {
|
|
2497
|
+
let i = 0;
|
|
2498
|
+
return format.replace(/%[qs]/g, () => args[i++] ?? "");
|
|
2499
|
+
}
|
|
2500
|
+
function validateRemovedFlowKeys(flow, issues) {
|
|
2501
|
+
const cfg = REMOVED_AGENT_SURFACES;
|
|
2502
|
+
for (const key of cfg.flowKeys) {
|
|
2503
|
+
if (Object.prototype.hasOwnProperty.call(flow, key)) {
|
|
2504
|
+
issues.error({
|
|
2505
|
+
field: key,
|
|
2506
|
+
message: fillRemovedMessage(cfg.flowKeyMessageFormat, JSON.stringify(key)),
|
|
2507
|
+
code: cfg.executorRemovedCode,
|
|
2508
|
+
suggestion: cfg.suggestion
|
|
2509
|
+
});
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2152
2513
|
function checkAtlasStep(executor, query, stepField, stepID, issues) {
|
|
2153
2514
|
if (executor.includes(TEMPLATE_ACTION_OPEN)) return;
|
|
2154
|
-
const
|
|
2155
|
-
if (
|
|
2156
|
-
const
|
|
2157
|
-
const
|
|
2515
|
+
const prefix = `${ATLAS_EXECUTOR.scheme}://`;
|
|
2516
|
+
if (!executor.startsWith(prefix)) return;
|
|
2517
|
+
const rest = executor.slice(prefix.length);
|
|
2518
|
+
const slash = rest.indexOf("/");
|
|
2519
|
+
const provider = slash < 0 ? rest : rest.slice(0, slash);
|
|
2520
|
+
const resource = slash < 0 ? "" : rest.slice(slash + 1);
|
|
2158
2521
|
const quoted = JSON.stringify(stepID);
|
|
2159
2522
|
const report = (rule, ...args) => {
|
|
2160
2523
|
let message = rule.messageFormat.replace("%s", () => quoted);
|
|
@@ -2168,31 +2531,41 @@ function checkAtlasStep(executor, query, stepField, stepID, issues) {
|
|
|
2168
2531
|
});
|
|
2169
2532
|
};
|
|
2170
2533
|
const q2 = isRecord(query) ? query : {};
|
|
2534
|
+
let refusedKeys = ATLAS_EXECUTOR.paramRefused.capabilityKeys;
|
|
2171
2535
|
if (provider === ATLAS_EXECUTOR.providerCapability) {
|
|
2172
2536
|
if (!ATLAS_EXECUTOR.capabilityRefPattern.test(resource)) {
|
|
2173
2537
|
report(ATLAS_EXECUTOR.capabilityRefInvalid, resource);
|
|
2174
2538
|
}
|
|
2175
2539
|
} else if (provider === ATLAS_EXECUTOR.providerAgent && resource === ATLAS_EXECUTOR.agentResource) {
|
|
2540
|
+
refusedKeys = ATLAS_EXECUTOR.paramRefused.agentKeys;
|
|
2176
2541
|
const agent = q2.agent;
|
|
2177
|
-
if (typeof agent !== "string" || agent === "") {
|
|
2542
|
+
if (typeof agent !== "string" || agent.trim() === "") {
|
|
2178
2543
|
report(ATLAS_EXECUTOR.agentMissing);
|
|
2179
2544
|
} else if (agent.includes(TEMPLATE_ACTION_OPEN)) {
|
|
2180
2545
|
report(ATLAS_EXECUTOR.agentNotLiteral);
|
|
2181
2546
|
}
|
|
2182
|
-
const
|
|
2183
|
-
const
|
|
2184
|
-
const hasPrompt = prompt !== void 0 && prompt !== null && prompt !== "";
|
|
2185
|
-
const hasTemplate = promptTemplate !== void 0 && promptTemplate !== null && promptTemplate !== "";
|
|
2547
|
+
const hasPrompt = atlasTextPresent(q2.prompt);
|
|
2548
|
+
const hasTemplate = atlasTextPresent(q2.prompt_template);
|
|
2186
2549
|
if (!hasPrompt && !hasTemplate) report(ATLAS_EXECUTOR.promptMissing);
|
|
2187
2550
|
else if (hasPrompt && hasTemplate) report(ATLAS_EXECUTOR.promptBoth);
|
|
2551
|
+
if (anyTemplate(q2.model)) report(ATLAS_EXECUTOR.modelNotLiteral);
|
|
2188
2552
|
} else {
|
|
2189
2553
|
report(ATLAS_EXECUTOR.operationUnknown, executor);
|
|
2190
2554
|
return;
|
|
2191
2555
|
}
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2556
|
+
if (anyTemplate(q2.capabilities)) report(ATLAS_EXECUTOR.capabilitiesNotLiteral);
|
|
2557
|
+
const refused = refusedKeys.filter((key) => Object.prototype.hasOwnProperty.call(q2, key)).sort();
|
|
2558
|
+
if (refused.length > 0) report(ATLAS_EXECUTOR.paramRefused, refused.join(", "));
|
|
2559
|
+
}
|
|
2560
|
+
function anyTemplate(v) {
|
|
2561
|
+
if (typeof v === "string") return v.includes(TEMPLATE_ACTION_OPEN);
|
|
2562
|
+
if (Array.isArray(v)) return v.some(anyTemplate);
|
|
2563
|
+
if (isRecord(v)) return Object.values(v).some(anyTemplate);
|
|
2564
|
+
return false;
|
|
2565
|
+
}
|
|
2566
|
+
function atlasTextPresent(v) {
|
|
2567
|
+
if (v === void 0 || v === null) return false;
|
|
2568
|
+
return typeof v !== "string" || v.trim() !== "";
|
|
2196
2569
|
}
|
|
2197
2570
|
function checkFunctionOperation(executor, field, stepID, templated, issues) {
|
|
2198
2571
|
if (templated) return;
|
|
@@ -2217,7 +2590,9 @@ function validateExecutors(flow, issues) {
|
|
|
2217
2590
|
const step = rawStep;
|
|
2218
2591
|
const executor = issues.stringOf(step, "executor", `steps.${stepID}`);
|
|
2219
2592
|
if (executor !== null && executor !== "") {
|
|
2220
|
-
|
|
2593
|
+
if (!checkRemovedSurfaces(executor, step.query, `steps.${stepID}`, stepID, issues)) {
|
|
2594
|
+
checkExecutor(executor, `steps.${stepID}.executor`, stepID, issues);
|
|
2595
|
+
}
|
|
2221
2596
|
checkAtlasStep(executor, step.query, `steps.${stepID}`, stepID, issues);
|
|
2222
2597
|
}
|
|
2223
2598
|
const loop = step.loop;
|
|
@@ -2229,12 +2604,14 @@ function validateExecutors(flow, issues) {
|
|
|
2229
2604
|
const subID = issues.stringOf(subStep, "id", subPath) ?? String(i);
|
|
2230
2605
|
const subExecutor = issues.stringOf(subStep, "executor", subPath);
|
|
2231
2606
|
if (subExecutor !== null && subExecutor !== "") {
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2607
|
+
if (!checkRemovedSurfaces(subExecutor, subStep.query, subPath, `${stepID}.${subID}`, issues)) {
|
|
2608
|
+
checkExecutor(
|
|
2609
|
+
subExecutor,
|
|
2610
|
+
`steps.${stepID}.loop.steps[${i}].executor`,
|
|
2611
|
+
`${stepID}.${subID}`,
|
|
2612
|
+
issues
|
|
2613
|
+
);
|
|
2614
|
+
}
|
|
2238
2615
|
checkAtlasStep(subExecutor, subStep.query, subPath, `${stepID}.${subID}`, issues);
|
|
2239
2616
|
}
|
|
2240
2617
|
});
|
|
@@ -3878,6 +4255,37 @@ function validateInputSchema(flow, issues) {
|
|
|
3878
4255
|
validateSchemaDefinition(flow.input_schema, "input_schema", issues);
|
|
3879
4256
|
}
|
|
3880
4257
|
|
|
4258
|
+
// src/validators/reservedInputs.ts
|
|
4259
|
+
function formatField(format, name) {
|
|
4260
|
+
return format.replace("%s", () => name);
|
|
4261
|
+
}
|
|
4262
|
+
function validateReservedInputNames(flow, issues) {
|
|
4263
|
+
const rule = RESERVED_INPUT_NAMES;
|
|
4264
|
+
const report = (name, fieldFormat) => {
|
|
4265
|
+
issues.error({
|
|
4266
|
+
field: formatField(fieldFormat, name),
|
|
4267
|
+
message: rule.messageFormat.replace("%s", () => JSON.stringify(name)),
|
|
4268
|
+
code: rule.code,
|
|
4269
|
+
suggestion: rule.suggestion
|
|
4270
|
+
});
|
|
4271
|
+
};
|
|
4272
|
+
const query = flow.query;
|
|
4273
|
+
if (isRecord(query)) {
|
|
4274
|
+
for (const name of Object.keys(query).sort()) {
|
|
4275
|
+
if (name.startsWith(rule.prefix)) report(name, rule.queryFieldFormat);
|
|
4276
|
+
}
|
|
4277
|
+
}
|
|
4278
|
+
const schema = flow.input_schema;
|
|
4279
|
+
const fields = isRecord(schema) ? schema.fields : void 0;
|
|
4280
|
+
if (!Array.isArray(fields)) return;
|
|
4281
|
+
for (const f of fields) {
|
|
4282
|
+
if (!isRecord(f)) continue;
|
|
4283
|
+
if (typeof f.name === "string" && f.name.startsWith(rule.prefix)) {
|
|
4284
|
+
report(f.name, rule.schemaFieldFormat);
|
|
4285
|
+
}
|
|
4286
|
+
}
|
|
4287
|
+
}
|
|
4288
|
+
|
|
3881
4289
|
// src/validators/outputSchema.ts
|
|
3882
4290
|
function validateOutputSchemas(flow, issues) {
|
|
3883
4291
|
const steps = flow.steps;
|
|
@@ -4992,7 +5400,7 @@ var CODE2 = "server_owned_query_key";
|
|
|
4992
5400
|
var KEY_QUERY = "query";
|
|
4993
5401
|
var KEY_ID = "id";
|
|
4994
5402
|
var SERVER_OWNED = new Set(SERVER_OWNED_QUERY_KEYS.keys);
|
|
4995
|
-
function
|
|
5403
|
+
function formatField2(format, ...args) {
|
|
4996
5404
|
let i = 0;
|
|
4997
5405
|
return format.replace(/%s/g, () => args[i++] ?? "");
|
|
4998
5406
|
}
|
|
@@ -5010,7 +5418,7 @@ function validateServerOwnedQueryKeys(flow, issues) {
|
|
|
5010
5418
|
issues,
|
|
5011
5419
|
stepID,
|
|
5012
5420
|
key,
|
|
5013
|
-
|
|
5421
|
+
formatField2(SERVER_OWNED_QUERY_KEYS.stepFieldFormat, stepID, key)
|
|
5014
5422
|
);
|
|
5015
5423
|
}
|
|
5016
5424
|
}
|
|
@@ -5024,7 +5432,7 @@ function validateServerOwnedQueryKeys(flow, issues) {
|
|
|
5024
5432
|
issues,
|
|
5025
5433
|
stepID,
|
|
5026
5434
|
key,
|
|
5027
|
-
|
|
5435
|
+
formatField2(SERVER_OWNED_QUERY_KEYS.loopStepFieldFormat, stepID, subID, key)
|
|
5028
5436
|
);
|
|
5029
5437
|
}
|
|
5030
5438
|
}
|
|
@@ -5041,9 +5449,9 @@ function refuse(issues, stepId, key, field) {
|
|
|
5041
5449
|
}
|
|
5042
5450
|
|
|
5043
5451
|
// src/validators/goUrl.ts
|
|
5044
|
-
var
|
|
5045
|
-
var GO_TRIM_SPACE_RE = new RegExp(`^[${
|
|
5046
|
-
function
|
|
5452
|
+
var GO_SPACE2 = "\\t\\n\\v\\f\\r \\u0085\\u00A0\\u1680\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000";
|
|
5453
|
+
var GO_TRIM_SPACE_RE = new RegExp(`^[${GO_SPACE2}]+|[${GO_SPACE2}]+$`, "g");
|
|
5454
|
+
function goTrimSpace2(s) {
|
|
5047
5455
|
return s.replace(GO_TRIM_SPACE_RE, "");
|
|
5048
5456
|
}
|
|
5049
5457
|
function goToLower(s) {
|
|
@@ -5264,7 +5672,7 @@ function goQueryKeys(rawQuery) {
|
|
|
5264
5672
|
return keys;
|
|
5265
5673
|
}
|
|
5266
5674
|
function goEndpointOrigin(raw, defaultPorts, protocolSeparator) {
|
|
5267
|
-
const trimmed =
|
|
5675
|
+
const trimmed = goTrimSpace2(raw);
|
|
5268
5676
|
const u = goParseSchemeHost(trimmed);
|
|
5269
5677
|
if (u === null || u.scheme === "" || u.host === "") {
|
|
5270
5678
|
return goToLower(trimmed.replace(/\/+$/, ""));
|
|
@@ -5291,7 +5699,7 @@ function own(m, key) {
|
|
|
5291
5699
|
if (!isRecord(m) || !Object.prototype.hasOwnProperty.call(m, key)) return void 0;
|
|
5292
5700
|
return m[key];
|
|
5293
5701
|
}
|
|
5294
|
-
function
|
|
5702
|
+
function formatField3(format, ...args) {
|
|
5295
5703
|
let i = 0;
|
|
5296
5704
|
return format.replace(/%s/g, () => args[i++] ?? "");
|
|
5297
5705
|
}
|
|
@@ -5313,14 +5721,14 @@ function credentialEndpointSteps(flow, issues) {
|
|
|
5313
5721
|
out.push({
|
|
5314
5722
|
executor: issues.stringOf(step, KEY_EXECUTOR, `steps.${stepId}`) ?? "",
|
|
5315
5723
|
query: own(step, KEY_QUERY2),
|
|
5316
|
-
field: (key) =>
|
|
5724
|
+
field: (key) => formatField3(CREDENTIAL_ENDPOINT_PAIRING.stepFieldFormat, stepId, key)
|
|
5317
5725
|
});
|
|
5318
5726
|
for (const sub of loopSubStepsOfStep(stepId, step)) {
|
|
5319
5727
|
const subId = issues.stringOf(sub.raw, KEY_ID2, sub.basePath) ?? "";
|
|
5320
5728
|
out.push({
|
|
5321
5729
|
executor: issues.stringOf(sub.raw, KEY_EXECUTOR, sub.basePath) ?? "",
|
|
5322
5730
|
query: own(sub.raw, KEY_QUERY2),
|
|
5323
|
-
field: (key) =>
|
|
5731
|
+
field: (key) => formatField3(CREDENTIAL_ENDPOINT_PAIRING.loopStepFieldFormat, stepId, subId, key)
|
|
5324
5732
|
});
|
|
5325
5733
|
}
|
|
5326
5734
|
}
|
|
@@ -5384,7 +5792,7 @@ function validateAI(flow, steps, issues) {
|
|
|
5384
5792
|
if ((stepsUsing.get(provider) ?? 0) > 0 && (stepsWithoutOwnKey.get(provider) ?? 0) === 0) {
|
|
5385
5793
|
continue;
|
|
5386
5794
|
}
|
|
5387
|
-
warnStoredShape(issues,
|
|
5795
|
+
warnStoredShape(issues, formatField3(CREDENTIAL_ENDPOINT_PAIRING.executorConfigBaseUrlFieldFormat, provider), provider);
|
|
5388
5796
|
}
|
|
5389
5797
|
}
|
|
5390
5798
|
function warnStoredShape(issues, field, provider) {
|
|
@@ -5431,7 +5839,7 @@ function familySecret(flow, family, step, issues) {
|
|
|
5431
5839
|
if (apiKey !== "" && listHas(family.secretParams, family.configApiKeyParam)) {
|
|
5432
5840
|
return {
|
|
5433
5841
|
envName: serverEnvRef(family, apiKey),
|
|
5434
|
-
field:
|
|
5842
|
+
field: formatField3(
|
|
5435
5843
|
CREDENTIAL_ENDPOINT_PAIRING.executorConfigKeyFieldFormat,
|
|
5436
5844
|
configKey,
|
|
5437
5845
|
CREDENTIAL_ENDPOINT_PAIRING.executorConfigApiKeyField
|
|
@@ -5444,7 +5852,7 @@ function familySecret(flow, family, step, issues) {
|
|
|
5444
5852
|
if (v !== null) {
|
|
5445
5853
|
return {
|
|
5446
5854
|
envName: serverEnvRef(family, v),
|
|
5447
|
-
field:
|
|
5855
|
+
field: formatField3(CREDENTIAL_ENDPOINT_PAIRING.executorConfigExtraFieldFormat, configKey, key)
|
|
5448
5856
|
};
|
|
5449
5857
|
}
|
|
5450
5858
|
}
|
|
@@ -5470,13 +5878,13 @@ function familyAuthorEndpoint(flow, family, step, issues) {
|
|
|
5470
5878
|
if (inst === null) continue;
|
|
5471
5879
|
const baseUrl = configStringField(inst, configKey, CREDENTIAL_ENDPOINT_PAIRING.executorConfigBaseUrlField, issues);
|
|
5472
5880
|
if (baseUrl !== "" && listHas(family.endpointParams, family.configBaseUrlParam)) {
|
|
5473
|
-
return decide(baseUrl,
|
|
5881
|
+
return decide(baseUrl, formatField3(CREDENTIAL_ENDPOINT_PAIRING.executorConfigBaseUrlFieldFormat, configKey));
|
|
5474
5882
|
}
|
|
5475
5883
|
const extra = own(inst, EXECUTOR_CONFIG_ENV_SCOPES.extraKey);
|
|
5476
5884
|
for (const key of family.endpointParams) {
|
|
5477
5885
|
const v = nonEmptyEndpoint(extra, key);
|
|
5478
5886
|
if (v !== null)
|
|
5479
|
-
return decide(v,
|
|
5887
|
+
return decide(v, formatField3(CREDENTIAL_ENDPOINT_PAIRING.executorConfigExtraFieldFormat, configKey, key));
|
|
5480
5888
|
}
|
|
5481
5889
|
}
|
|
5482
5890
|
return null;
|
|
@@ -5498,227 +5906,6 @@ function validateFamilies(flow, steps, issues) {
|
|
|
5498
5906
|
});
|
|
5499
5907
|
}
|
|
5500
5908
|
}
|
|
5501
|
-
var CODE_PARSE_FAILED = "orchestrator_exons_parse_failed";
|
|
5502
|
-
var CODE_NO_PROVIDER = "orchestrator_exons_no_provider";
|
|
5503
|
-
var CODE_RESOURCES = "exons_resources_unhonoured";
|
|
5504
|
-
var CODE_TOOL_WITHHELD = "orchestrator_tool_withheld";
|
|
5505
|
-
var EXONS_OPEN_DELIM = "{~";
|
|
5506
|
-
var EXONS_LEGACY_CONFIG_OPEN = "{~exons.config~}";
|
|
5507
|
-
var UTF8_BOM = "\uFEFF";
|
|
5508
|
-
var MAX_ALIAS_COUNT2 = 100;
|
|
5509
|
-
var UNREAD = {
|
|
5510
|
-
parseError: null,
|
|
5511
|
-
specRead: false,
|
|
5512
|
-
hasSpec: false,
|
|
5513
|
-
provider: "",
|
|
5514
|
-
toolsAllowDeclared: false,
|
|
5515
|
-
toolsAllow: [],
|
|
5516
|
-
resources: []
|
|
5517
|
-
};
|
|
5518
|
-
var GO_SPACE2 = "\\t\\n\\v\\f\\r \\u0085\\u00a0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000";
|
|
5519
|
-
var GO_TRIM_SPACE = new RegExp(`^[${GO_SPACE2}]+|[${GO_SPACE2}]+$`, "g");
|
|
5520
|
-
function goTrimSpace2(s) {
|
|
5521
|
-
return s.replace(GO_TRIM_SPACE, "");
|
|
5522
|
-
}
|
|
5523
|
-
function extractExonsFrontmatter(source) {
|
|
5524
|
-
const delim = EXONS.frontmatterDelimiter;
|
|
5525
|
-
let start = source.startsWith(UTF8_BOM) ? source.slice(UTF8_BOM.length) : source;
|
|
5526
|
-
start = start.replace(/^[ \t]+/, "");
|
|
5527
|
-
if (start.startsWith(EXONS_LEGACY_CONFIG_OPEN)) {
|
|
5528
|
-
return {
|
|
5529
|
-
frontmatter: "",
|
|
5530
|
-
hasFrontmatter: false,
|
|
5531
|
-
parseError: "legacy JSON config block detected; use YAML frontmatter"
|
|
5532
|
-
};
|
|
5533
|
-
}
|
|
5534
|
-
if (!start.startsWith(delim)) return { frontmatter: "", hasFrontmatter: false, parseError: null };
|
|
5535
|
-
let rest = start.slice(delim.length);
|
|
5536
|
-
if (rest.startsWith("\n")) rest = rest.slice(1);
|
|
5537
|
-
else if (rest.startsWith("\r\n")) rest = rest.slice(2);
|
|
5538
|
-
else return { frontmatter: "", hasFrontmatter: false, parseError: null };
|
|
5539
|
-
let pos = 0;
|
|
5540
|
-
while (pos <= rest.length) {
|
|
5541
|
-
const line = rest.slice(pos);
|
|
5542
|
-
if (line.startsWith(delim)) {
|
|
5543
|
-
const after = line.slice(delim.length);
|
|
5544
|
-
if (after === "" || after[0] === "\n" || after[0] === "\r") {
|
|
5545
|
-
return {
|
|
5546
|
-
frontmatter: goTrimSpace2(rest.slice(0, pos)),
|
|
5547
|
-
hasFrontmatter: true,
|
|
5548
|
-
parseError: null
|
|
5549
|
-
};
|
|
5550
|
-
}
|
|
5551
|
-
}
|
|
5552
|
-
const nl = line.indexOf("\n");
|
|
5553
|
-
if (nl < 0) break;
|
|
5554
|
-
pos += nl + 1;
|
|
5555
|
-
}
|
|
5556
|
-
return {
|
|
5557
|
-
frontmatter: "",
|
|
5558
|
-
hasFrontmatter: true,
|
|
5559
|
-
parseError: "YAML frontmatter not properly closed"
|
|
5560
|
-
};
|
|
5561
|
-
}
|
|
5562
|
-
function retagScalars(node) {
|
|
5563
|
-
if (isMap(node)) {
|
|
5564
|
-
for (const pair of node.items) {
|
|
5565
|
-
retagScalars(pair.key);
|
|
5566
|
-
retagScalars(pair.value);
|
|
5567
|
-
}
|
|
5568
|
-
} else if (isSeq(node)) {
|
|
5569
|
-
for (const item of node.items) retagScalars(item);
|
|
5570
|
-
} else if (isScalar(node) && (typeof node.value === "number" || typeof node.value === "boolean") && typeof node.source === "string") {
|
|
5571
|
-
node.value = node.source;
|
|
5572
|
-
}
|
|
5573
|
-
}
|
|
5574
|
-
var DecodeError = class extends Error {
|
|
5575
|
-
};
|
|
5576
|
-
function goString(v) {
|
|
5577
|
-
if (v === null || v === void 0) return "";
|
|
5578
|
-
if (isString(v)) return v;
|
|
5579
|
-
throw new DecodeError("not a string");
|
|
5580
|
-
}
|
|
5581
|
-
function goStringList(v) {
|
|
5582
|
-
if (v === null || v === void 0) return null;
|
|
5583
|
-
if (!isArray(v)) throw new DecodeError("not a list");
|
|
5584
|
-
return v.filter((item) => item !== null).map(goString);
|
|
5585
|
-
}
|
|
5586
|
-
function goStruct(v) {
|
|
5587
|
-
if (v === null || v === void 0) return null;
|
|
5588
|
-
if (!isRecord(v)) throw new DecodeError("not a mapping");
|
|
5589
|
-
return v;
|
|
5590
|
-
}
|
|
5591
|
-
function decodeSpecFields(frontmatter) {
|
|
5592
|
-
let value;
|
|
5593
|
-
try {
|
|
5594
|
-
const docs = parseAllDocuments(frontmatter, { merge: true });
|
|
5595
|
-
const doc = Array.isArray(docs) ? docs[0] : void 0;
|
|
5596
|
-
if (doc === void 0) {
|
|
5597
|
-
value = null;
|
|
5598
|
-
} else {
|
|
5599
|
-
if (doc.errors.length > 0) return null;
|
|
5600
|
-
retagScalars(doc.contents);
|
|
5601
|
-
value = doc.toJS({ maxAliasCount: MAX_ALIAS_COUNT2 });
|
|
5602
|
-
}
|
|
5603
|
-
} catch {
|
|
5604
|
-
return null;
|
|
5605
|
-
}
|
|
5606
|
-
try {
|
|
5607
|
-
const root = goStruct(value) ?? {};
|
|
5608
|
-
const execution = goStruct(root.execution);
|
|
5609
|
-
const tools = goStruct(root.tools);
|
|
5610
|
-
const requirements = goStruct(root.requirements);
|
|
5611
|
-
const allow = tools === null ? null : goStringList(tools.allow);
|
|
5612
|
-
let resources = [];
|
|
5613
|
-
if (requirements !== null) {
|
|
5614
|
-
const raw = requirements.resources;
|
|
5615
|
-
if (raw !== null && raw !== void 0) {
|
|
5616
|
-
if (!isArray(raw)) throw new DecodeError("not a list");
|
|
5617
|
-
resources = raw.filter((entry) => entry !== null).map((entry) => {
|
|
5618
|
-
const r = goStruct(entry) ?? {};
|
|
5619
|
-
return { ref: goString(r.ref), kind: goString(r.kind), scope: goString(r.scope) };
|
|
5620
|
-
});
|
|
5621
|
-
}
|
|
5622
|
-
}
|
|
5623
|
-
return {
|
|
5624
|
-
provider: execution === null ? "" : goString(execution.provider),
|
|
5625
|
-
toolsAllowDeclared: allow !== null,
|
|
5626
|
-
toolsAllow: allow ?? [],
|
|
5627
|
-
resources
|
|
5628
|
-
};
|
|
5629
|
-
} catch (e) {
|
|
5630
|
-
if (e instanceof DecodeError) return null;
|
|
5631
|
-
throw e;
|
|
5632
|
-
}
|
|
5633
|
-
}
|
|
5634
|
-
function inspectExonsFrontmatter(source) {
|
|
5635
|
-
const fm = extractExonsFrontmatter(source);
|
|
5636
|
-
if (fm.parseError !== null) return { ...UNREAD, parseError: fm.parseError };
|
|
5637
|
-
if (!fm.hasFrontmatter || fm.frontmatter === "") return { ...UNREAD, specRead: true };
|
|
5638
|
-
if (fm.frontmatter.includes(EXONS_OPEN_DELIM)) return { ...UNREAD };
|
|
5639
|
-
const fields = decodeSpecFields(fm.frontmatter);
|
|
5640
|
-
if (fields === null) return { ...UNREAD };
|
|
5641
|
-
return { parseError: null, specRead: true, hasSpec: true, ...fields };
|
|
5642
|
-
}
|
|
5643
|
-
function validateOrchestratorExons(flow, issues) {
|
|
5644
|
-
const orch = flow.orchestrator;
|
|
5645
|
-
if (!isRecord(orch)) return;
|
|
5646
|
-
const source = issues.stringOf(orch, "exons", "orchestrator");
|
|
5647
|
-
if (source === null || source === "") return;
|
|
5648
|
-
const field = "orchestrator.exons";
|
|
5649
|
-
const report = inspectExonsFrontmatter(source);
|
|
5650
|
-
if (report.parseError !== null) {
|
|
5651
|
-
issues.error({
|
|
5652
|
-
field,
|
|
5653
|
-
code: CODE_PARSE_FAILED,
|
|
5654
|
-
message: `orchestrator: failed to parse exons spec: ${report.parseError}`
|
|
5655
|
-
});
|
|
5656
|
-
return;
|
|
5657
|
-
}
|
|
5658
|
-
if (!report.specRead) return;
|
|
5659
|
-
if (!report.hasSpec) {
|
|
5660
|
-
issues.error({
|
|
5661
|
-
field,
|
|
5662
|
-
code: CODE_PARSE_FAILED,
|
|
5663
|
-
message: "orchestrator: failed to parse exons spec: the document has no frontmatter spec",
|
|
5664
|
-
suggestion: "Start the document with a --- frontmatter block that sets execution.provider"
|
|
5665
|
-
});
|
|
5666
|
-
return;
|
|
5667
|
-
}
|
|
5668
|
-
if (report.provider === "") {
|
|
5669
|
-
issues.error({
|
|
5670
|
-
field,
|
|
5671
|
-
code: CODE_NO_PROVIDER,
|
|
5672
|
-
message: "orchestrator: exons spec must have execution.provider set"
|
|
5673
|
-
});
|
|
5674
|
-
}
|
|
5675
|
-
if (report.resources.length > 0) {
|
|
5676
|
-
const refs = report.resources.map((r) => `${JSON.stringify(r.ref)} (kind ${r.kind})`);
|
|
5677
|
-
issues.error({
|
|
5678
|
-
field,
|
|
5679
|
-
code: CODE_RESOURCES,
|
|
5680
|
-
message: "the orchestrator's exons spec declares requirements.resources that AIgentFlow cannot honour, so it is refused rather than run without the narrowing it asks for: " + refs.join("; "),
|
|
5681
|
-
suggestion: "Remove requirements.resources, and narrow the agent with tools.mcp_servers and tools.allow"
|
|
5682
|
-
});
|
|
5683
|
-
}
|
|
5684
|
-
warnOrchestratorToolWithheld(flow, orch, report, issues);
|
|
5685
|
-
}
|
|
5686
|
-
function warnOrchestratorToolWithheld(flow, orch, report, issues) {
|
|
5687
|
-
if (!report.toolsAllowDeclared) return;
|
|
5688
|
-
const cfg = ORCHESTRATOR_TOOL_ALLOW;
|
|
5689
|
-
const exempt = new Set(cfg.lifecycleTools);
|
|
5690
|
-
const signals = typeof orch.enable_signals === "boolean" ? orch.enable_signals : cfg.enableSignalsDefault;
|
|
5691
|
-
if (signals) for (const t of cfg.signalTools) exempt.add(t);
|
|
5692
|
-
const allow = new Set(report.toolsAllow);
|
|
5693
|
-
const withheld = (name) => !allow.has(name) && !exempt.has(name);
|
|
5694
|
-
const warn = (message) => issues.warn({ field: cfg.warningField, code: CODE_TOOL_WITHHELD, message });
|
|
5695
|
-
const named = [];
|
|
5696
|
-
if (isArray(orch.tools)) {
|
|
5697
|
-
orch.tools.forEach((raw, i) => {
|
|
5698
|
-
const name = issues.stringAt(raw, `orchestrator.tools[${i}]`);
|
|
5699
|
-
if (name !== null) named.push(name);
|
|
5700
|
-
});
|
|
5701
|
-
}
|
|
5702
|
-
if (named.length > 0) {
|
|
5703
|
-
for (const name of named) {
|
|
5704
|
-
if (!withheld(name)) continue;
|
|
5705
|
-
warn(
|
|
5706
|
-
`orchestrator tool '${name}' is named in orchestrator.tools but the orchestrator's exons definition's tools.allow does not name it, so it is never offered: a tool must pass both lists. Add '${name}' to tools.allow, or remove it from orchestrator.tools.`
|
|
5707
|
-
);
|
|
5708
|
-
}
|
|
5709
|
-
return;
|
|
5710
|
-
}
|
|
5711
|
-
if (withheld(cfg.askHumanTool)) {
|
|
5712
|
-
warn(
|
|
5713
|
-
`the orchestrator's exons definition has a tools.allow list that does not name ${cfg.askHumanTool}, so this orchestrator can never ask a person a question: human intervention is off. Add ${cfg.askHumanTool} to tools.allow if a human should be reachable, or ignore this if the orchestrator is meant to run unattended.`
|
|
5714
|
-
);
|
|
5715
|
-
}
|
|
5716
|
-
if (isRecord(flow.campaign) && !cfg.campaignTools.some((name) => !withheld(name))) {
|
|
5717
|
-
warn(
|
|
5718
|
-
`this flow declares a campaign, but the orchestrator's exons definition has a tools.allow list that names none of the campaign tools (${cfg.campaignTools.join(", ")}), so no child mission can be spawned or tracked. Add the campaign tools the orchestrator needs to tools.allow.`
|
|
5719
|
-
);
|
|
5720
|
-
}
|
|
5721
|
-
}
|
|
5722
5909
|
|
|
5723
5910
|
// src/validators/complianceResidency.ts
|
|
5724
5911
|
function own2(m, key) {
|
|
@@ -5733,7 +5920,7 @@ function sprintf(format, ...args) {
|
|
|
5733
5920
|
});
|
|
5734
5921
|
}
|
|
5735
5922
|
function literalOrEmpty(s) {
|
|
5736
|
-
const t =
|
|
5923
|
+
const t = goTrimSpace(s);
|
|
5737
5924
|
return t.includes(TEMPLATE_ACTION_OPEN) ? "" : t;
|
|
5738
5925
|
}
|
|
5739
5926
|
function euOnlyResidency(flow, issues) {
|
|
@@ -5748,7 +5935,7 @@ function euOnlyResidency(flow, issues) {
|
|
|
5748
5935
|
}
|
|
5749
5936
|
if (codes.length === 0) return null;
|
|
5750
5937
|
const eu = new Set(COMPLIANCE_RESIDENCY.euResidencyCodes);
|
|
5751
|
-
return codes.every((c) => eu.has(
|
|
5938
|
+
return codes.every((c) => eu.has(goTrimSpace(c).toUpperCase())) ? codes : null;
|
|
5752
5939
|
}
|
|
5753
5940
|
function aiProvider(executor) {
|
|
5754
5941
|
const prefix = COMPLIANCE_RESIDENCY.aiProtocol + COMPLIANCE_RESIDENCY.protocolSeparator;
|
|
@@ -5764,7 +5951,7 @@ function isRegionLane(provider) {
|
|
|
5764
5951
|
}
|
|
5765
5952
|
function nonBlankString(m, key) {
|
|
5766
5953
|
const v = own2(m, key);
|
|
5767
|
-
return typeof v === "string" &&
|
|
5954
|
+
return typeof v === "string" && goTrimSpace(v) !== "";
|
|
5768
5955
|
}
|
|
5769
5956
|
function queryEnforcesRegion(query) {
|
|
5770
5957
|
const v = own2(query, COMPLIANCE_RESIDENCY.enforceRegionParam);
|
|
@@ -5776,7 +5963,7 @@ function flowDirectsEndpoint(flow, provider, issues) {
|
|
|
5776
5963
|
const inst = own2(own2(flow, "executor_config"), key);
|
|
5777
5964
|
if (!isRecord(inst)) return false;
|
|
5778
5965
|
const baseUrl = issues.stringOf(inst, "base_url", `executor_config.${key}`);
|
|
5779
|
-
return baseUrl !== null &&
|
|
5966
|
+
return baseUrl !== null && goTrimSpace(baseUrl) !== "";
|
|
5780
5967
|
}
|
|
5781
5968
|
function callPinsEndpoint(flow, provider, query, issues) {
|
|
5782
5969
|
if (nonBlankString(query, COMPLIANCE_RESIDENCY.regionParam)) return true;
|
|
@@ -5849,7 +6036,7 @@ function warnOrchestrator(flow, issues, warn) {
|
|
|
5849
6036
|
const provider = literalOrEmpty(report.provider);
|
|
5850
6037
|
if (!isRegionLane(provider)) return;
|
|
5851
6038
|
const region = issues.stringOf(orch, "region", "orchestrator");
|
|
5852
|
-
if (region !== null &&
|
|
6039
|
+
if (region !== null && goTrimSpace(region) !== "") return;
|
|
5853
6040
|
if (orch.enforce_region === true) return;
|
|
5854
6041
|
if (flowDirectsEndpoint(flow, provider, issues)) return;
|
|
5855
6042
|
warn(COMPLIANCE_RESIDENCY.siteOrchestrator, COMPLIANCE_RESIDENCY.orchestratorField, void 0, provider);
|
|
@@ -6015,7 +6202,7 @@ function asFileRef(v) {
|
|
|
6015
6202
|
};
|
|
6016
6203
|
}
|
|
6017
6204
|
function isTextualMediaType(mediaType) {
|
|
6018
|
-
const base = goToLower(
|
|
6205
|
+
const base = goToLower(goTrimSpace2((mediaType.split(";")[0] ?? "").toString()));
|
|
6019
6206
|
if (base.startsWith("text/")) return true;
|
|
6020
6207
|
if (base.endsWith("+json") || base.endsWith("+xml")) return true;
|
|
6021
6208
|
return TEXTUAL_MEDIA_BASES.has(base);
|
|
@@ -6119,7 +6306,7 @@ var Walker = class _Walker {
|
|
|
6119
6306
|
}
|
|
6120
6307
|
}
|
|
6121
6308
|
const title = this.s(ex, "title", path);
|
|
6122
|
-
if (
|
|
6309
|
+
if (goTrimSpace2(title) === "") {
|
|
6123
6310
|
this.err(
|
|
6124
6311
|
"example_title_missing",
|
|
6125
6312
|
`${path}.title`,
|
|
@@ -6136,7 +6323,7 @@ var Walker = class _Walker {
|
|
|
6136
6323
|
}
|
|
6137
6324
|
}
|
|
6138
6325
|
const guidance = this.s(ex, "guidance", path);
|
|
6139
|
-
if (
|
|
6326
|
+
if (goTrimSpace2(guidance) === "") {
|
|
6140
6327
|
this.warn(
|
|
6141
6328
|
"example_guidance_missing",
|
|
6142
6329
|
`${path}.guidance`,
|
|
@@ -6563,7 +6750,7 @@ var Walker = class _Walker {
|
|
|
6563
6750
|
}
|
|
6564
6751
|
const rubric = this.s(e, "rubric", path);
|
|
6565
6752
|
if (rubric !== "") {
|
|
6566
|
-
if (
|
|
6753
|
+
if (goTrimSpace2(rubric) === "") {
|
|
6567
6754
|
this.err(
|
|
6568
6755
|
"example_rubric_empty",
|
|
6569
6756
|
`${path}.rubric`,
|
|
@@ -6618,7 +6805,7 @@ var Walker = class _Walker {
|
|
|
6618
6805
|
this.err("example_must_not_invalid", `${path}.must_not`, mustNotMsg);
|
|
6619
6806
|
}
|
|
6620
6807
|
mustNot.forEach((item, j) => {
|
|
6621
|
-
if (
|
|
6808
|
+
if (goTrimSpace2(item) === "" || runeCount(item) > L.maxMustNotRunes) {
|
|
6622
6809
|
this.err("example_must_not_invalid", `${path}.must_not[${j}]`, mustNotMsg);
|
|
6623
6810
|
}
|
|
6624
6811
|
});
|
|
@@ -7176,6 +7363,7 @@ function validateParsed(flow, opts, sources) {
|
|
|
7176
7363
|
const f = flow;
|
|
7177
7364
|
validateBasicStructure(f, issues);
|
|
7178
7365
|
validateRetiredKeys(f, issues);
|
|
7366
|
+
validateRemovedFlowKeys(f, issues);
|
|
7179
7367
|
validateExecutors(f, issues);
|
|
7180
7368
|
validateQuerySchema(f, issues);
|
|
7181
7369
|
validateResponseExpectations(f, issues);
|
|
@@ -7187,6 +7375,7 @@ function validateParsed(flow, opts, sources) {
|
|
|
7187
7375
|
validateOrchestratorCampaign(f, issues, opts);
|
|
7188
7376
|
validateCredentialBindings(f, issues);
|
|
7189
7377
|
validateInputSchema(f, issues);
|
|
7378
|
+
validateReservedInputNames(f, issues);
|
|
7190
7379
|
validateOutputSchemas(f, issues);
|
|
7191
7380
|
validateQualityGates(f, issues);
|
|
7192
7381
|
validateProcessingOperations(f, issues);
|