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