@vaudience/aigentflow-flow-validator 0.26.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/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.831.0",
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: "}",
@@ -2020,6 +2044,19 @@ var EXONS = {
2020
2044
  /** The YAML frontmatter delimiter line. */
2021
2045
  frontmatterDelimiter: aigentflow_spec_default.exons.frontmatterDelimiter
2022
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
+ };
2023
2060
  var EXECUTOR_CONFIG_ENV_SCOPES = {
2024
2061
  referencePrefix: aigentflow_spec_default.executorConfigEnvScopes.referencePrefix,
2025
2062
  referenceSuffix: aigentflow_spec_default.executorConfigEnvScopes.referenceSuffix,
@@ -2141,6 +2178,227 @@ var RESERVED_INPUT_NAMES = {
2141
2178
  queryFieldFormat: aigentflow_spec_default.reservedInputNames.queryFieldFormat,
2142
2179
  schemaFieldFormat: aigentflow_spec_default.reservedInputNames.schemaFieldFormat
2143
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 {
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
+ }
2144
2402
 
2145
2403
  // src/validators/executors.ts
2146
2404
  var SCHEME_SEPARATOR = "://";
@@ -2204,6 +2462,56 @@ function checkExecutor(executor, field, stepID, issues) {
2204
2462
  }
2205
2463
  checkFunctionOperation(executor, field, stepID, templated, issues);
2206
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
+ }
2207
2515
  function checkAtlasStep(executor, query, stepField, stepID, issues) {
2208
2516
  if (executor.includes(TEMPLATE_ACTION_OPEN)) return;
2209
2517
  const prefix = `${ATLAS_EXECUTOR.scheme}://`;
@@ -2284,7 +2592,9 @@ function validateExecutors(flow, issues) {
2284
2592
  const step = rawStep;
2285
2593
  const executor = issues.stringOf(step, "executor", `steps.${stepID}`);
2286
2594
  if (executor !== null && executor !== "") {
2287
- checkExecutor(executor, `steps.${stepID}.executor`, stepID, issues);
2595
+ if (!checkRemovedSurfaces(executor, step.query, `steps.${stepID}`, stepID, issues)) {
2596
+ checkExecutor(executor, `steps.${stepID}.executor`, stepID, issues);
2597
+ }
2288
2598
  checkAtlasStep(executor, step.query, `steps.${stepID}`, stepID, issues);
2289
2599
  }
2290
2600
  const loop = step.loop;
@@ -2296,12 +2606,14 @@ function validateExecutors(flow, issues) {
2296
2606
  const subID = issues.stringOf(subStep, "id", subPath) ?? String(i);
2297
2607
  const subExecutor = issues.stringOf(subStep, "executor", subPath);
2298
2608
  if (subExecutor !== null && subExecutor !== "") {
2299
- checkExecutor(
2300
- subExecutor,
2301
- `steps.${stepID}.loop.steps[${i}].executor`,
2302
- `${stepID}.${subID}`,
2303
- issues
2304
- );
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
+ }
2305
2617
  checkAtlasStep(subExecutor, subStep.query, subPath, `${stepID}.${subID}`, issues);
2306
2618
  }
2307
2619
  });
@@ -5139,9 +5451,9 @@ function refuse(issues, stepId, key, field) {
5139
5451
  }
5140
5452
 
5141
5453
  // src/validators/goUrl.ts
5142
- var GO_SPACE = "\\t\\n\\v\\f\\r \\u0085\\u00A0\\u1680\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000";
5143
- var GO_TRIM_SPACE_RE = new RegExp(`^[${GO_SPACE}]+|[${GO_SPACE}]+$`, "g");
5144
- function goTrimSpace(s) {
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) {
5145
5457
  return s.replace(GO_TRIM_SPACE_RE, "");
5146
5458
  }
5147
5459
  function goToLower(s) {
@@ -5362,7 +5674,7 @@ function goQueryKeys(rawQuery) {
5362
5674
  return keys;
5363
5675
  }
5364
5676
  function goEndpointOrigin(raw, defaultPorts, protocolSeparator) {
5365
- const trimmed = goTrimSpace(raw);
5677
+ const trimmed = goTrimSpace2(raw);
5366
5678
  const u = goParseSchemeHost(trimmed);
5367
5679
  if (u === null || u.scheme === "" || u.host === "") {
5368
5680
  return goToLower(trimmed.replace(/\/+$/, ""));
@@ -5596,227 +5908,6 @@ function validateFamilies(flow, steps, issues) {
5596
5908
  });
5597
5909
  }
5598
5910
  }
5599
- var CODE_PARSE_FAILED = "orchestrator_exons_parse_failed";
5600
- var CODE_NO_PROVIDER = "orchestrator_exons_no_provider";
5601
- var CODE_RESOURCES = "exons_resources_unhonoured";
5602
- var CODE_TOOL_WITHHELD = "orchestrator_tool_withheld";
5603
- var EXONS_OPEN_DELIM = "{~";
5604
- var EXONS_LEGACY_CONFIG_OPEN = "{~exons.config~}";
5605
- var UTF8_BOM = "\uFEFF";
5606
- var MAX_ALIAS_COUNT2 = 100;
5607
- var UNREAD = {
5608
- parseError: null,
5609
- specRead: false,
5610
- hasSpec: false,
5611
- provider: "",
5612
- toolsAllowDeclared: false,
5613
- toolsAllow: [],
5614
- resources: []
5615
- };
5616
- var GO_SPACE2 = "\\t\\n\\v\\f\\r \\u0085\\u00a0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000";
5617
- var GO_TRIM_SPACE = new RegExp(`^[${GO_SPACE2}]+|[${GO_SPACE2}]+$`, "g");
5618
- function goTrimSpace2(s) {
5619
- return s.replace(GO_TRIM_SPACE, "");
5620
- }
5621
- function extractExonsFrontmatter(source) {
5622
- const delim = EXONS.frontmatterDelimiter;
5623
- let start = source.startsWith(UTF8_BOM) ? source.slice(UTF8_BOM.length) : source;
5624
- start = start.replace(/^[ \t]+/, "");
5625
- if (start.startsWith(EXONS_LEGACY_CONFIG_OPEN)) {
5626
- return {
5627
- frontmatter: "",
5628
- hasFrontmatter: false,
5629
- parseError: "legacy JSON config block detected; use YAML frontmatter"
5630
- };
5631
- }
5632
- if (!start.startsWith(delim)) return { frontmatter: "", hasFrontmatter: false, parseError: null };
5633
- let rest = start.slice(delim.length);
5634
- if (rest.startsWith("\n")) rest = rest.slice(1);
5635
- else if (rest.startsWith("\r\n")) rest = rest.slice(2);
5636
- else return { frontmatter: "", hasFrontmatter: false, parseError: null };
5637
- let pos = 0;
5638
- while (pos <= rest.length) {
5639
- const line = rest.slice(pos);
5640
- if (line.startsWith(delim)) {
5641
- const after = line.slice(delim.length);
5642
- if (after === "" || after[0] === "\n" || after[0] === "\r") {
5643
- return {
5644
- frontmatter: goTrimSpace2(rest.slice(0, pos)),
5645
- hasFrontmatter: true,
5646
- parseError: null
5647
- };
5648
- }
5649
- }
5650
- const nl = line.indexOf("\n");
5651
- if (nl < 0) break;
5652
- pos += nl + 1;
5653
- }
5654
- return {
5655
- frontmatter: "",
5656
- hasFrontmatter: true,
5657
- parseError: "YAML frontmatter not properly closed"
5658
- };
5659
- }
5660
- function retagScalars(node) {
5661
- if (yaml.isMap(node)) {
5662
- for (const pair of node.items) {
5663
- retagScalars(pair.key);
5664
- retagScalars(pair.value);
5665
- }
5666
- } else if (yaml.isSeq(node)) {
5667
- for (const item of node.items) retagScalars(item);
5668
- } else if (yaml.isScalar(node) && (typeof node.value === "number" || typeof node.value === "boolean") && typeof node.source === "string") {
5669
- node.value = node.source;
5670
- }
5671
- }
5672
- var DecodeError = class extends Error {
5673
- };
5674
- function goString(v) {
5675
- if (v === null || v === void 0) return "";
5676
- if (isString(v)) return v;
5677
- throw new DecodeError("not a string");
5678
- }
5679
- function goStringList(v) {
5680
- if (v === null || v === void 0) return null;
5681
- if (!isArray(v)) throw new DecodeError("not a list");
5682
- return v.filter((item) => item !== null).map(goString);
5683
- }
5684
- function goStruct(v) {
5685
- if (v === null || v === void 0) return null;
5686
- if (!isRecord(v)) throw new DecodeError("not a mapping");
5687
- return v;
5688
- }
5689
- function decodeSpecFields(frontmatter) {
5690
- let value;
5691
- try {
5692
- const docs = yaml.parseAllDocuments(frontmatter, { merge: true });
5693
- const doc = Array.isArray(docs) ? docs[0] : void 0;
5694
- if (doc === void 0) {
5695
- value = null;
5696
- } else {
5697
- if (doc.errors.length > 0) return null;
5698
- retagScalars(doc.contents);
5699
- value = doc.toJS({ maxAliasCount: MAX_ALIAS_COUNT2 });
5700
- }
5701
- } catch {
5702
- return null;
5703
- }
5704
- try {
5705
- const root = goStruct(value) ?? {};
5706
- const execution = goStruct(root.execution);
5707
- const tools = goStruct(root.tools);
5708
- const requirements = goStruct(root.requirements);
5709
- const allow = tools === null ? null : goStringList(tools.allow);
5710
- let resources = [];
5711
- if (requirements !== null) {
5712
- const raw = requirements.resources;
5713
- if (raw !== null && raw !== void 0) {
5714
- if (!isArray(raw)) throw new DecodeError("not a list");
5715
- resources = raw.filter((entry) => entry !== null).map((entry) => {
5716
- const r = goStruct(entry) ?? {};
5717
- return { ref: goString(r.ref), kind: goString(r.kind), scope: goString(r.scope) };
5718
- });
5719
- }
5720
- }
5721
- return {
5722
- provider: execution === null ? "" : goString(execution.provider),
5723
- toolsAllowDeclared: allow !== null,
5724
- toolsAllow: allow ?? [],
5725
- resources
5726
- };
5727
- } catch (e) {
5728
- if (e instanceof DecodeError) return null;
5729
- throw e;
5730
- }
5731
- }
5732
- function inspectExonsFrontmatter(source) {
5733
- const fm = extractExonsFrontmatter(source);
5734
- if (fm.parseError !== null) return { ...UNREAD, parseError: fm.parseError };
5735
- if (!fm.hasFrontmatter || fm.frontmatter === "") return { ...UNREAD, specRead: true };
5736
- if (fm.frontmatter.includes(EXONS_OPEN_DELIM)) return { ...UNREAD };
5737
- const fields = decodeSpecFields(fm.frontmatter);
5738
- if (fields === null) return { ...UNREAD };
5739
- return { parseError: null, specRead: true, hasSpec: true, ...fields };
5740
- }
5741
- function validateOrchestratorExons(flow, issues) {
5742
- const orch = flow.orchestrator;
5743
- if (!isRecord(orch)) return;
5744
- const source = issues.stringOf(orch, "exons", "orchestrator");
5745
- if (source === null || source === "") return;
5746
- const field = "orchestrator.exons";
5747
- const report = inspectExonsFrontmatter(source);
5748
- if (report.parseError !== null) {
5749
- issues.error({
5750
- field,
5751
- code: CODE_PARSE_FAILED,
5752
- message: `orchestrator: failed to parse exons spec: ${report.parseError}`
5753
- });
5754
- return;
5755
- }
5756
- if (!report.specRead) return;
5757
- if (!report.hasSpec) {
5758
- issues.error({
5759
- field,
5760
- code: CODE_PARSE_FAILED,
5761
- message: "orchestrator: failed to parse exons spec: the document has no frontmatter spec",
5762
- suggestion: "Start the document with a --- frontmatter block that sets execution.provider"
5763
- });
5764
- return;
5765
- }
5766
- if (report.provider === "") {
5767
- issues.error({
5768
- field,
5769
- code: CODE_NO_PROVIDER,
5770
- message: "orchestrator: exons spec must have execution.provider set"
5771
- });
5772
- }
5773
- if (report.resources.length > 0) {
5774
- const refs = report.resources.map((r) => `${JSON.stringify(r.ref)} (kind ${r.kind})`);
5775
- issues.error({
5776
- field,
5777
- code: CODE_RESOURCES,
5778
- 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("; "),
5779
- suggestion: "Remove requirements.resources, and narrow the agent with tools.mcp_servers and tools.allow"
5780
- });
5781
- }
5782
- warnOrchestratorToolWithheld(flow, orch, report, issues);
5783
- }
5784
- function warnOrchestratorToolWithheld(flow, orch, report, issues) {
5785
- if (!report.toolsAllowDeclared) return;
5786
- const cfg = ORCHESTRATOR_TOOL_ALLOW;
5787
- const exempt = new Set(cfg.lifecycleTools);
5788
- const signals = typeof orch.enable_signals === "boolean" ? orch.enable_signals : cfg.enableSignalsDefault;
5789
- if (signals) for (const t of cfg.signalTools) exempt.add(t);
5790
- const allow = new Set(report.toolsAllow);
5791
- const withheld = (name) => !allow.has(name) && !exempt.has(name);
5792
- const warn = (message) => issues.warn({ field: cfg.warningField, code: CODE_TOOL_WITHHELD, message });
5793
- const named = [];
5794
- if (isArray(orch.tools)) {
5795
- orch.tools.forEach((raw, i) => {
5796
- const name = issues.stringAt(raw, `orchestrator.tools[${i}]`);
5797
- if (name !== null) named.push(name);
5798
- });
5799
- }
5800
- if (named.length > 0) {
5801
- for (const name of named) {
5802
- if (!withheld(name)) continue;
5803
- warn(
5804
- `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.`
5805
- );
5806
- }
5807
- return;
5808
- }
5809
- if (withheld(cfg.askHumanTool)) {
5810
- warn(
5811
- `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.`
5812
- );
5813
- }
5814
- if (isRecord(flow.campaign) && !cfg.campaignTools.some((name) => !withheld(name))) {
5815
- warn(
5816
- `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.`
5817
- );
5818
- }
5819
- }
5820
5911
 
5821
5912
  // src/validators/complianceResidency.ts
5822
5913
  function own2(m, key) {
@@ -5831,7 +5922,7 @@ function sprintf(format, ...args) {
5831
5922
  });
5832
5923
  }
