@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/cli.js CHANGED
@@ -455,11 +455,10 @@ function validateBasicStructure(flow, issues) {
455
455
  // src/spec/aigentflow-spec.json
456
456
  var aigentflow_spec_default = {
457
457
  $comment: "Vendored enum surface mirrored from the AIgentFlow Go reference implementation. Regenerate / review on every flow-YAML schema change. See PARITY.md. Values here are public protocol/type names only.",
458
- specVersion: "2.831.0",
458
+ specVersion: "2.833.0",
459
459
  inputSchemaVersion: 1,
460
460
  executorSchemes: [
461
461
  "a2a",
462
- "agent",
463
462
  "ai",
464
463
  "aiv",
465
464
  "async",
@@ -473,7 +472,6 @@ var aigentflow_spec_default = {
473
472
  "email",
474
473
  "eurostat",
475
474
  "eval",
476
- "exons",
477
475
  "flow",
478
476
  "function",
479
477
  "gdelt",
@@ -778,11 +776,36 @@ var aigentflow_spec_default = {
778
776
  ]
779
777
  },
780
778
  exons: {
781
- $comment: "Where an inline .exons document arrives at the save door. A step's document is judged only on an executor starting executorPrefix, from the string query parameter documentParam, and never when it contains templateActionOpen (the engine renders step params first). Reference: validateInlineExonsStep, validateOrchestrator (v2.767.0, v2.777.0).",
782
- executorPrefix: "exons://",
783
- documentParam: "exons",
779
+ $comment: "The orchestrator's inline .exons document is still judged at the save door (orchestrator stays). A document opens with frontmatterDelimiter. Steps no longer carry inline .exons documents: the exons:// executor was removed (see removedAgentSurfaces).",
784
780
  frontmatterDelimiter: "---"
785
781
  },
782
+ removedAgentSurfaces: {
783
+ $comment: "AIgentFlow no longer runs agents of its own. executor_removed (ERROR): a step or loop sub-step whose executor scheme (text before '://', whitespace-trimmed, lower-cased) is in removedSchemes, or a top-level key in flowKeys (field = the key). agentic_param_removed (ERROR): an ai:// step or loop sub-step whose query carries ANY key of aiParams, on presence alone (null and false included); ONE finding per step, the keys sorted and joined by ', '. Only the paramScheme scheme is checked for params. A templated executor (containing templateActionOpen) is not judged. A step on a removed scheme yields ONLY executor_removed: no unknown_executor_scheme, executor-URL shape or inline-exons finding. Fields: steps.<id> and steps.<id>.loop.steps[<index>]. Not refused: aiv://, atlas://, ai:// tools / tool_choice / autofix / content_parts / stream, flow-level tool_discovery / discoverable_tools, the orchestrator block's own keys. Messages fill %s (the step id as written in the finding, then the scheme or the key list).",
784
+ removedSchemes: ["exons", "agent"],
785
+ paramScheme: "ai",
786
+ aiParams: [
787
+ "agentic",
788
+ "deny_tools",
789
+ "discoverable_tools",
790
+ "enable_signals",
791
+ "guardrails",
792
+ "max_tool_calls",
793
+ "max_tool_turns",
794
+ "mcp_server_url",
795
+ "parallel_tool_calls",
796
+ "require_confirmation",
797
+ "timeout_seconds",
798
+ "tool_auth_token",
799
+ "tool_discovery"
800
+ ],
801
+ flowKeys: ["exons_uploads"],
802
+ executorRemovedCode: "executor_removed",
803
+ agenticParamRemovedCode: "agentic_param_removed",
804
+ schemeMessageFormat: "step %q uses %s://, which AIgentFlow no longer runs: its own agent chains were removed",
805
+ paramsMessageFormat: "step %q sets %s on ai://: AIgentFlow's own agent loop (agentic mode) was removed, so these parameters no longer exist",
806
+ flowKeyMessageFormat: "the flow-level key %q belonged to a removed agent surface and is no longer read",
807
+ 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'
808
+ },
786
809
  executorConfigEnvScopes: {
787
810
  $comment: "executor_config may expand only the environment variables of the key it is written under. A value is a reference when it starts with referencePrefix and ends with referenceSuffix around a non-empty name. Checked fields: fields, plus every string value of extra (prefixed extraFieldPrefix in the message). A key absent from scopes has an EMPTY scope and refuses every reference. scopes is the reference's executorConfigEnvScope evaluated for every key with a non-empty scope. Reference: ValidateExecutorConfigEnvScopes (v2.597.0).",
788
811
  referencePrefix: "${",
@@ -2111,13 +2134,22 @@ var ORCHESTRATOR_TOOL_ALLOW = {
2111
2134
  campaignTools: aigentflow_spec_default.orchestratorToolAllow.campaignTools
2112
2135
  };
2113
2136
  var EXONS = {
2114
- /** A step's document is judged only on an executor with this prefix. */
2115
- executorPrefix: aigentflow_spec_default.exons.executorPrefix,
2116
- /** The step `query` parameter that carries the document. */
2117
- documentParam: aigentflow_spec_default.exons.documentParam,
2118
2137
  /** The YAML frontmatter delimiter line. */
2119
2138
  frontmatterDelimiter: aigentflow_spec_default.exons.frontmatterDelimiter
2120
2139
  };
2140
+ var removed = aigentflow_spec_default.removedAgentSurfaces;
2141
+ var REMOVED_AGENT_SURFACES = {
2142
+ schemes: new Set(removed.removedSchemes),
2143
+ paramScheme: removed.paramScheme,
2144
+ aiParams: new Set(removed.aiParams),
2145
+ flowKeys: removed.flowKeys,
2146
+ executorRemovedCode: removed.executorRemovedCode,
2147
+ agenticParamRemovedCode: removed.agenticParamRemovedCode,
2148
+ schemeMessageFormat: removed.schemeMessageFormat,
2149
+ paramsMessageFormat: removed.paramsMessageFormat,
2150
+ flowKeyMessageFormat: removed.flowKeyMessageFormat,
2151
+ suggestion: removed.suggestion
2152
+ };
2121
2153
  var EXECUTOR_CONFIG_ENV_SCOPES = {
2122
2154
  referencePrefix: aigentflow_spec_default.executorConfigEnvScopes.referencePrefix,
2123
2155
  referenceSuffix: aigentflow_spec_default.executorConfigEnvScopes.referenceSuffix,
@@ -2243,6 +2275,230 @@ var RESERVED_INPUT_NAMES = {
2243
2275
  schemaFieldFormat: aigentflow_spec_default.reservedInputNames.schemaFieldFormat
2244
2276
  };
2245
2277
 
2278
+ // src/validators/exons.ts
2279
+ import { isMap as isMap2, isScalar as isScalar2, isSeq as isSeq2, parseAllDocuments } from "yaml";
2280
+ var CODE_PARSE_FAILED = "orchestrator_exons_parse_failed";
2281
+ var CODE_NO_PROVIDER = "orchestrator_exons_no_provider";
2282
+ var CODE_RESOURCES = "exons_resources_unhonoured";
2283
+ var CODE_TOOL_WITHHELD = "orchestrator_tool_withheld";
2284
+ var EXONS_OPEN_DELIM = "{~";
2285
+ var EXONS_LEGACY_CONFIG_OPEN = "{~exons.config~}";
2286
+ var UTF8_BOM = "\uFEFF";
2287
+ var MAX_ALIAS_COUNT2 = 100;
2288
+ var UNREAD = {
2289
+ parseError: null,
2290
+ specRead: false,
2291
+ hasSpec: false,
2292
+ provider: "",
2293
+ toolsAllowDeclared: false,
2294
+ toolsAllow: [],
2295
+ resources: []
2296
+ };
2297
+ var GO_SPACE = "\\t\\n\\v\\f\\r \\u0085\\u00a0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000";
2298
+ var GO_TRIM_SPACE = new RegExp(`^[${GO_SPACE}]+|[${GO_SPACE}]+$`, "g");
2299
+ function goTrimSpace(s) {
2300
+ return s.replace(GO_TRIM_SPACE, "");
2301
+ }
2302
+ function extractExonsFrontmatter(source) {
2303
+ const delim = EXONS.frontmatterDelimiter;
2304
+ let start = source.startsWith(UTF8_BOM) ? source.slice(UTF8_BOM.length) : source;
2305
+ start = start.replace(/^[ \t]+/, "");
2306
+ if (start.startsWith(EXONS_LEGACY_CONFIG_OPEN)) {
2307
+ return {
2308
+ frontmatter: "",
2309
+ hasFrontmatter: false,
2310
+ parseError: "legacy JSON config block detected; use YAML frontmatter"
2311
+ };
2312
+ }
2313
+ if (!start.startsWith(delim)) return { frontmatter: "", hasFrontmatter: false, parseError: null };
2314
+ let rest = start.slice(delim.length);
2315
+ if (rest.startsWith("\n")) rest = rest.slice(1);
2316
+ else if (rest.startsWith("\r\n")) rest = rest.slice(2);
2317
+ else return { frontmatter: "", hasFrontmatter: false, parseError: null };
2318
+ let pos = 0;
2319
+ while (pos <= rest.length) {
2320
+ const line = rest.slice(pos);
2321
+ if (line.startsWith(delim)) {
2322
+ const after = line.slice(delim.length);
2323
+ if (after === "" || after[0] === "\n" || after[0] === "\r") {
2324
+ return {
2325
+ frontmatter: goTrimSpace(rest.slice(0, pos)),
2326
+ hasFrontmatter: true,
2327
+ parseError: null
2328
+ };
2329
+ }
2330
+ }
2331
+ const nl = line.indexOf("\n");
2332
+ if (nl < 0) break;
2333
+ pos += nl + 1;
2334
+ }
2335
+ return {
2336
+ frontmatter: "",
2337
+ hasFrontmatter: true,
2338
+ parseError: "YAML frontmatter not properly closed"
2339
+ };
2340
+ }
2341
+ function retagScalars(node) {
2342
+ if (isMap2(node)) {
2343
+ for (const pair of node.items) {
2344
+ retagScalars(pair.key);
2345
+ retagScalars(pair.value);
2346
+ }
2347
+ } else if (isSeq2(node)) {
2348
+ for (const item of node.items) retagScalars(item);
2349
+ } else if (isScalar2(node) && (typeof node.value === "number" || typeof node.value === "boolean") && typeof node.source === "string") {
2350
+ node.value = node.source;
2351
+ }
2352
+ }
2353
+ var DecodeError = class extends Error {
2354
+ };
2355
+ function goString(v) {
2356
+ if (v === null || v === void 0) return "";
2357
+ if (isString(v)) return v;
2358
+ throw new DecodeError("not a string");
2359
+ }
2360
+ function goStringList(v) {
2361
+ if (v === null || v === void 0) return null;
2362
+ if (!isArray(v)) throw new DecodeError("not a list");
2363
+ return v.filter((item) => item !== null).map(goString);
2364
+ }
2365
+ function goStruct(v) {
2366
+ if (v === null || v === void 0) return null;
2367
+ if (!isRecord(v)) throw new DecodeError("not a mapping");
2368
+ return v;
2369
+ }
2370
+ function decodeSpecFields(frontmatter) {
2371
+ let value;
2372
+ try {
2373
+ const docs = parseAllDocuments(frontmatter, { merge: true });
2374
+ const doc = Array.isArray(docs) ? docs[0] : void 0;
2375
+ if (doc === void 0) {
2376
+ value = null;
2377
+ } else {
2378
+ if (doc.errors.length > 0) return null;
2379
+ retagScalars(doc.contents);
2380
+ value = doc.toJS({ maxAliasCount: MAX_ALIAS_COUNT2 });
2381
+ }
2382
+ } catch {
2383
+ return null;
2384
+ }
2385
+ try {
2386
+ const root = goStruct(value) ?? {};
2387
+ const execution = goStruct(root.execution);
2388
+ const tools = goStruct(root.tools);
2389
+ const requirements = goStruct(root.requirements);
2390
+ const allow = tools === null ? null : goStringList(tools.allow);
2391
+ let resources = [];
2392
+ if (requirements !== null) {
2393
+ const raw = requirements.resources;
2394
+ if (raw !== null && raw !== void 0) {
2395
+ if (!isArray(raw)) throw new DecodeError("not a list");
2396
+ resources = raw.filter((entry) => entry !== null).map((entry) => {
2397
+ const r = goStruct(entry) ?? {};
2398
+ return { ref: goString(r.ref), kind: goString(r.kind), scope: goString(r.scope) };
2399
+ });
2400
+ }
2401
+ }
2402
+ return {
2403
+ provider: execution === null ? "" : goString(execution.provider),
2404
+ toolsAllowDeclared: allow !== null,
2405
+ toolsAllow: allow ?? [],
2406
+ resources
2407
+ };
2408
+ } catch (e) {
2409
+ if (e instanceof DecodeError) return null;
2410
+ throw e;
2411
+ }
2412
+ }
2413
+ function inspectExonsFrontmatter(source) {
2414
+ const fm = extractExonsFrontmatter(source);
2415
+ if (fm.parseError !== null) return { ...UNREAD, parseError: fm.parseError };
2416
+ if (!fm.hasFrontmatter || fm.frontmatter === "") return { ...UNREAD, specRead: true };
2417
+ if (fm.frontmatter.includes(EXONS_OPEN_DELIM)) return { ...UNREAD };
2418
+ const fields = decodeSpecFields(fm.frontmatter);
2419
+ if (fields === null) return { ...UNREAD };
2420
+ return { parseError: null, specRead: true, hasSpec: true, ...fields };
2421
+ }
2422
+ function validateOrchestratorExons(flow, issues) {
2423
+ const orch = flow.orchestrator;
2424
+ if (!isRecord(orch)) return;
2425
+ const source = issues.stringOf(orch, "exons", "orchestrator");
2426
+ if (source === null || source === "") return;
2427
+ const field = "orchestrator.exons";
2428
+ const report = inspectExonsFrontmatter(source);
2429
+ if (report.parseError !== null) {
2430
+ issues.error({
2431
+ field,
2432
+ code: CODE_PARSE_FAILED,
2433
+ message: `orchestrator: failed to parse exons spec: ${report.parseError}`
2434
+ });
2435
+ return;
2436
+ }
2437
+ if (!report.specRead) return;
2438
+ if (!report.hasSpec) {
2439
+ issues.error({
2440
+ field,
2441
+ code: CODE_PARSE_FAILED,
2442
+ message: "orchestrator: failed to parse exons spec: the document has no frontmatter spec",
2443
+ suggestion: "Start the document with a --- frontmatter block that sets execution.provider"
2444
+ });
2445
+ return;
2446
+ }
2447
+ if (report.provider === "") {
2448
+ issues.error({
2449
+ field,
2450
+ code: CODE_NO_PROVIDER,
2451
+ message: "orchestrator: exons spec must have execution.provider set"
2452
+ });
2453
+ }
2454
+ if (report.resources.length > 0) {
2455
+ const refs = report.resources.map((r) => `${JSON.stringify(r.ref)} (kind ${r.kind})`);
2456
+ issues.error({
2457
+ field,
2458
+ code: CODE_RESOURCES,
2459
+ 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("; "),
2460
+ suggestion: "Remove requirements.resources, and narrow the agent with tools.mcp_servers and tools.allow"
2461
+ });
2462
+ }
2463
+ warnOrchestratorToolWithheld(flow, orch, report, issues);
2464
+ }
2465
+ function warnOrchestratorToolWithheld(flow, orch, report, issues) {
2466
+ if (!report.toolsAllowDeclared) return;
2467
+ const cfg = ORCHESTRATOR_TOOL_ALLOW;
2468
+ const exempt = new Set(cfg.lifecycleTools);
2469
+ const signals = typeof orch.enable_signals === "boolean" ? orch.enable_signals : cfg.enableSignalsDefault;
2470
+ if (signals) for (const t of cfg.signalTools) exempt.add(t);
2471
+ const allow = new Set(report.toolsAllow);
2472
+ const withheld = (name) => !allow.has(name) && !exempt.has(name);
2473
+ const warn = (message) => issues.warn({ field: cfg.warningField, code: CODE_TOOL_WITHHELD, message });
2474
+ const named = [];
2475
+ if (isArray(orch.tools)) {
2476
+ orch.tools.forEach((raw, i) => {
2477
+ const name = issues.stringAt(raw, `orchestrator.tools[${i}]`);
2478
+ if (name !== null) named.push(name);
2479
+ });
2480
+ }
2481
+ if (named.length > 0) {
2482
+ for (const name of named) {
2483
+ if (!withheld(name)) continue;
2484
+ warn(
2485
+ `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.`
2486
+ );
2487
+ }
2488
+ return;
2489
+ }
2490
+ if (withheld(cfg.askHumanTool)) {
2491
+ warn(
2492
+ `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.`
2493
+ );
2494
+ }
2495
+ if (isRecord(flow.campaign) && !cfg.campaignTools.some((name) => !withheld(name))) {
2496
+ warn(
2497
+ `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.`
2498
+ );
2499
+ }
2500
+ }
2501
+
2246
2502
  // src/validators/executors.ts
2247
2503
  var SCHEME_SEPARATOR = "://";
2248
2504
  var UNKNOWN_SCHEME_BASE = (scheme) => `Unknown executor scheme '${scheme}'. It may be valid in a newer AIgentFlow release, or it could be a typo.`;
@@ -2305,6 +2561,56 @@ function checkExecutor(executor, field, stepID, issues) {
2305
2561
  }
2306
2562
  checkFunctionOperation(executor, field, stepID, templated, issues);
2307
2563
  }
2564
+ function removedExecutorScheme(executor) {
2565
+ if (executor.includes(TEMPLATE_ACTION_OPEN)) return "";
2566
+ const trimmed = goTrimSpace(executor);
2567
+ const at = trimmed.indexOf(SCHEME_SEPARATOR);
2568
+ return at < 0 ? "" : trimmed.slice(0, at).toLowerCase();
2569
+ }
2570
+ function checkRemovedSurfaces(executor, query, field, stepID, issues) {
2571
+ const cfg = REMOVED_AGENT_SURFACES;
2572
+ const quoted = JSON.stringify(stepID);
2573
+ const scheme = removedExecutorScheme(executor);
2574
+ if (cfg.schemes.has(scheme)) {
2575
+ issues.error({
2576
+ field,
2577
+ message: fillRemovedMessage(cfg.schemeMessageFormat, quoted, scheme),
2578
+ code: cfg.executorRemovedCode,
2579
+ stepId: stepID,
2580
+ suggestion: cfg.suggestion
2581
+ });
2582
+ return true;
2583
+ }
2584
+ if (scheme !== cfg.paramScheme || !isRecord(query)) return false;
2585
+ const found = Object.keys(query).filter((key) => cfg.aiParams.has(key)).sort();
2586
+ if (found.length > 0) {
2587
+ issues.error({
2588
+ field,
2589
+ message: fillRemovedMessage(cfg.paramsMessageFormat, quoted, found.join(", ")),
2590
+ code: cfg.agenticParamRemovedCode,
2591
+ stepId: stepID,
2592
+ suggestion: cfg.suggestion
2593
+ });
2594
+ }
2595
+ return false;
2596
+ }
2597
+ function fillRemovedMessage(format, ...args) {
2598
+ let i = 0;
2599
+ return format.replace(/%[qs]/g, () => args[i++] ?? "");
2600
+ }
2601
+ function validateRemovedFlowKeys(flow, issues) {
2602
+ const cfg = REMOVED_AGENT_SURFACES;
2603
+ for (const key of cfg.flowKeys) {
2604
+ if (Object.prototype.hasOwnProperty.call(flow, key)) {
2605
+ issues.error({
2606
+ field: key,
2607
+ message: fillRemovedMessage(cfg.flowKeyMessageFormat, JSON.stringify(key)),
2608
+ code: cfg.executorRemovedCode,
2609
+ suggestion: cfg.suggestion
2610
+ });
2611
+ }
2612
+ }
2613
+ }
2308
2614
  function checkAtlasStep(executor, query, stepField, stepID, issues) {
2309
2615
  if (executor.includes(TEMPLATE_ACTION_OPEN)) return;
2310
2616
  const prefix = `${ATLAS_EXECUTOR.scheme}://`;
@@ -2385,7 +2691,9 @@ function validateExecutors(flow, issues) {
2385
2691
  const step = rawStep;
2386
2692
  const executor = issues.stringOf(step, "executor", `steps.${stepID}`);
2387
2693
  if (executor !== null && executor !== "") {
2388
- checkExecutor(executor, `steps.${stepID}.executor`, stepID, issues);
2694
+ if (!checkRemovedSurfaces(executor, step.query, `steps.${stepID}`, stepID, issues)) {
2695
+ checkExecutor(executor, `steps.${stepID}.executor`, stepID, issues);
2696
+ }
2389
2697
  checkAtlasStep(executor, step.query, `steps.${stepID}`, stepID, issues);
2390
2698
  }
2391
2699
  const loop = step.loop;
@@ -2397,12 +2705,14 @@ function validateExecutors(flow, issues) {
2397
2705
  const subID = issues.stringOf(subStep, "id", subPath) ?? String(i);
2398
2706
  const subExecutor = issues.stringOf(subStep, "executor", subPath);
2399
2707
  if (subExecutor !== null && subExecutor !== "") {
2400
- checkExecutor(
2401
- subExecutor,
2402
- `steps.${stepID}.loop.steps[${i}].executor`,
2403
- `${stepID}.${subID}`,
2404
- issues
2405
- );
2708
+ if (!checkRemovedSurfaces(subExecutor, subStep.query, subPath, `${stepID}.${subID}`, issues)) {
2709
+ checkExecutor(
2710
+ subExecutor,
2711
+ `steps.${stepID}.loop.steps[${i}].executor`,
2712
+ `${stepID}.${subID}`,
2713
+ issues
2714
+ );
2715
+ }
2406
2716
  checkAtlasStep(subExecutor, subStep.query, subPath, `${stepID}.${subID}`, issues);
2407
2717
  }
2408
2718
  });
@@ -5240,9 +5550,9 @@ function refuse(issues, stepId, key, field) {
5240
5550
  }
5241
5551
 
5242
5552
  // src/validators/goUrl.ts
5243
- var GO_SPACE = "\\t\\n\\v\\f\\r \\u0085\\u00A0\\u1680\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000";
5244
- var GO_TRIM_SPACE_RE = new RegExp(`^[${GO_SPACE}]+|[${GO_SPACE}]+$`, "g");
5245
- function goTrimSpace(s) {
5553
+ var GO_SPACE2 = "\\t\\n\\v\\f\\r \\u0085\\u00A0\\u1680\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000";
5554
+ var GO_TRIM_SPACE_RE = new RegExp(`^[${GO_SPACE2}]+|[${GO_SPACE2}]+$`, "g");
5555
+ function goTrimSpace2(s) {
5246
5556
  return s.replace(GO_TRIM_SPACE_RE, "");
5247
5557
  }
5248
5558
  function goToLower(s) {
@@ -5465,7 +5775,7 @@ function goQueryKeys(rawQuery) {
5465
5775
  return keys;
5466
5776
  }
5467
5777
  function goEndpointOrigin(raw, defaultPorts, protocolSeparator) {
5468
- const trimmed = goTrimSpace(raw);
5778
+ const trimmed = goTrimSpace2(raw);
5469
5779
  const u = goParseSchemeHost(trimmed);
5470
5780
  if (u === null || u.scheme === "" || u.host === "") {
5471
5781
  return goToLower(trimmed.replace(/\/+$/, ""));
@@ -5700,230 +6010,6 @@ function validateFamilies(flow, steps, issues) {
5700
6010
  }
5701
6011
  }
5702
6012
 
5703
- // src/validators/exons.ts
5704
- import { isMap as isMap2, isScalar as isScalar2, isSeq as isSeq2, parseAllDocuments } from "yaml";
5705
- var CODE_PARSE_FAILED = "orchestrator_exons_parse_failed";
5706
- var CODE_NO_PROVIDER = "orchestrator_exons_no_provider";
5707
- var CODE_RESOURCES = "exons_resources_unhonoured";
5708
- var CODE_TOOL_WITHHELD = "orchestrator_tool_withheld";
5709
- var EXONS_OPEN_DELIM = "{~";
5710
- var EXONS_LEGACY_CONFIG_OPEN = "{~exons.config~}";
5711
- var UTF8_BOM = "\uFEFF";
5712
- var MAX_ALIAS_COUNT2 = 100;
5713
- var UNREAD = {
5714
- parseError: null,
5715
- specRead: false,
5716
- hasSpec: false,
5717
- provider: "",
5718
- toolsAllowDeclared: false,
5719
- toolsAllow: [],
5720
- resources: []
5721
- };
5722
- var GO_SPACE2 = "\\t\\n\\v\\f\\r \\u0085\\u00a0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000";
5723
- var GO_TRIM_SPACE = new RegExp(`^[${GO_SPACE2}]+|[${GO_SPACE2}]+$`, "g");
5724
- function goTrimSpace2(s) {
5725
- return s.replace(GO_TRIM_SPACE, "");
5726
- }
5727
- function extractExonsFrontmatter(source) {
5728
- const delim = EXONS.frontmatterDelimiter;
5729
- let start = source.startsWith(UTF8_BOM) ? source.slice(UTF8_BOM.length) : source;
5730
- start = start.replace(/^[ \t]+/, "");
5731
- if (start.startsWith(EXONS_LEGACY_CONFIG_OPEN)) {
5732
- return {
5733
- frontmatter: "",
5734
- hasFrontmatter: false,
5735
- parseError: "legacy JSON config block detected; use YAML frontmatter"
5736
- };
5737
- }
5738
- if (!start.startsWith(delim)) return { frontmatter: "", hasFrontmatter: false, parseError: null };
5739
- let rest = start.slice(delim.length);
5740
- if (rest.startsWith("\n")) rest = rest.slice(1);
5741
- else if (rest.startsWith("\r\n")) rest = rest.slice(2);
5742
- else return { frontmatter: "", hasFrontmatter: false, parseError: null };
5743
- let pos = 0;
5744
- while (pos <= rest.length) {
5745
- const line = rest.slice(pos);
5746
- if (line.startsWith(delim)) {
5747
- const after = line.slice(delim.length);
5748
- if (after === "" || after[0] === "\n" || after[0] === "\r") {
5749
- return {
5750
- frontmatter: goTrimSpace2(rest.slice(0, pos)),
5751
- hasFrontmatter: true,
5752
- parseError: null
5753
- };
5754
- }
5755
- }
5756
- const nl = line.indexOf("\n");
5757
- if (nl < 0) break;
5758
- pos += nl + 1;
5759
- }
5760
- return {
5761
- frontmatter: "",
5762
- hasFrontmatter: true,
5763
- parseError: "YAML frontmatter not properly closed"
5764
- };
5765
- }
5766
- function retagScalars(node) {
5767
- if (isMap2(node)) {
5768
- for (const pair of node.items) {
5769
- retagScalars(pair.key);
5770
- retagScalars(pair.value);
5771
- }
5772
- } else if (isSeq2(node)) {
5773
- for (const item of node.items) retagScalars(item);
5774
- } else if (isScalar2(node) && (typeof node.value === "number" || typeof node.value === "boolean") && typeof node.source === "string") {
5775
- node.value = node.source;
5776
- }
5777
- }
5778
- var DecodeError = class extends Error {
5779
- };
5780
- function goString(v) {
5781
- if (v === null || v === void 0) return "";
5782
- if (isString(v)) return v;
5783
- throw new DecodeError("not a string");
5784
- }
5785
- function goStringList(v) {
5786
- if (v === null || v === void 0) return null;
5787
- if (!isArray(v)) throw new DecodeError("not a list");
5788
- return v.filter((item) => item !== null).map(goString);
5789
- }
5790
- function goStruct(v) {
5791
- if (v === null || v === void 0) return null;
5792
- if (!isRecord(v)) throw new DecodeError("not a mapping");
5793
- return v;
5794
- }
5795
- function decodeSpecFields(frontmatter) {
5796
- let value;
5797
- try {
5798
- const docs = parseAllDocuments(frontmatter, { merge: true });
5799
- const doc = Array.isArray(docs) ? docs[0] : void 0;
5800
- if (doc === void 0) {
5801
- value = null;
5802
- } else {
5803
- if (doc.errors.length > 0) return null;
5804
- retagScalars(doc.contents);
5805
- value = doc.toJS({ maxAliasCount: MAX_ALIAS_COUNT2 });
5806
- }
5807
- } catch {
5808
- return null;
5809
- }
5810
- try {
5811
- const root = goStruct(value) ?? {};
5812
- const execution = goStruct(root.execution);
5813
- const tools = goStruct(root.tools);
5814
- const requirements = goStruct(root.requirements);
5815
- const allow = tools === null ? null : goStringList(tools.allow);
5816
- let resources = [];
5817
- if (requirements !== null) {
5818
- const raw = requirements.resources;
5819
- if (raw !== null && raw !== void 0) {
5820
- if (!isArray(raw)) throw new DecodeError("not a list");
5821
- resources = raw.filter((entry) => entry !== null).map((entry) => {
5822
- const r = goStruct(entry) ?? {};
5823
- return { ref: goString(r.ref), kind: goString(r.kind), scope: goString(r.scope) };
5824
- });
5825
- }
5826
- }
5827
- return {
5828
- provider: execution === null ? "" : goString(execution.provider),
5829
- toolsAllowDeclared: allow !== null,
5830
- toolsAllow: allow ?? [],
5831
- resources
5832
- };
5833
- } catch (e) {
5834
- if (e instanceof DecodeError) return null;
5835
- throw e;
5836
- }
5837
- }
5838
- function inspectExonsFrontmatter(source) {
5839
- const fm = extractExonsFrontmatter(source);
5840
- if (fm.parseError !== null) return { ...UNREAD, parseError: fm.parseError };
5841
- if (!fm.hasFrontmatter || fm.frontmatter === "") return { ...UNREAD, specRead: true };
5842
- if (fm.frontmatter.includes(EXONS_OPEN_DELIM)) return { ...UNREAD };
5843
- const fields = decodeSpecFields(fm.frontmatter);
5844
- if (fields === null) return { ...UNREAD };
5845
- return { parseError: null, specRead: true, hasSpec: true, ...fields };
5846
- }
5847
- function validateOrchestratorExons(flow, issues) {
5848
- const orch = flow.orchestrator;
5849
- if (!isRecord(orch)) return;
5850
- const source = issues.stringOf(orch, "exons", "orchestrator");
5851
- if (source === null || source === "") return;
5852
- const field = "orchestrator.exons";
5853
- const report = inspectExonsFrontmatter(source);
5854
- if (report.parseError !== null) {
5855
- issues.error({
5856
- field,
5857
- code: CODE_PARSE_FAILED,
5858
- message: `orchestrator: failed to parse exons spec: ${report.parseError}`
5859
- });
5860
- return;
5861
- }
5862
- if (!report.specRead) return;
5863
- if (!report.hasSpec) {
5864
- issues.error({
5865
- field,
5866
- code: CODE_PARSE_FAILED,
5867
- message: "orchestrator: failed to parse exons spec: the document has no frontmatter spec",
5868
- suggestion: "Start the document with a --- frontmatter block that sets execution.provider"
5869
- });
5870
- return;
5871
- }
5872
- if (report.provider === "") {
5873
- issues.error({
5874
- field,
5875
- code: CODE_NO_PROVIDER,
5876
- message: "orchestrator: exons spec must have execution.provider set"
5877
- });
5878
- }
5879
- if (report.resources.length > 0) {
5880
- const refs = report.resources.map((r) => `${JSON.stringify(r.ref)} (kind ${r.kind})`);
5881
- issues.error({
5882
- field,
5883
- code: CODE_RESOURCES,
5884
- 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("; "),
5885
- suggestion: "Remove requirements.resources, and narrow the agent with tools.mcp_servers and tools.allow"
5886
- });
5887
- }
5888
- warnOrchestratorToolWithheld(flow, orch, report, issues);
5889
- }
5890
- function warnOrchestratorToolWithheld(flow, orch, report, issues) {
5891
- if (!report.toolsAllowDeclared) return;
5892
- const cfg = ORCHESTRATOR_TOOL_ALLOW;
5893
- const exempt = new Set(cfg.lifecycleTools);
5894
- const signals = typeof orch.enable_signals === "boolean" ? orch.enable_signals : cfg.enableSignalsDefault;
5895
- if (signals) for (const t of cfg.signalTools) exempt.add(t);
5896
- const allow = new Set(report.toolsAllow);
5897
- const withheld = (name) => !allow.has(name) && !exempt.has(name);
5898
- const warn = (message) => issues.warn({ field: cfg.warningField, code: CODE_TOOL_WITHHELD, message });
5899
- const named = [];
5900
- if (isArray(orch.tools)) {
5901
- orch.tools.forEach((raw, i) => {
5902
- const name = issues.stringAt(raw, `orchestrator.tools[${i}]`);
5903
- if (name !== null) named.push(name);
5904
- });
5905
- }
5906
- if (named.length > 0) {
5907
- for (const name of named) {
5908
- if (!withheld(name)) continue;
5909
- warn(
5910
- `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.`
5911
- );
5912
- }
5913
- return;
5914
- }
5915
- if (withheld(cfg.askHumanTool)) {
5916
- warn(
5917
- `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.`
5918
- );
5919
- }
5920
- if (isRecord(flow.campaign) && !cfg.campaignTools.some((name) => !withheld(name))) {
5921
- warn(
5922
- `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.`
5923
- );
5924
- }
5925
- }
5926
-
5927
6013
  // src/validators/complianceResidency.ts
5928
6014
  function own2(m, key) {
5929
6015
  if (!isRecord(m) || !Object.prototype.hasOwnProperty.call(m, key)) return void 0;
@@ -5937,7 +6023,7 @@ function sprintf(format, ...args) {
5937
6023
  });
5938
6024
  }
5939
6025
  function literalOrEmpty(s) {
5940
- const t = goTrimSpace2(s);
6026
+ const t = goTrimSpace(s);
5941
6027
  return t.includes(TEMPLATE_ACTION_OPEN) ? "" : t;
5942
6028
  }
5943
6029
  function euOnlyResidency(flow, issues) {
@@ -5952,7 +6038,7 @@ function euOnlyResidency(flow, issues) {
5952
6038
  }
5953
6039
  if (codes.length === 0) return null;
5954
6040
  const eu = new Set(COMPLIANCE_RESIDENCY.euResidencyCodes);
5955
- return codes.every((c) => eu.has(goTrimSpace2(c).toUpperCase())) ? codes : null;
6041
+ return codes.every((c) => eu.has(goTrimSpace(c).toUpperCase())) ? codes : null;
5956
6042
  }
5957
6043
  function aiProvider(executor) {
5958
6044
  const prefix = COMPLIANCE_RESIDENCY.aiProtocol + COMPLIANCE_RESIDENCY.protocolSeparator;
@@ -5968,7 +6054,7 @@ function isRegionLane(provider) {
5968
6054
  }
5969
6055
  function nonBlankString(m, key) {
5970
6056
  const v = own2(m, key);
5971
- return typeof v === "string" && goTrimSpace2(v) !== "";
6057
+ return typeof v === "string" && goTrimSpace(v) !== "";
5972
6058
  }
5973
6059
  function queryEnforcesRegion(query) {
5974
6060
  const v = own2(query, COMPLIANCE_RESIDENCY.enforceRegionParam);
@@ -5980,7 +6066,7 @@ function flowDirectsEndpoint(flow, provider, issues) {
5980
6066
  const inst = own2(own2(flow, "executor_config"), key);
5981
6067
  if (!isRecord(inst)) return false;
5982
6068
  const baseUrl = issues.stringOf(inst, "base_url", `executor_config.${key}`);
5983
- return baseUrl !== null && goTrimSpace2(baseUrl) !== "";
6069
+ return baseUrl !== null && goTrimSpace(baseUrl) !== "";
5984
6070
  }
5985
6071
  function callPinsEndpoint(flow, provider, query, issues) {
5986
6072
  if (nonBlankString(query, COMPLIANCE_RESIDENCY.regionParam)) return true;
@@ -6053,7 +6139,7 @@ function warnOrchestrator(flow, issues, warn) {
6053
6139
  const provider = literalOrEmpty(report.provider);
6054
6140
  if (!isRegionLane(provider)) return;
6055
6141
  const region = issues.stringOf(orch, "region", "orchestrator");
6056
- if (region !== null && goTrimSpace2(region) !== "") return;
6142
+ if (region !== null && goTrimSpace(region) !== "") return;
6057
6143
  if (orch.enforce_region === true) return;
6058
6144
  if (flowDirectsEndpoint(flow, provider, issues)) return;
6059
6145
  warn(COMPLIANCE_RESIDENCY.siteOrchestrator, COMPLIANCE_RESIDENCY.orchestratorField, void 0, provider);
@@ -6219,7 +6305,7 @@ function asFileRef(v) {
6219
6305
  };
6220
6306
  }
6221
6307
  function isTextualMediaType(mediaType) {
6222
- const base = goToLower(goTrimSpace((mediaType.split(";")[0] ?? "").toString()));
6308
+ const base = goToLower(goTrimSpace2((mediaType.split(";")[0] ?? "").toString()));
6223
6309
  if (base.startsWith("text/")) return true;
6224
6310
  if (base.endsWith("+json") || base.endsWith("+xml")) return true;
6225
6311
  return TEXTUAL_MEDIA_BASES.has(base);
@@ -6323,7 +6409,7 @@ var Walker = class _Walker {
6323
6409
  }
6324
6410
  }
6325
6411
  const title = this.s(ex, "title", path);
6326
- if (goTrimSpace(title) === "") {
6412
+ if (goTrimSpace2(title) === "") {
6327
6413
  this.err(
6328
6414
  "example_title_missing",
6329
6415
  `${path}.title`,
@@ -6340,7 +6426,7 @@ var Walker = class _Walker {
6340
6426
  }
6341
6427
  }
6342
6428
  const guidance = this.s(ex, "guidance", path);
6343
- if (goTrimSpace(guidance) === "") {
6429
+ if (goTrimSpace2(guidance) === "") {
6344
6430
  this.warn(
6345
6431
  "example_guidance_missing",
6346
6432
  `${path}.guidance`,
@@ -6767,7 +6853,7 @@ var Walker = class _Walker {
6767
6853
  }
6768
6854
  const rubric = this.s(e, "rubric", path);
6769
6855
  if (rubric !== "") {
6770
- if (goTrimSpace(rubric) === "") {
6856
+ if (goTrimSpace2(rubric) === "") {
6771
6857
  this.err(
6772
6858
  "example_rubric_empty",
6773
6859
  `${path}.rubric`,
@@ -6822,7 +6908,7 @@ var Walker = class _Walker {
6822
6908
  this.err("example_must_not_invalid", `${path}.must_not`, mustNotMsg);
6823
6909
  }
6824
6910
  mustNot.forEach((item, j) => {
6825
- if (goTrimSpace(item) === "" || runeCount(item) > L.maxMustNotRunes) {
6911
+ if (goTrimSpace2(item) === "" || runeCount(item) > L.maxMustNotRunes) {
6826
6912
  this.err("example_must_not_invalid", `${path}.must_not[${j}]`, mustNotMsg);
6827
6913
  }
6828
6914
  });
@@ -7377,6 +7463,7 @@ function validateParsed(flow, opts, sources) {
7377
7463
  const f = flow;
7378
7464
  validateBasicStructure(f, issues);
7379
7465
  validateRetiredKeys(f, issues);
7466
+ validateRemovedFlowKeys(f, issues);
7380
7467
  validateExecutors(f, issues);
7381
7468
  validateQuerySchema(f, issues);
7382
7469
  validateResponseExpectations(f, issues);