@vaudience/aigentflow-flow-validator 0.26.0 → 0.28.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.837.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",
@@ -484,6 +482,7 @@ var aigentflow_spec_default = {
484
482
  "messenger",
485
483
  "mock",
486
484
  "mqtt",
485
+ "nexus",
487
486
  "opencorporates",
488
487
  "openmeteo",
489
488
  "overpass",
@@ -548,12 +547,9 @@ var aigentflow_spec_default = {
548
547
  "step_failed",
549
548
  "mission_started",
550
549
  "mission_completed",
551
- "dfm_suggestion",
552
550
  "timer",
553
551
  "request_turn",
554
552
  "mission_recovered",
555
- "campaign_child_completed",
556
- "campaign_child_failed",
557
553
  "human_response",
558
554
  "human_input"
559
555
  ],
@@ -569,15 +565,6 @@ var aigentflow_spec_default = {
569
565
  "aif_ask_human",
570
566
  "aif_flow_list",
571
567
  "aif_mission_reflect",
572
- "aif_signal_complete",
573
- "aif_signal_need_human",
574
- "aif_signal_delegate",
575
- "aif_signal_need_tool",
576
- "aif_signal_modify_flow",
577
- "aif_campaign_spawn",
578
- "aif_campaign_status",
579
- "aif_campaign_results",
580
- "aif_campaign_cancel",
581
568
  "aif_memory_plan",
582
569
  "aif_memory_record",
583
570
  "aif_memory_recall",
@@ -749,24 +736,87 @@ var aigentflow_spec_default = {
749
736
  orchestratorToolAllow: {
750
737
  warningField: "orchestrator.exons",
751
738
  askHumanTool: "aif_ask_human",
752
- enableSignalsDefault: true,
753
- lifecycleTools: ["aif_mission_complete", "aif_mission_fail", "aif_request_turn"],
754
- signalTools: [
739
+ lifecycleTools: [
740
+ "aif_mission_complete",
741
+ "aif_mission_fail",
742
+ "aif_request_turn"
743
+ ]
744
+ },
745
+ exons: {
746
+ frontmatterDelimiter: "---"
747
+ },
748
+ removedAgentSurfaces: {
749
+ removedSchemes: ["exons", "agent"],
750
+ paramScheme: "ai",
751
+ aiParams: [
752
+ "agentic",
753
+ "deny_tools",
754
+ "discoverable_tools",
755
+ "enable_signals",
756
+ "guardrails",
757
+ "max_tool_calls",
758
+ "max_tool_turns",
759
+ "mcp_server_url",
760
+ "parallel_tool_calls",
761
+ "require_confirmation",
762
+ "timeout_seconds",
763
+ "tool_auth_token",
764
+ "tool_discovery"
765
+ ],
766
+ flowKeys: ["exons_uploads"],
767
+ executorRemovedCode: "executor_removed",
768
+ agenticParamRemovedCode: "agentic_param_removed",
769
+ schemeMessageFormat: "step %q uses %s://, which AIgentFlow no longer runs: its own agent chains were removed",
770
+ paramsMessageFormat: "step %q sets %s on ai://: AIgentFlow's own agent loop (agentic mode) was removed, so these parameters no longer exist",
771
+ flowKeyMessageFormat: "the flow-level key %q belonged to a removed agent surface and is no longer read",
772
+ suggestion: `Rebuild the step: a tool-using agent becomes nexus://agent/run (agent: "@org/name", prompt, capabilities); one tool becomes nexus://capability/<ref>; a plain prompt becomes ai://<provider>/chat or a prompt-only aiv://<org>/<name>. A nexus:// step acts as the person responsible for the run: who started it, who last saved its schedule or webhook trigger, or a flow:// child's parent. What cannot be converted: a run with no person (one started by a service or key that names nobody; share links no longer exist), and a step that needs an inline .exons spec, an arbitrary MCP server URL or signals. For those, start the flow from a person, a schedule or a trigger, or use a routine in Nexus`
773
+ },
774
+ removedSignalSurfaces: {
775
+ signalRemovedCode: "signal_removed",
776
+ flowModificationRemovedCode: "flow_modification_removed",
777
+ removedKeyIgnoredCode: "removed_key_ignored",
778
+ tools: [
755
779
  "aif_signal_complete",
756
780
  "aif_signal_need_human",
757
781
  "aif_signal_delegate",
758
782
  "aif_signal_need_tool",
759
783
  "aif_signal_modify_flow"
760
784
  ],
761
- campaignTools: [
762
- "aif_campaign_cancel",
763
- "aif_campaign_results",
764
- "aif_campaign_spawn",
765
- "aif_campaign_status"
766
- ]
785
+ triggers: [
786
+ "dfm_suggestion"
787
+ ],
788
+ stepKey: "enable_signals",
789
+ stepKeyScheme: "aiv",
790
+ flowModificationStepKey: "flow_modification_allowed",
791
+ stepSkipSchemes: [
792
+ "flow"
793
+ ],
794
+ enableSignalsMessage: "the orchestrator sets enable_signals: true: AIgentFlow's agent signals were removed in 2.834.0, so the tools it asks for no longer exist",
795
+ toolNamesMessageFormat: "%s names %s: AIgentFlow's agent signal tools were removed in 2.834.0",
796
+ stepKeyMessageFormat: "step %q sets enable_signals: AIgentFlow's agent signals were removed in 2.834.0, so the parameter no longer exists",
797
+ triggersMessage: "orchestrator.triggers names dfm_suggestion: dynamic flow modification was removed in 2.834.0, so nothing raises that trigger",
798
+ flowModificationStepMessageFormat: "step %q sets flow_modification_allowed: dynamic flow modification was removed in 2.834.0",
799
+ ignoredKeyMessageFormat: "%s is ignored: it configured AIgentFlow's agent signals / dynamic flow modification, removed in 2.834.0. AIgentFlow may have written this key into the flow itself; the flow runs as if it were absent",
800
+ ignoredKeySuggestion: "Remove the key; nothing reads it",
801
+ suggestion: "Signals and dynamic flow modification were removed in 2.834.0. aif_signal_need_human: add an async://human/<operation> step where the person must answer (inside a nexus://agent/run step, the atlas approval wait asks the person). aif_signal_delegate: run the other work as a flow:// step, or route with next: conditions. aif_signal_complete: an orchestrator ends the mission with aif_mission_complete. aif_signal_need_tool: no replacement; give the agent the capability in atlas. aif_signal_modify_flow, dfm_suggestion and flow_modification_allowed: no replacement; declare every step the flow may run and choose between them with next: conditions"
767
802
  },
768
- exons: {
769
- frontmatterDelimiter: "---"
803
+ removedCampaignSurfaces: {
804
+ code: "campaign_removed",
805
+ flowKey: "campaign",
806
+ tools: [
807
+ "aif_campaign_spawn",
808
+ "aif_campaign_status",
809
+ "aif_campaign_results",
810
+ "aif_campaign_cancel"
811
+ ],
812
+ triggers: [
813
+ "campaign_child_completed",
814
+ "campaign_child_failed"
815
+ ],
816
+ blockMessage: "the flow declares a campaign: block: campaigns were removed in 2.836.0, so nothing would spawn or track its child missions",
817
+ toolNamesMessageFormat: "%s names %s: AIgentFlow's campaign tools were removed in 2.836.0",
818
+ triggersMessageFormat: "orchestrator.triggers names %s: campaigns were removed in 2.836.0, so no child mission raises that trigger",
819
+ suggestion: "Campaigns were removed in 2.836.0. Fan the work out inside one flow instead: a for_each step whose executor is flow://stored/<flow_id> runs the child flow once per item and hands the next step every result (.data.<step>, in item order), so a coordinator -> workers -> synthesiser campaign becomes a plan step -> a for_each over flow:// -> a synthesis step (a fixed set of different workers is next: parallel: with a rendezvous). When each worker is an agent, make the worker a nexus://agent/run step (the for_each step itself, or a step of the child flow): it runs as the person responsible for the run, and a flow:// child inherits its parent's. Delete the campaign: block, the aif_campaign_* tool names and the campaign_child_* triggers"
770
820
  },
771
821
  executorConfigEnvScopes: {
772
822
  referencePrefix: "${",
@@ -990,19 +1040,20 @@ var aigentflow_spec_default = {
990
1040
  ]
991
1041
  },
992
1042
  atlasExecutor: {
993
- scheme: "atlas",
1043
+ scheme: "nexus",
1044
+ aliases: ["atlas"],
994
1045
  providerAgent: "agent",
995
1046
  providerCapability: "capability",
996
1047
  agentResource: "run",
997
1048
  capabilityRefPattern: "^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$",
998
1049
  operationUnknown: {
999
1050
  code: "atlas_operation_unknown",
1000
- messageFormat: "step %s uses %s, which is not an Atlas operation. The providers are agent (resource run) and capability (resource: a capability reference).",
1001
- suggestion: "Use atlas://agent/run or atlas://capability/<capability-ref>."
1051
+ messageFormat: "step %s uses %s, which is not a Nexus operation. The providers are agent (resource run) and capability (resource: a capability reference).",
1052
+ suggestion: "Use nexus://agent/run or nexus://capability/<capability-ref>."
1002
1053
  },
1003
1054
  agentMissing: {
1004
1055
  code: "atlas_agent_missing",
1005
- messageFormat: "step %s runs an Atlas agent but query.agent is missing, empty or not a string.",
1056
+ messageFormat: "step %s runs a Nexus agent but query.agent is missing, empty or not a string.",
1006
1057
  suggestion: "Set query.agent to the agent to run, written out as a literal such as @acme/reporter."
1007
1058
  },
1008
1059
  agentNotLiteral: {
@@ -1017,7 +1068,7 @@ var aigentflow_spec_default = {
1017
1068
  },
1018
1069
  promptMissing: {
1019
1070
  code: "atlas_prompt_missing",
1020
- messageFormat: "step %s runs an Atlas agent with neither query.prompt nor query.prompt_template.",
1071
+ messageFormat: "step %s runs a Nexus agent with neither query.prompt nor query.prompt_template.",
1021
1072
  suggestion: "Set exactly one of query.prompt or query.prompt_template."
1022
1073
  },
1023
1074
  promptBoth: {
@@ -1027,7 +1078,7 @@ var aigentflow_spec_default = {
1027
1078
  },
1028
1079
  capabilityRefInvalid: {
1029
1080
  code: "atlas_capability_ref_invalid",
1030
- messageFormat: "step %s uses atlas://capability/%s, which is not a valid capability reference.",
1081
+ messageFormat: "step %s uses nexus://capability/%s, which is not a valid capability reference.",
1031
1082
  suggestion: "A capability reference starts with a letter or digit and continues with letters, digits, _ and - up to 128 characters in all."
1032
1083
  },
1033
1084
  modelNotLiteral: {
@@ -1554,20 +1605,6 @@ var aigentflow_spec_default = {
1554
1605
  BillingConfig: {
1555
1606
  max_credits: "scalar"
1556
1607
  },
1557
- CampaignChildFlowDef: {
1558
- alias: "scalar",
1559
- flow_id: "scalar",
1560
- flow_name: "scalar"
1561
- },
1562
- CampaignConfig: {
1563
- child_flows: "list<CampaignChildFlowDef>",
1564
- max_concurrent: "scalar",
1565
- max_credits_per_child: "scalar",
1566
- max_depth: "scalar",
1567
- max_total_children: "scalar",
1568
- on_children_complete: "scalar",
1569
- shared_memory: "scalar"
1570
- },
1571
1608
  Checkpoint: {
1572
1609
  exact: "map<any>",
1573
1610
  fields: "map<FieldMatcher>",
@@ -1701,7 +1738,7 @@ var aigentflow_spec_default = {
1701
1738
  Flow: {
1702
1739
  aigentflow_version: "scalar",
1703
1740
  billing: "BillingConfig",
1704
- campaign: "CampaignConfig",
1741
+ campaign: "any",
1705
1742
  checkpoint: "CheckpointConfig",
1706
1743
  classification: "FlowClassification",
1707
1744
  compliance: "FlowCompliance",
@@ -2011,15 +2048,64 @@ var KNOWN_KEYS = aigentflow_spec_default.knownKeys;
2011
2048
  var ORCHESTRATOR_TOOL_ALLOW = {
2012
2049
  warningField: aigentflow_spec_default.orchestratorToolAllow.warningField,
2013
2050
  askHumanTool: aigentflow_spec_default.orchestratorToolAllow.askHumanTool,
2014
- enableSignalsDefault: aigentflow_spec_default.orchestratorToolAllow.enableSignalsDefault,
2015
- lifecycleTools: aigentflow_spec_default.orchestratorToolAllow.lifecycleTools,
2016
- signalTools: aigentflow_spec_default.orchestratorToolAllow.signalTools,
2017
- campaignTools: aigentflow_spec_default.orchestratorToolAllow.campaignTools
2051
+ lifecycleTools: aigentflow_spec_default.orchestratorToolAllow.lifecycleTools
2018
2052
  };
2019
2053
  var EXONS = {
2020
2054
  /** The YAML frontmatter delimiter line. */
2021
2055
  frontmatterDelimiter: aigentflow_spec_default.exons.frontmatterDelimiter
2022
2056
  };
2057
+ var removed = aigentflow_spec_default.removedAgentSurfaces;
2058
+ var REMOVED_AGENT_SURFACES = {
2059
+ schemes: new Set(removed.removedSchemes),
2060
+ paramScheme: removed.paramScheme,
2061
+ aiParams: new Set(removed.aiParams),
2062
+ flowKeys: removed.flowKeys,
2063
+ executorRemovedCode: removed.executorRemovedCode,
2064
+ agenticParamRemovedCode: removed.agenticParamRemovedCode,
2065
+ schemeMessageFormat: removed.schemeMessageFormat,
2066
+ paramsMessageFormat: removed.paramsMessageFormat,
2067
+ flowKeyMessageFormat: removed.flowKeyMessageFormat,
2068
+ suggestion: removed.suggestion
2069
+ };
2070
+ var signal = aigentflow_spec_default.removedSignalSurfaces;
2071
+ var REMOVED_SIGNAL_SURFACES = {
2072
+ signalRemovedCode: signal.signalRemovedCode,
2073
+ flowModificationRemovedCode: signal.flowModificationRemovedCode,
2074
+ removedKeyIgnoredCode: signal.removedKeyIgnoredCode,
2075
+ tools: signal.tools,
2076
+ triggers: signal.triggers,
2077
+ stepKey: signal.stepKey,
2078
+ stepKeyScheme: signal.stepKeyScheme,
2079
+ flowModificationStepKey: signal.flowModificationStepKey,
2080
+ stepSkipSchemes: new Set(signal.stepSkipSchemes),
2081
+ enableSignalsMessage: signal.enableSignalsMessage,
2082
+ toolNamesMessageFormat: signal.toolNamesMessageFormat,
2083
+ stepKeyMessageFormat: signal.stepKeyMessageFormat,
2084
+ triggersMessage: signal.triggersMessage,
2085
+ flowModificationStepMessageFormat: signal.flowModificationStepMessageFormat,
2086
+ ignoredKeyMessageFormat: signal.ignoredKeyMessageFormat,
2087
+ ignoredKeySuggestion: signal.ignoredKeySuggestion,
2088
+ suggestion: signal.suggestion
2089
+ };
2090
+ var campaign = aigentflow_spec_default.removedCampaignSurfaces;
2091
+ var REMOVED_CAMPAIGN_SURFACES = {
2092
+ code: campaign.code,
2093
+ flowKey: campaign.flowKey,
2094
+ tools: campaign.tools,
2095
+ triggers: campaign.triggers,
2096
+ blockMessage: campaign.blockMessage,
2097
+ toolNamesMessageFormat: campaign.toolNamesMessageFormat,
2098
+ triggersMessageFormat: campaign.triggersMessageFormat,
2099
+ suggestion: campaign.suggestion
2100
+ };
2101
+ var REMOVED_ORCHESTRATOR_TOOLS = /* @__PURE__ */ new Set([
2102
+ ...signal.tools,
2103
+ ...campaign.tools
2104
+ ]);
2105
+ var REMOVED_ORCHESTRATOR_TRIGGERS = /* @__PURE__ */ new Set([
2106
+ ...signal.triggers,
2107
+ ...campaign.triggers
2108
+ ]);
2023
2109
  var EXECUTOR_CONFIG_ENV_SCOPES = {
2024
2110
  referencePrefix: aigentflow_spec_default.executorConfigEnvScopes.referencePrefix,
2025
2111
  referenceSuffix: aigentflow_spec_default.executorConfigEnvScopes.referenceSuffix,
@@ -2112,7 +2198,8 @@ var FUNCTION_OPERATIONS = {
2112
2198
  };
2113
2199
  var atlas = aigentflow_spec_default.atlasExecutor;
2114
2200
  var ATLAS_EXECUTOR = {
2115
- scheme: atlas.scheme,
2201
+ /** The canonical scheme (nexus) and its aliases (atlas): one set of rules. */
2202
+ schemes: /* @__PURE__ */ new Set([atlas.scheme, ...atlas.aliases]),
2116
2203
  providerAgent: atlas.providerAgent,
2117
2204
  providerCapability: atlas.providerCapability,
2118
2205
  agentResource: atlas.agentResource,
@@ -2141,6 +2228,220 @@ var RESERVED_INPUT_NAMES = {
2141
2228
  queryFieldFormat: aigentflow_spec_default.reservedInputNames.queryFieldFormat,
2142
2229
  schemaFieldFormat: aigentflow_spec_default.reservedInputNames.schemaFieldFormat
2143
2230
  };
2231
+ var CODE_PARSE_FAILED = "orchestrator_exons_parse_failed";
2232
+ var CODE_NO_PROVIDER = "orchestrator_exons_no_provider";
2233
+ var CODE_RESOURCES = "exons_resources_unhonoured";
2234
+ var CODE_TOOL_WITHHELD = "orchestrator_tool_withheld";
2235
+ var EXONS_OPEN_DELIM = "{~";
2236
+ var EXONS_LEGACY_CONFIG_OPEN = "{~exons.config~}";
2237
+ var UTF8_BOM = "\uFEFF";
2238
+ var MAX_ALIAS_COUNT2 = 100;
2239
+ var UNREAD = {
2240
+ parseError: null,
2241
+ specRead: false,
2242
+ hasSpec: false,
2243
+ provider: "",
2244
+ toolsAllowDeclared: false,
2245
+ toolsAllow: [],
2246
+ resources: []
2247
+ };
2248
+ var GO_SPACE = "\\t\\n\\v\\f\\r \\u0085\\u00a0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000";
2249
+ var GO_TRIM_SPACE = new RegExp(`^[${GO_SPACE}]+|[${GO_SPACE}]+$`, "g");
2250
+ function goTrimSpace(s) {
2251
+ return s.replace(GO_TRIM_SPACE, "");
2252
+ }
2253
+ function extractExonsFrontmatter(source) {
2254
+ const delim = EXONS.frontmatterDelimiter;
2255
+ let start = source.startsWith(UTF8_BOM) ? source.slice(UTF8_BOM.length) : source;
2256
+ start = start.replace(/^[ \t]+/, "");
2257
+ if (start.startsWith(EXONS_LEGACY_CONFIG_OPEN)) {
2258
+ return {
2259
+ frontmatter: "",
2260
+ hasFrontmatter: false,
2261
+ parseError: "legacy JSON config block detected; use YAML frontmatter"
2262
+ };
2263
+ }
2264
+ if (!start.startsWith(delim)) return { frontmatter: "", hasFrontmatter: false, parseError: null };
2265
+ let rest = start.slice(delim.length);
2266
+ if (rest.startsWith("\n")) rest = rest.slice(1);
2267
+ else if (rest.startsWith("\r\n")) rest = rest.slice(2);
2268
+ else return { frontmatter: "", hasFrontmatter: false, parseError: null };
2269
+ let pos = 0;
2270
+ while (pos <= rest.length) {
2271
+ const line = rest.slice(pos);
2272
+ if (line.startsWith(delim)) {
2273
+ const after = line.slice(delim.length);
2274
+ if (after === "" || after[0] === "\n" || after[0] === "\r") {
2275
+ return {
2276
+ frontmatter: goTrimSpace(rest.slice(0, pos)),
2277
+ hasFrontmatter: true,
2278
+ parseError: null
2279
+ };
2280
+ }
2281
+ }
2282
+ const nl = line.indexOf("\n");
2283
+ if (nl < 0) break;
2284
+ pos += nl + 1;
2285
+ }
2286
+ return {
2287
+ frontmatter: "",
2288
+ hasFrontmatter: true,
2289
+ parseError: "YAML frontmatter not properly closed"
2290
+ };
2291
+ }
2292
+ function retagScalars(node) {
2293
+ if (yaml.isMap(node)) {
2294
+ for (const pair of node.items) {
2295
+ retagScalars(pair.key);
2296
+ retagScalars(pair.value);
2297
+ }
2298
+ } else if (yaml.isSeq(node)) {
2299
+ for (const item of node.items) retagScalars(item);
2300
+ } else if (yaml.isScalar(node) && (typeof node.value === "number" || typeof node.value === "boolean") && typeof node.source === "string") {
2301
+ node.value = node.source;
2302
+ }
2303
+ }
2304
+ var DecodeError = class extends Error {
2305
+ };
2306
+ function goString(v) {
2307
+ if (v === null || v === void 0) return "";
2308
+ if (isString(v)) return v;
2309
+ throw new DecodeError("not a string");
2310
+ }
2311
+ function goStringList(v) {
2312
+ if (v === null || v === void 0) return null;
2313
+ if (!isArray(v)) throw new DecodeError("not a list");
2314
+ return v.filter((item) => item !== null).map(goString);
2315
+ }
2316
+ function goStruct(v) {
2317
+ if (v === null || v === void 0) return null;
2318
+ if (!isRecord(v)) throw new DecodeError("not a mapping");
2319
+ return v;
2320
+ }
2321
+ function decodeSpecFields(frontmatter) {
2322
+ let value;
2323
+ try {
2324
+ const docs = yaml.parseAllDocuments(frontmatter, { merge: true });
2325
+ const doc = Array.isArray(docs) ? docs[0] : void 0;
2326
+ if (doc === void 0) {
2327
+ value = null;
2328
+ } else {
2329
+ if (doc.errors.length > 0) return null;
2330
+ retagScalars(doc.contents);
2331
+ value = doc.toJS({ maxAliasCount: MAX_ALIAS_COUNT2 });
2332
+ }
2333
+ } catch {
2334
+ return null;
2335
+ }
2336
+ try {
2337
+ const root = goStruct(value) ?? {};
2338
+ const execution = goStruct(root.execution);
2339
+ const tools = goStruct(root.tools);
2340
+ const requirements = goStruct(root.requirements);
2341
+ const allow = tools === null ? null : goStringList(tools.allow);
2342
+ let resources = [];
2343
+ if (requirements !== null) {
2344
+ const raw = requirements.resources;
2345
+ if (raw !== null && raw !== void 0) {
2346
+ if (!isArray(raw)) throw new DecodeError("not a list");
2347
+ resources = raw.filter((entry) => entry !== null).map((entry) => {
2348
+ const r = goStruct(entry) ?? {};
2349
+ return { ref: goString(r.ref), kind: goString(r.kind), scope: goString(r.scope) };
2350
+ });
2351
+ }
2352
+ }
2353
+ return {
2354
+ provider: execution === null ? "" : goString(execution.provider),
2355
+ toolsAllowDeclared: allow !== null,
2356
+ toolsAllow: allow ?? [],
2357
+ resources
2358
+ };
2359
+ } catch (e) {
2360
+ if (e instanceof DecodeError) return null;
2361
+ throw e;
2362
+ }
2363
+ }
2364
+ function inspectExonsFrontmatter(source) {
2365
+ const fm = extractExonsFrontmatter(source);
2366
+ if (fm.parseError !== null) return { ...UNREAD, parseError: fm.parseError };
2367
+ if (!fm.hasFrontmatter || fm.frontmatter === "") return { ...UNREAD, specRead: true };
2368
+ if (fm.frontmatter.includes(EXONS_OPEN_DELIM)) return { ...UNREAD };
2369
+ const fields = decodeSpecFields(fm.frontmatter);
2370
+ if (fields === null) return { ...UNREAD };
2371
+ return { parseError: null, specRead: true, hasSpec: true, ...fields };
2372
+ }
2373
+ function validateOrchestratorExons(flow, issues) {
2374
+ const orch = flow.orchestrator;
2375
+ if (!isRecord(orch)) return;
2376
+ const source = issues.stringOf(orch, "exons", "orchestrator");
2377
+ if (source === null || source === "") return;
2378
+ const field = "orchestrator.exons";
2379
+ const report = inspectExonsFrontmatter(source);
2380
+ if (report.parseError !== null) {
2381
+ issues.error({
2382
+ field,
2383
+ code: CODE_PARSE_FAILED,
2384
+ message: `orchestrator: failed to parse exons spec: ${report.parseError}`
2385
+ });
2386
+ return;
2387
+ }
2388
+ if (!report.specRead) return;
2389
+ if (!report.hasSpec) {
2390
+ issues.error({
2391
+ field,
2392
+ code: CODE_PARSE_FAILED,
2393
+ message: "orchestrator: failed to parse exons spec: the document has no frontmatter spec",
2394
+ suggestion: "Start the document with a --- frontmatter block that sets execution.provider"
2395
+ });
2396
+ return;
2397
+ }
2398
+ if (report.provider === "") {
2399
+ issues.error({
2400
+ field,
2401
+ code: CODE_NO_PROVIDER,
2402
+ message: "orchestrator: exons spec must have execution.provider set"
2403
+ });
2404
+ }
2405
+ if (report.resources.length > 0) {
2406
+ const refs = report.resources.map((r) => `${JSON.stringify(r.ref)} (kind ${r.kind})`);
2407
+ issues.error({
2408
+ field,
2409
+ code: CODE_RESOURCES,
2410
+ 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("; "),
2411
+ suggestion: "Remove requirements.resources, and narrow the agent with tools.mcp_servers and tools.allow"
2412
+ });
2413
+ }
2414
+ warnOrchestratorToolWithheld(orch, report, issues);
2415
+ }
2416
+ function warnOrchestratorToolWithheld(orch, report, issues) {
2417
+ if (!report.toolsAllowDeclared) return;
2418
+ const cfg = ORCHESTRATOR_TOOL_ALLOW;
2419
+ const exempt = new Set(cfg.lifecycleTools);
2420
+ const allow = new Set(report.toolsAllow);
2421
+ const withheld = (name) => !allow.has(name) && !exempt.has(name) && !REMOVED_ORCHESTRATOR_TOOLS.has(name);
2422
+ const warn = (message) => issues.warn({ field: cfg.warningField, code: CODE_TOOL_WITHHELD, message });
2423
+ const named = [];
2424
+ if (isArray(orch.tools)) {
2425
+ orch.tools.forEach((raw, i) => {
2426
+ const name = issues.stringAt(raw, `orchestrator.tools[${i}]`);
2427
+ if (name !== null) named.push(name);
2428
+ });
2429
+ }
2430
+ if (named.length > 0) {
2431
+ for (const name of named) {
2432
+ if (!withheld(name)) continue;
2433
+ warn(
2434
+ `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.`
2435
+ );
2436
+ }
2437
+ return;
2438
+ }
2439
+ if (withheld(cfg.askHumanTool)) {
2440
+ warn(
2441
+ `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.`
2442
+ );
2443
+ }
2444
+ }
2144
2445
 
2145
2446
  // src/validators/executors.ts
2146
2447
  var SCHEME_SEPARATOR = "://";
@@ -2204,11 +2505,61 @@ function checkExecutor(executor, field, stepID, issues) {
2204
2505
  }
2205
2506
  checkFunctionOperation(executor, field, stepID, templated, issues);
2206
2507
  }
2508
+ function removedExecutorScheme(executor) {
2509
+ if (executor.includes(TEMPLATE_ACTION_OPEN)) return "";
2510
+ const trimmed = goTrimSpace(executor);
2511
+ const at = trimmed.indexOf(SCHEME_SEPARATOR);
2512
+ return at < 0 ? "" : trimmed.slice(0, at).toLowerCase();
2513
+ }
2514
+ function checkRemovedSurfaces(executor, query, field, stepID, issues) {
2515
+ const cfg = REMOVED_AGENT_SURFACES;
2516
+ const quoted = JSON.stringify(stepID);
2517
+ const scheme = removedExecutorScheme(executor);
2518
+ if (cfg.schemes.has(scheme)) {
2519
+ issues.error({
2520
+ field,
2521
+ message: fillRemovedMessage(cfg.schemeMessageFormat, quoted, scheme),
2522
+ code: cfg.executorRemovedCode,
2523
+ stepId: stepID,
2524
+ suggestion: cfg.suggestion
2525
+ });
2526
+ return true;
2527
+ }
2528
+ if (scheme !== cfg.paramScheme || !isRecord(query)) return false;
2529
+ const found = Object.keys(query).filter((key) => cfg.aiParams.has(key)).sort();
2530
+ if (found.length > 0) {
2531
+ issues.error({
2532
+ field,
2533
+ message: fillRemovedMessage(cfg.paramsMessageFormat, quoted, found.join(", ")),
2534
+ code: cfg.agenticParamRemovedCode,
2535
+ stepId: stepID,
2536
+ suggestion: cfg.suggestion
2537
+ });
2538
+ }
2539
+ return false;
2540
+ }
2541
+ function fillRemovedMessage(format, ...args) {
2542
+ let i = 0;
2543
+ return format.replace(/%[qs]/g, () => args[i++] ?? "");
2544
+ }
2545
+ function validateRemovedFlowKeys(flow, issues) {
2546
+ const cfg = REMOVED_AGENT_SURFACES;
2547
+ for (const key of cfg.flowKeys) {
2548
+ if (Object.prototype.hasOwnProperty.call(flow, key)) {
2549
+ issues.error({
2550
+ field: key,
2551
+ message: fillRemovedMessage(cfg.flowKeyMessageFormat, JSON.stringify(key)),
2552
+ code: cfg.executorRemovedCode,
2553
+ suggestion: cfg.suggestion
2554
+ });
2555
+ }
2556
+ }
2557
+ }
2207
2558
  function checkAtlasStep(executor, query, stepField, stepID, issues) {
2208
2559
  if (executor.includes(TEMPLATE_ACTION_OPEN)) return;
2209
- const prefix = `${ATLAS_EXECUTOR.scheme}://`;
2210
- if (!executor.startsWith(prefix)) return;
2211
- const rest = executor.slice(prefix.length);
2560
+ const sep = executor.indexOf(SCHEME_SEPARATOR);
2561
+ if (sep < 0 || !ATLAS_EXECUTOR.schemes.has(executor.slice(0, sep))) return;
2562
+ const rest = executor.slice(sep + SCHEME_SEPARATOR.length);
2212
2563
  const slash = rest.indexOf("/");
2213
2564
  const provider = slash < 0 ? rest : rest.slice(0, slash);
2214
2565
  const resource = slash < 0 ? "" : rest.slice(slash + 1);
@@ -2284,7 +2635,9 @@ function validateExecutors(flow, issues) {
2284
2635
  const step = rawStep;
2285
2636
  const executor = issues.stringOf(step, "executor", `steps.${stepID}`);
2286
2637
  if (executor !== null && executor !== "") {
2287
- checkExecutor(executor, `steps.${stepID}.executor`, stepID, issues);
2638
+ if (!checkRemovedSurfaces(executor, step.query, `steps.${stepID}`, stepID, issues)) {
2639
+ checkExecutor(executor, `steps.${stepID}.executor`, stepID, issues);
2640
+ }
2288
2641
  checkAtlasStep(executor, step.query, `steps.${stepID}`, stepID, issues);
2289
2642
  }
2290
2643
  const loop = step.loop;
@@ -2296,12 +2649,14 @@ function validateExecutors(flow, issues) {
2296
2649
  const subID = issues.stringOf(subStep, "id", subPath) ?? String(i);
2297
2650
  const subExecutor = issues.stringOf(subStep, "executor", subPath);
2298
2651
  if (subExecutor !== null && subExecutor !== "") {
2299
- checkExecutor(
2300
- subExecutor,
2301
- `steps.${stepID}.loop.steps[${i}].executor`,
2302
- `${stepID}.${subID}`,
2303
- issues
2304
- );
2652
+ if (!checkRemovedSurfaces(subExecutor, subStep.query, subPath, `${stepID}.${subID}`, issues)) {
2653
+ checkExecutor(
2654
+ subExecutor,
2655
+ `steps.${stepID}.loop.steps[${i}].executor`,
2656
+ `${stepID}.${subID}`,
2657
+ issues
2658
+ );
2659
+ }
2305
2660
  checkAtlasStep(subExecutor, subStep.query, subPath, `${stepID}.${subID}`, issues);
2306
2661
  }
2307
2662
  });
@@ -3417,49 +3772,6 @@ function flowHasOrchestratorYieldEdge(flow) {
3417
3772
  }
3418
3773
  return false;
3419
3774
  }
3420
- var GO_INT64_FLOAT_LIMIT = 2 ** 63;
3421
- function decodeGoInt(v) {
3422
- if (typeof v !== "number" || Number.isNaN(v)) return null;
3423
- if (v >= GO_INT64_FLOAT_LIMIT) return null;
3424
- if (v === -Infinity) return -GO_INT64_FLOAT_LIMIT;
3425
- return Math.trunc(v);
3426
- }
3427
- function validateCampaignChildFlows(campaign, issues) {
3428
- const field = "campaign.child_flows";
3429
- const raw = campaign.child_flows;
3430
- if (raw !== void 0 && raw !== null && !Array.isArray(raw)) {
3431
- issues.error({ field, message: "campaign.child_flows must be a list", code: "invalid_type" });
3432
- return;
3433
- }
3434
- const entries = (Array.isArray(raw) ? raw : []).map((entry, i) => ({ entry, i })).filter(({ entry }) => entry !== null);
3435
- if (entries.length === 0) {
3436
- issues.error({
3437
- field,
3438
- message: "campaign requires at least one entry in child_flows",
3439
- code: "campaign_no_child_flows"
3440
- });
3441
- return;
3442
- }
3443
- for (const { entry, i } of entries) {
3444
- if (!isRecord(entry)) {
3445
- issues.error({
3446
- field: `${field}[${i}]`,
3447
- message: "each campaign.child_flows entry must be a mapping",
3448
- code: "invalid_type"
3449
- });
3450
- continue;
3451
- }
3452
- const id = issues.stringOf(entry, "flow_id", `${field}[${i}]`);
3453
- const name = issues.stringOf(entry, "flow_name", `${field}[${i}]`);
3454
- if (id !== null && id !== "" || name !== null && name !== "") continue;
3455
- issues.error({
3456
- field: `${field}[${i}]`,
3457
- message: `campaign.child_flows[${i}] needs a flow_id or a flow_name`,
3458
- code: "campaign_child_flow_no_id",
3459
- suggestion: "Name the child flow with flow_name, or pin it with flow_id"
3460
- });
3461
- }
3462
- }
3463
3775
  function validateOrchestratorCampaign(flow, issues, opts) {
3464
3776
  const hasOrchestrator = isRecord(flow.orchestrator);
3465
3777
  if (hasOrchestrator) {
@@ -3515,6 +3827,7 @@ function validateOrchestratorCampaign(flow, issues, opts) {
3515
3827
  const trigger = rawTrigger;
3516
3828
  const base = `orchestrator.triggers[${i}]`;
3517
3829
  const type = issues.stringOf(trigger, "type", base);
3830
+ if (type !== null && REMOVED_ORCHESTRATOR_TRIGGERS.has(type)) return;
3518
3831
  if (type === null || !ORCHESTRATOR_TRIGGERS.has(type)) {
3519
3832
  issues.error({
3520
3833
  field: `${base}.type`,
@@ -3545,66 +3858,27 @@ function validateOrchestratorCampaign(flow, issues, opts) {
3545
3858
  if (Array.isArray(orch.tools)) {
3546
3859
  orch.tools.forEach((raw, i) => {
3547
3860
  const tool = issues.stringAt(raw, `orchestrator.tools[${i}]`);
3548
- if (tool === null || ORCHESTRATOR_TOOLS.has(tool)) return;
3861
+ if (tool === null || ORCHESTRATOR_TOOLS.has(tool) || REMOVED_ORCHESTRATOR_TOOLS.has(tool)) {
3862
+ return;
3863
+ }
3549
3864
  const finding = {
3550
3865
  field: `orchestrator.tools[${i}]`,
3551
3866
  message: `Unrecognised orchestrator tool name '${tool}'`,
3552
3867
  code: "orchestrator_tool_unknown"
3553
3868
  };
3554
3869
  if (opts.strictRegistries) {
3555
- issues.error(finding);
3556
- } else {
3557
- issues.warn(finding);
3558
- }
3559
- });
3560
- }
3561
- if (orch.agentic === false) {
3562
- issues.warn({
3563
- field: "orchestrator.agentic",
3564
- message: "orchestrator is most useful with agentic: true",
3565
- code: "orchestrator_not_agentic"
3566
- });
3567
- }
3568
- }
3569
- if (flow.campaign !== void 0 && flow.campaign !== null) {
3570
- if (!hasOrchestrator) {
3571
- issues.error({
3572
- field: "campaign",
3573
- message: "campaign requires an orchestrator block",
3574
- code: "campaign_requires_orchestrator"
3575
- });
3576
- }
3577
- const campaign = flow.campaign;
3578
- if (isRecord(campaign)) {
3579
- const cap = campaign.max_credits_per_child;
3580
- if (cap !== void 0 && cap !== null) {
3581
- const decoded = decodeGoInt(cap);
3582
- if (decoded === null) {
3583
- issues.error({
3584
- field: "campaign.max_credits_per_child",
3585
- message: `campaign.max_credits_per_child must be a number of credits, got '${String(cap)}'`,
3586
- code: "invalid_type"
3587
- });
3588
- } else if (decoded < 0) {
3589
- issues.error({
3590
- field: "campaign.max_credits_per_child",
3591
- message: "campaign: max_credits_per_child must be >= 0",
3592
- code: "campaign_invalid_max_credits_per_child"
3593
- });
3870
+ issues.error(finding);
3871
+ } else {
3872
+ issues.warn(finding);
3594
3873
  }
3595
- }
3596
- validateCampaignChildFlows(campaign, issues);
3874
+ });
3597
3875
  }
3598
- const target = isRecord(campaign) ? issues.stringOf(campaign, "on_children_complete", "campaign") : null;
3599
- if (target !== null && target !== "") {
3600
- const steps = isRecord(flow.steps) ? flow.steps : {};
3601
- if (!(target in steps)) {
3602
- issues.error({
3603
- field: "campaign.on_children_complete",
3604
- message: `campaign.on_children_complete references unknown step '${target}'`,
3605
- code: "campaign_handoff_step_unknown"
3606
- });
3607
- }
3876
+ if (orch.agentic === false) {
3877
+ issues.warn({
3878
+ field: "orchestrator.agentic",
3879
+ message: "orchestrator is most useful with agentic: true",
3880
+ code: "orchestrator_not_agentic"
3881
+ });
3608
3882
  }
3609
3883
  }
3610
3884
  }
@@ -4732,10 +5006,6 @@ var CODE_UNKNOWN_YAML_KEY = "unknown_yaml_key";
4732
5006
  var KEY_BUDGET = "budget";
4733
5007
  var KEY_MAX_RETRIES = "max_retries";
4734
5008
  var REMOVED_IN = "v2.721.0";
4735
- var KEY_BUDGET_MAX_PER_CHILD = "budget_max_per_child";
4736
- var BUDGET_MAX_PER_CHILD_REMOVED_IN = "v2.728.0";
4737
- var BUDGET_MAX_PER_CHILD_MESSAGE = `\`campaign.budget_max_per_child\` (USD) was removed from the grammar in AIgentFlow ${BUDGET_MAX_PER_CHILD_REMOVED_IN} because it capped nothing, and AIgentFlow refuses to save a flow that declares it.`;
4738
- var BUDGET_MAX_PER_CHILD_SUGGESTION = "Use `campaign.max_credits_per_child: N` (a whole number of credits) \u2014 it is checked before each agentic turn of a child, and a spawn may lower it with `max_credits`.";
4739
5009
  var BUDGET_MESSAGE = `The flow-level \`budget:\` key was removed from the grammar in AIgentFlow ${REMOVED_IN} because nothing read it (it limited no spend), and AIgentFlow refuses to save a flow that declares it.`;
4740
5010
  var BUDGET_SUGGESTION = "Delete it. To cap spend use `billing: { max_credits: N }` \u2014 it caps the credit reservation and refuses further agentic turns (agentic ai://, exons://, the orchestrator); plain chat steps and flow:// sub-flows are not checked against it.";
4741
5011
  function maxRetriesMessage(owner) {
@@ -4763,15 +5033,6 @@ function validateRetiredKeys(flow, issues) {
4763
5033
  suggestion: MAX_RETRIES_SUGGESTION
4764
5034
  });
4765
5035
  }
4766
- const campaign = root.campaign;
4767
- if (isRecord(campaign) && declares(campaign, KEY_BUDGET_MAX_PER_CHILD)) {
4768
- issues.error({
4769
- field: `campaign.${KEY_BUDGET_MAX_PER_CHILD}`,
4770
- message: BUDGET_MAX_PER_CHILD_MESSAGE,
4771
- code: CODE_UNKNOWN_YAML_KEY,
4772
- suggestion: BUDGET_MAX_PER_CHILD_SUGGESTION
4773
- });
4774
- }
4775
5036
  const steps = flow.steps;
4776
5037
  if (!isRecord(steps)) return;
4777
5038
  for (const [stepID, rawStep] of Object.entries(steps)) {
@@ -5139,9 +5400,9 @@ function refuse(issues, stepId, key, field) {
5139
5400
  }
5140
5401
 
5141
5402
  // 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) {
5403
+ var GO_SPACE2 = "\\t\\n\\v\\f\\r \\u0085\\u00A0\\u1680\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000";
5404
+ var GO_TRIM_SPACE_RE = new RegExp(`^[${GO_SPACE2}]+|[${GO_SPACE2}]+$`, "g");
5405
+ function goTrimSpace2(s) {
5145
5406
  return s.replace(GO_TRIM_SPACE_RE, "");
5146
5407
  }
5147
5408
  function goToLower(s) {
@@ -5362,7 +5623,7 @@ function goQueryKeys(rawQuery) {
5362
5623
  return keys;
5363
5624
  }
5364
5625
  function goEndpointOrigin(raw, defaultPorts, protocolSeparator) {
5365
- const trimmed = goTrimSpace(raw);
5626
+ const trimmed = goTrimSpace2(raw);
5366
5627
  const u = goParseSchemeHost(trimmed);
5367
5628
  if (u === null || u.scheme === "" || u.host === "") {
5368
5629
  return goToLower(trimmed.replace(/\/+$/, ""));
@@ -5596,225 +5857,165 @@ function validateFamilies(flow, steps, issues) {
5596
5857
  });
5597
5858
  }
5598
5859
  }
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, "");
5860
+
5861
+ // src/validators/removedSignalCampaign.ts
5862
+ var TEMPLATE_ACTION_OPEN3 = "{{";
5863
+ function fill(format, ...args) {
5864
+ let i = 0;
5865
+ return format.replace(/%[qs]/g, () => args[i++] ?? "");
5620
5866
  }
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
- };
5867
+ function stringEntries(m, key) {
5868
+ const raw = m[key];
5869
+ return Array.isArray(raw) ? raw.filter((v) => typeof v === "string") : [];
5659
5870
  }
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;
5871
+ function triggerTypes(orch) {
5872
+ const raw = orch.triggers;
5873
+ if (!Array.isArray(raw)) return [];
5874
+ const out = [];
5875
+ for (const t of raw) {
5876
+ if (isRecord(t) && typeof t.type === "string") out.push(t.type);
5670
5877
  }
5878
+ return out;
5671
5879
  }
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;
5880
+ function namesIn(names, removed2) {
5881
+ const found = [];
5882
+ for (const name of names) {
5883
+ if (removed2.includes(name) && !found.includes(name)) found.push(name);
5730
5884
  }
5885
+ return found;
5731
5886
  }
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";
5887
+ function orchestratorToolsAllow(orch) {
5888
+ const source = orch.exons;
5889
+ if (typeof source !== "string" || source === "") return [];
5747
5890
  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
- });
5891
+ if (report.parseError !== null || !report.specRead || !report.toolsAllowDeclared) return [];
5892
+ return [...report.toolsAllow];
5893
+ }
5894
+ function validateRemovedOrchestratorSurfaces(flow, issues) {
5895
+ const sig = REMOVED_SIGNAL_SURFACES;
5896
+ const camp = REMOVED_CAMPAIGN_SURFACES;
5897
+ const orch = isRecord(flow.orchestrator) ? flow.orchestrator : null;
5898
+ const lists = orch === null ? [] : [
5899
+ ...["tools", "deny_tools", "require_approval_for"].map((key) => ({
5900
+ field: `orchestrator.${key}`,
5901
+ names: stringEntries(orch, key)
5902
+ })),
5903
+ { field: "orchestrator.exons", names: orchestratorToolsAllow(orch) }
5904
+ ];
5905
+ if (orch !== null) {
5906
+ if (orch.enable_signals === true) {
5907
+ issues.error({
5908
+ field: "orchestrator.enable_signals",
5909
+ code: sig.signalRemovedCode,
5910
+ message: sig.enableSignalsMessage,
5911
+ suggestion: sig.suggestion
5912
+ });
5913
+ }
5914
+ for (const list of lists) {
5915
+ const named = namesIn(list.names, sig.tools);
5916
+ if (named.length === 0) continue;
5917
+ issues.error({
5918
+ field: list.field,
5919
+ code: sig.signalRemovedCode,
5920
+ message: fill(sig.toolNamesMessageFormat, list.field, named.join(", ")),
5921
+ suggestion: sig.suggestion
5922
+ });
5923
+ }
5924
+ if (namesIn(triggerTypes(orch), sig.triggers).length > 0) {
5925
+ issues.error({
5926
+ field: "orchestrator.triggers",
5927
+ code: sig.flowModificationRemovedCode,
5928
+ message: sig.triggersMessage,
5929
+ suggestion: sig.suggestion
5930
+ });
5931
+ }
5772
5932
  }
5773
- if (report.resources.length > 0) {
5774
- const refs = report.resources.map((r) => `${JSON.stringify(r.ref)} (kind ${r.kind})`);
5933
+ const block = flow[camp.flowKey];
5934
+ if (block !== void 0 && block !== null) {
5775
5935
  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);
5936
+ field: camp.flowKey,
5937
+ code: camp.code,
5938
+ message: camp.blockMessage,
5939
+ suggestion: camp.suggestion
5798
5940
  });
5799
5941
  }
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
- );
5942
+ if (orch !== null) {
5943
+ for (const list of lists) {
5944
+ const named = namesIn(list.names, camp.tools);
5945
+ if (named.length === 0) continue;
5946
+ issues.error({
5947
+ field: list.field,
5948
+ code: camp.code,
5949
+ message: fill(camp.toolNamesMessageFormat, list.field, named.join(", ")),
5950
+ suggestion: camp.suggestion
5951
+ });
5952
+ }
5953
+ const triggers = namesIn(triggerTypes(orch), camp.triggers);
5954
+ if (triggers.length > 0) {
5955
+ issues.error({
5956
+ field: "orchestrator.triggers",
5957
+ code: camp.code,
5958
+ message: fill(camp.triggersMessageFormat, triggers.join(", ")),
5959
+ suggestion: camp.suggestion
5960
+ });
5806
5961
  }
5807
- return;
5808
5962
  }
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
- );
5963
+ const judgeStep = (executor, query, field, stepID) => {
5964
+ if (typeof executor !== "string" || executor === "") return;
5965
+ if (executor.includes(TEMPLATE_ACTION_OPEN3)) return;
5966
+ const scheme = removedExecutorScheme(executor);
5967
+ if (REMOVED_AGENT_SURFACES.schemes.has(scheme) || sig.stepSkipSchemes.has(scheme)) return;
5968
+ if (!isRecord(query)) return;
5969
+ const quoted = JSON.stringify(stepID);
5970
+ if (Object.prototype.hasOwnProperty.call(query, sig.flowModificationStepKey)) {
5971
+ issues.error({
5972
+ field,
5973
+ code: sig.flowModificationRemovedCode,
5974
+ stepId: stepID,
5975
+ message: fill(sig.flowModificationStepMessageFormat, quoted),
5976
+ suggestion: sig.suggestion
5977
+ });
5978
+ }
5979
+ if (scheme === sig.stepKeyScheme && Object.prototype.hasOwnProperty.call(query, sig.stepKey)) {
5980
+ issues.error({
5981
+ field,
5982
+ code: sig.signalRemovedCode,
5983
+ stepId: stepID,
5984
+ message: fill(sig.stepKeyMessageFormat, quoted),
5985
+ suggestion: sig.suggestion
5986
+ });
5987
+ }
5988
+ };
5989
+ const steps = flow.steps;
5990
+ if (isRecord(steps)) {
5991
+ for (const stepID of Object.keys(steps).sort()) {
5992
+ const step = steps[stepID];
5993
+ if (!isRecord(step)) continue;
5994
+ judgeStep(step.executor, step.query, `steps.${stepID}`, stepID);
5995
+ const loop = step.loop;
5996
+ if (!isRecord(loop) || !Array.isArray(loop.steps)) continue;
5997
+ loop.steps.forEach((sub, i) => {
5998
+ if (!isRecord(sub)) return;
5999
+ const subID = issues.stringOf(sub, "id", `steps.${stepID}.loop.steps[${i}]`) ?? String(i);
6000
+ judgeStep(
6001
+ sub.executor,
6002
+ sub.query,
6003
+ `steps.${stepID}.loop.steps[${i}]`,
6004
+ `${stepID}.${subID}`
6005
+ );
6006
+ });
6007
+ }
5813
6008
  }
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
- );
6009
+ const ignored = (field) => issues.warn({
6010
+ field,
6011
+ code: sig.removedKeyIgnoredCode,
6012
+ message: fill(sig.ignoredKeyMessageFormat, field),
6013
+ suggestion: sig.ignoredKeySuggestion
6014
+ });
6015
+ if (orch !== null && orch.enable_signals === false) ignored("orchestrator.enable_signals");
6016
+ const checkpoint = flow.checkpoint;
6017
+ if (isRecord(checkpoint) && checkpoint.pre_dfm !== void 0 && checkpoint.pre_dfm !== null) {
6018
+ ignored("checkpoint.pre_dfm");
5818
6019
  }
5819
6020
  }
5820
6021
 
@@ -5831,7 +6032,7 @@ function sprintf(format, ...args) {
5831
6032
  });
5832
6033
  }
5833
6034
  function literalOrEmpty(s) {
5834
- const t = goTrimSpace2(s);
6035
+ const t = goTrimSpace(s);
5835
6036
  return t.includes(TEMPLATE_ACTION_OPEN) ? "" : t;
5836
6037
  }
5837
6038
  function euOnlyResidency(flow, issues) {
@@ -5846,7 +6047,7 @@ function euOnlyResidency(flow, issues) {
5846
6047
  }
5847
6048
  if (codes.length === 0) return null;
5848
6049
  const eu = new Set(COMPLIANCE_RESIDENCY.euResidencyCodes);
5849
- return codes.every((c) => eu.has(goTrimSpace2(c).toUpperCase())) ? codes : null;
6050
+ return codes.every((c) => eu.has(goTrimSpace(c).toUpperCase())) ? codes : null;
5850
6051
  }
5851
6052
  function aiProvider(executor) {
5852
6053
  const prefix = COMPLIANCE_RESIDENCY.aiProtocol + COMPLIANCE_RESIDENCY.protocolSeparator;
@@ -5862,7 +6063,7 @@ function isRegionLane(provider) {
5862
6063
  }
5863
6064
  function nonBlankString(m, key) {
5864
6065
  const v = own2(m, key);
5865
- return typeof v === "string" && goTrimSpace2(v) !== "";
6066
+ return typeof v === "string" && goTrimSpace(v) !== "";
5866
6067
  }
5867
6068
  function queryEnforcesRegion(query) {
5868
6069
  const v = own2(query, COMPLIANCE_RESIDENCY.enforceRegionParam);
@@ -5874,7 +6075,7 @@ function flowDirectsEndpoint(flow, provider, issues) {
5874
6075
  const inst = own2(own2(flow, "executor_config"), key);
5875
6076
  if (!isRecord(inst)) return false;
5876
6077
  const baseUrl = issues.stringOf(inst, "base_url", `executor_config.${key}`);
5877
- return baseUrl !== null && goTrimSpace2(baseUrl) !== "";
6078
+ return baseUrl !== null && goTrimSpace(baseUrl) !== "";
5878
6079
  }
5879
6080
  function callPinsEndpoint(flow, provider, query, issues) {
5880
6081
  if (nonBlankString(query, COMPLIANCE_RESIDENCY.regionParam)) return true;
@@ -5947,7 +6148,7 @@ function warnOrchestrator(flow, issues, warn) {
5947
6148
  const provider = literalOrEmpty(report.provider);
5948
6149
  if (!isRegionLane(provider)) return;
5949
6150
  const region = issues.stringOf(orch, "region", "orchestrator");
5950
- if (region !== null && goTrimSpace2(region) !== "") return;
6151
+ if (region !== null && goTrimSpace(region) !== "") return;
5951
6152
  if (orch.enforce_region === true) return;
5952
6153
  if (flowDirectsEndpoint(flow, provider, issues)) return;
5953
6154
  warn(COMPLIANCE_RESIDENCY.siteOrchestrator, COMPLIANCE_RESIDENCY.orchestratorField, void 0, provider);
@@ -6113,7 +6314,7 @@ function asFileRef(v) {
6113
6314
  };
6114
6315
  }
6115
6316
  function isTextualMediaType(mediaType) {
6116
- const base = goToLower(goTrimSpace((mediaType.split(";")[0] ?? "").toString()));
6317
+ const base = goToLower(goTrimSpace2((mediaType.split(";")[0] ?? "").toString()));
6117
6318
  if (base.startsWith("text/")) return true;
6118
6319
  if (base.endsWith("+json") || base.endsWith("+xml")) return true;
6119
6320
  return TEXTUAL_MEDIA_BASES.has(base);
@@ -6217,7 +6418,7 @@ var Walker = class _Walker {
6217
6418
  }
6218
6419
  }
6219
6420
  const title = this.s(ex, "title", path);
6220
- if (goTrimSpace(title) === "") {
6421
+ if (goTrimSpace2(title) === "") {
6221
6422
  this.err(
6222
6423
  "example_title_missing",
6223
6424
  `${path}.title`,
@@ -6234,7 +6435,7 @@ var Walker = class _Walker {
6234
6435
  }
6235
6436
  }
6236
6437
  const guidance = this.s(ex, "guidance", path);
6237
- if (goTrimSpace(guidance) === "") {
6438
+ if (goTrimSpace2(guidance) === "") {
6238
6439
  this.warn(
6239
6440
  "example_guidance_missing",
6240
6441
  `${path}.guidance`,
@@ -6661,7 +6862,7 @@ var Walker = class _Walker {
6661
6862
  }
6662
6863
  const rubric = this.s(e, "rubric", path);
6663
6864
  if (rubric !== "") {
6664
- if (goTrimSpace(rubric) === "") {
6865
+ if (goTrimSpace2(rubric) === "") {
6665
6866
  this.err(
6666
6867
  "example_rubric_empty",
6667
6868
  `${path}.rubric`,
@@ -6716,7 +6917,7 @@ var Walker = class _Walker {
6716
6917
  this.err("example_must_not_invalid", `${path}.must_not`, mustNotMsg);
6717
6918
  }
6718
6919
  mustNot.forEach((item, j) => {
6719
- if (goTrimSpace(item) === "" || runeCount(item) > L.maxMustNotRunes) {
6920
+ if (goTrimSpace2(item) === "" || runeCount(item) > L.maxMustNotRunes) {
6720
6921
  this.err("example_must_not_invalid", `${path}.must_not[${j}]`, mustNotMsg);
6721
6922
  }
6722
6923
  });
@@ -7274,6 +7475,7 @@ function validateParsed(flow, opts, sources) {
7274
7475
  const f = flow;
7275
7476
  validateBasicStructure(f, issues);
7276
7477
  validateRetiredKeys(f, issues);
7478
+ validateRemovedFlowKeys(f, issues);
7277
7479
  validateExecutors(f, issues);
7278
7480
  validateQuerySchema(f, issues);
7279
7481
  validateResponseExpectations(f, issues);
@@ -7297,6 +7499,7 @@ function validateParsed(flow, opts, sources) {
7297
7499
  validateCredentialEndpointPairing(f, issues);
7298
7500
  validateExamples(f, issues);
7299
7501
  validateOrchestratorExons(f, issues);
7502
+ validateRemovedOrchestratorSurfaces(f, issues);
7300
7503
  validateResidencyEnforcement(f, issues);
7301
7504
  validateUnknownKeys(f, issues);
7302
7505
  const templateStats = validateTemplates(f, issues, opts);