5833
5924
  function literalOrEmpty(s) {
5834
- const t = goTrimSpace2(s);
5925
+ const t = goTrimSpace(s);
5835
5926
  return t.includes(TEMPLATE_ACTION_OPEN) ? "" : t;
5836
5927
  }
5837
5928
  function euOnlyResidency(flow, issues) {
@@ -5846,7 +5937,7 @@ function euOnlyResidency(flow, issues) {
5846
5937
  }
5847
5938
  if (codes.length === 0) return null;
5848
5939
  const eu = new Set(COMPLIANCE_RESIDENCY.euResidencyCodes);
5849
- return codes.every((c) => eu.has(goTrimSpace2(c).toUpperCase())) ? codes : null;
5940
+ return codes.every((c) => eu.has(goTrimSpace(c).toUpperCase())) ? codes : null;
5850
5941
  }
5851
5942
  function aiProvider(executor) {
5852
5943
  const prefix = COMPLIANCE_RESIDENCY.aiProtocol + COMPLIANCE_RESIDENCY.protocolSeparator;
@@ -5862,7 +5953,7 @@ function isRegionLane(provider) {
5862
5953
  }
5863
5954
  function nonBlankString(m, key) {
5864
5955
  const v = own2(m, key);
5865
- return typeof v === "string" && goTrimSpace2(v) !== "";
5956
+ return typeof v === "string" && goTrimSpace(v) !== "";
5866
5957
  }
5867
5958
  function queryEnforcesRegion(query) {
5868
5959
  const v = own2(query, COMPLIANCE_RESIDENCY.enforceRegionParam);
@@ -5874,7 +5965,7 @@ function flowDirectsEndpoint(flow, provider, issues) {
5874
5965
  const inst = own2(own2(flow, "executor_config"), key);
5875
5966
  if (!isRecord(inst)) return false;
5876
5967
  const baseUrl = issues.stringOf(inst, "base_url", `executor_config.${key}`);
5877
- return baseUrl !== null && goTrimSpace2(baseUrl) !== "";
5968
+ return baseUrl !== null && goTrimSpace(baseUrl) !== "";
5878
5969
  }
5879
5970
  function callPinsEndpoint(flow, provider, query, issues) {
5880
5971
  if (nonBlankString(query, COMPLIANCE_RESIDENCY.regionParam)) return true;
@@ -5947,7 +6038,7 @@ function warnOrchestrator(flow, issues, warn) {
5947
6038
  const provider = literalOrEmpty(report.provider);
5948
6039
  if (!isRegionLane(provider)) return;
5949
6040
  const region = issues.stringOf(orch, "region", "orchestrator");
5950
- if (region !== null && goTrimSpace2(region) !== "") return;
6041
+ if (region !== null && goTrimSpace(region) !== "") return;
5951
6042
  if (orch.enforce_region === true) return;
5952
6043
  if (flowDirectsEndpoint(flow, provider, issues)) return;
5953
6044
  warn(COMPLIANCE_RESIDENCY.siteOrchestrator, COMPLIANCE_RESIDENCY.orchestratorField, void 0, provider);
@@ -6113,7 +6204,7 @@ function asFileRef(v) {
6113
6204
  };
6114
6205
  }
6115
6206
  function isTextualMediaType(mediaType) {
6116
- const base = goToLower(goTrimSpace((mediaType.split(";")[0] ?? "").toString()));
6207
+ const base = goToLower(goTrimSpace2((mediaType.split(";")[0] ?? "").toString()));
6117
6208
  if (base.startsWith("text/")) return true;
6118
6209
  if (base.endsWith("+json") || base.endsWith("+xml")) return true;
6119
6210
  return TEXTUAL_MEDIA_BASES.has(base);
@@ -6217,7 +6308,7 @@ var Walker = class _Walker {
6217
6308
  }
6218
6309
  }
6219
6310
  const title = this.s(ex, "title", path);
6220
- if (goTrimSpace(title) === "") {
6311
+ if (goTrimSpace2(title) === "") {
6221
6312
  this.err(
6222
6313
  "example_title_missing",
6223
6314
  `${path}.title`,
@@ -6234,7 +6325,7 @@ var Walker = class _Walker {
6234
6325
  }
6235
6326
  }
6236
6327
  const guidance = this.s(ex, "guidance", path);
6237
- if (goTrimSpace(guidance) === "") {
6328
+ if (goTrimSpace2(guidance) === "") {
6238
6329
  this.warn(
6239
6330
  "example_guidance_missing",
6240
6331
  `${path}.guidance`,
@@ -6661,7 +6752,7 @@ var Walker = class _Walker {
6661
6752
  }
6662
6753
  const rubric = this.s(e, "rubric", path);
6663
6754
  if (rubric !== "") {
6664
- if (goTrimSpace(rubric) === "") {
6755
+ if (goTrimSpace2(rubric) === "") {
6665
6756
  this.err(
6666
6757
  "example_rubric_empty",
6667
6758
  `${path}.rubric`,
@@ -6716,7 +6807,7 @@ var Walker = class _Walker {
6716
6807
  this.err("example_must_not_invalid", `${path}.must_not`, mustNotMsg);
6717
6808
  }
6718
6809
  mustNot.forEach((item, j) => {
6719
- if (goTrimSpace(item) === "" || runeCount(item) > L.maxMustNotRunes) {
6810
+ if (goTrimSpace2(item) === "" || runeCount(item) > L.maxMustNotRunes) {
6720
6811
  this.err("example_must_not_invalid", `${path}.must_not[${j}]`, mustNotMsg);
6721
6812
  }
6722
6813
  });
@@ -7274,6 +7365,7 @@ function validateParsed(flow, opts, sources) {
7274
7365
  const f = flow;
7275
7366
  validateBasicStructure(f, issues);
7276
7367
  validateRetiredKeys(f, issues);
7368
+ validateRemovedFlowKeys(f, issues);
7277
7369
  validateExecutors(f, issues);
7278
7370
  validateQuerySchema(f, issues);
7279
7371
  validateResponseExpectations(f, issues);