@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.js CHANGED
@@ -449,11 +449,10 @@ function validateBasicStructure(flow, issues) {
449
449
 
450
450
  // src/spec/aigentflow-spec.json
451
451
  var aigentflow_spec_default = {
452
- specVersion: "2.831.0",
452
+ specVersion: "2.837.0",
453
453
  inputSchemaVersion: 1,
454
454
  executorSchemes: [
455
455
  "a2a",
456
- "agent",
457
456
  "ai",
458
457
  "aiv",
459
458
  "async",
@@ -467,7 +466,6 @@ var aigentflow_spec_default = {
467
466
  "email",
468
467
  "eurostat",
469
468
  "eval",
470
- "exons",
471
469
  "flow",
472
470
  "function",
473
471
  "gdelt",
@@ -482,6 +480,7 @@ var aigentflow_spec_default = {
482
480
  "messenger",
483
481
  "mock",
484
482
  "mqtt",
483
+ "nexus",
485
484
  "opencorporates",
486
485
  "openmeteo",
487
486
  "overpass",
@@ -546,12 +545,9 @@ var aigentflow_spec_default = {
546
545
  "step_failed",
547
546
  "mission_started",
548
547
  "mission_completed",
549
- "dfm_suggestion",
550
548
  "timer",
551
549
  "request_turn",
552
550
  "mission_recovered",
553
- "campaign_child_completed",
554
- "campaign_child_failed",
555
551
  "human_response",
556
552
  "human_input"
557
553
  ],
@@ -567,15 +563,6 @@ var aigentflow_spec_default = {
567
563
  "aif_ask_human",
568
564
  "aif_flow_list",
569
565
  "aif_mission_reflect",
570
- "aif_signal_complete",
571
- "aif_signal_need_human",
572
- "aif_signal_delegate",
573
- "aif_signal_need_tool",
574
- "aif_signal_modify_flow",
575
- "aif_campaign_spawn",
576
- "aif_campaign_status",
577
- "aif_campaign_results",
578
- "aif_campaign_cancel",
579
566
  "aif_memory_plan",
580
567
  "aif_memory_record",
581
568
  "aif_memory_recall",
@@ -747,24 +734,87 @@ var aigentflow_spec_default = {
747
734
  orchestratorToolAllow: {
748
735
  warningField: "orchestrator.exons",
749
736
  askHumanTool: "aif_ask_human",
750
- enableSignalsDefault: true,
751
- lifecycleTools: ["aif_mission_complete", "aif_mission_fail", "aif_request_turn"],
752
- signalTools: [
737
+ lifecycleTools: [
738
+ "aif_mission_complete",
739
+ "aif_mission_fail",
740
+ "aif_request_turn"
741
+ ]
742
+ },
743
+ exons: {
744
+ frontmatterDelimiter: "---"
745
+ },
746
+ removedAgentSurfaces: {
747
+ removedSchemes: ["exons", "agent"],
748
+ paramScheme: "ai",
749
+ aiParams: [
750
+ "agentic",
751
+ "deny_tools",
752
+ "discoverable_tools",
753
+ "enable_signals",
754
+ "guardrails",
755
+ "max_tool_calls",
756
+ "max_tool_turns",
757
+ "mcp_server_url",
758
+ "parallel_tool_calls",
759
+ "require_confirmation",
760
+ "timeout_seconds",
761
+ "tool_auth_token",
762
+ "tool_discovery"
763
+ ],
764
+ flowKeys: ["exons_uploads"],
765
+ executorRemovedCode: "executor_removed",
766
+ agenticParamRemovedCode: "agentic_param_removed",
767
+ schemeMessageFormat: "step %q uses %s://, which AIgentFlow no longer runs: its own agent chains were removed",
768
+ paramsMessageFormat: "step %q sets %s on ai://: AIgentFlow's own agent loop (agentic mode) was removed, so these parameters no longer exist",
769
+ flowKeyMessageFormat: "the flow-level key %q belonged to a removed agent surface and is no longer read",
770
+ 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`
771
+ },
772
+ removedSignalSurfaces: {
773
+ signalRemovedCode: "signal_removed",
774
+ flowModificationRemovedCode: "flow_modification_removed",
775
+ removedKeyIgnoredCode: "removed_key_ignored",
776
+ tools: [
753
777
  "aif_signal_complete",
754
778
  "aif_signal_need_human",
755
779
  "aif_signal_delegate",
756
780
  "aif_signal_need_tool",
757
781
  "aif_signal_modify_flow"
758
782
  ],
759
- campaignTools: [
760
- "aif_campaign_cancel",
761
- "aif_campaign_results",
762
- "aif_campaign_spawn",
763
- "aif_campaign_status"
764
- ]
783
+ triggers: [
784
+ "dfm_suggestion"
785
+ ],
786
+ stepKey: "enable_signals",
787
+ stepKeyScheme: "aiv",
788
+ flowModificationStepKey: "flow_modification_allowed",
789
+ stepSkipSchemes: [
790
+ "flow"
791
+ ],
792
+ 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",
793
+ toolNamesMessageFormat: "%s names %s: AIgentFlow's agent signal tools were removed in 2.834.0",
794
+ stepKeyMessageFormat: "step %q sets enable_signals: AIgentFlow's agent signals were removed in 2.834.0, so the parameter no longer exists",
795
+ triggersMessage: "orchestrator.triggers names dfm_suggestion: dynamic flow modification was removed in 2.834.0, so nothing raises that trigger",
796
+ flowModificationStepMessageFormat: "step %q sets flow_modification_allowed: dynamic flow modification was removed in 2.834.0",
797
+ 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",
798
+ ignoredKeySuggestion: "Remove the key; nothing reads it",
799
+ 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"
765
800
  },
766
- exons: {
767
- frontmatterDelimiter: "---"
801
+ removedCampaignSurfaces: {
802
+ code: "campaign_removed",
803
+ flowKey: "campaign",
804
+ tools: [
805
+ "aif_campaign_spawn",
806
+ "aif_campaign_status",
807
+ "aif_campaign_results",
808
+ "aif_campaign_cancel"
809
+ ],
810
+ triggers: [
811
+ "campaign_child_completed",
812
+ "campaign_child_failed"
813
+ ],
814
+ blockMessage: "the flow declares a campaign: block: campaigns were removed in 2.836.0, so nothing would spawn or track its child missions",
815
+ toolNamesMessageFormat: "%s names %s: AIgentFlow's campaign tools were removed in 2.836.0",
816
+ triggersMessageFormat: "orchestrator.triggers names %s: campaigns were removed in 2.836.0, so no child mission raises that trigger",
817
+ 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"
768
818
  },
769
819
  executorConfigEnvScopes: {
770
820
  referencePrefix: "${",
@@ -988,19 +1038,20 @@ var aigentflow_spec_default = {
988
1038
  ]
989
1039
  },
990
1040
  atlasExecutor: {
991
- scheme: "atlas",
1041
+ scheme: "nexus",
1042
+ aliases: ["atlas"],
992
1043
  providerAgent: "agent",
993
1044
  providerCapability: "capability",
994
1045
  agentResource: "run",
995
1046
  capabilityRefPattern: "^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$",
996
1047
  operationUnknown: {
997
1048
  code: "atlas_operation_unknown",
998
- messageFormat: "step %s uses %s, which is not an Atlas operation. The providers are agent (resource run) and capability (resource: a capability reference).",
999
- suggestion: "Use atlas://agent/run or atlas://capability/<capability-ref>."
1049
+ messageFormat: "step %s uses %s, which is not a Nexus operation. The providers are agent (resource run) and capability (resource: a capability reference).",
1050
+ suggestion: "Use nexus://agent/run or nexus://capability/<capability-ref>."
1000
1051
  },
1001
1052
  agentMissing: {
1002
1053
  code: "atlas_agent_missing",
1003
- messageFormat: "step %s runs an Atlas agent but query.agent is missing, empty or not a string.",
1054
+ messageFormat: "step %s runs a Nexus agent but query.agent is missing, empty or not a string.",
1004
1055
  suggestion: "Set query.agent to the agent to run, written out as a literal such as @acme/reporter."
1005
1056
  },
1006
1057
  agentNotLiteral: {
@@ -1015,7 +1066,7 @@ var aigentflow_spec_default = {
1015
1066
  },
1016
1067
  promptMissing: {
1017
1068
  code: "atlas_prompt_missing",
1018
- messageFormat: "step %s runs an Atlas agent with neither query.prompt nor query.prompt_template.",
1069
+ messageFormat: "step %s runs a Nexus agent with neither query.prompt nor query.prompt_template.",
1019
1070
  suggestion: "Set exactly one of query.prompt or query.prompt_template."
1020
1071
  },
1021
1072
  promptBoth: {
@@ -1025,7 +1076,7 @@ var aigentflow_spec_default = {
1025
1076
  },
1026
1077
  capabilityRefInvalid: {
1027
1078
  code: "atlas_capability_ref_invalid",
1028
- messageFormat: "step %s uses atlas://capability/%s, which is not a valid capability reference.",
1079
+ messageFormat: "step %s uses nexus://capability/%s, which is not a valid capability reference.",
1029
1080
  suggestion: "A capability reference starts with a letter or digit and continues with letters, digits, _ and - up to 128 characters in all."
1030
1081
  },
1031
1082
  modelNotLiteral: {
@@ -1552,20 +1603,6 @@ var aigentflow_spec_default = {
1552
1603
  BillingConfig: {
1553
1604
  max_credits: "scalar"
1554
1605
  },
1555
- CampaignChildFlowDef: {
1556
- alias: "scalar",
1557
- flow_id: "scalar",
1558
- flow_name: "scalar"
1559
- },
1560
- CampaignConfig: {
1561
- child_flows: "list<CampaignChildFlowDef>",
1562
- max_concurrent: "scalar",
1563
- max_credits_per_child: "scalar",
1564
- max_depth: "scalar",
1565
- max_total_children: "scalar",
1566
- on_children_complete: "scalar",
1567
- shared_memory: "scalar"
1568
- },
1569
1606
  Checkpoint: {
1570
1607
  exact: "map<any>",
1571
1608
  fields: "map<FieldMatcher>",
@@ -1699,7 +1736,7 @@ var aigentflow_spec_default = {
1699
1736
  Flow: {
1700
1737
  aigentflow_version: "scalar",
1701
1738
  billing: "BillingConfig",
1702
- campaign: "CampaignConfig",
1739
+ campaign: "any",
1703
1740
  checkpoint: "CheckpointConfig",
1704
1741
  classification: "FlowClassification",
1705
1742
  compliance: "FlowCompliance",
@@ -2009,15 +2046,64 @@ var KNOWN_KEYS = aigentflow_spec_default.knownKeys;
2009
2046
  var ORCHESTRATOR_TOOL_ALLOW = {
2010
2047
  warningField: aigentflow_spec_default.orchestratorToolAllow.warningField,
2011
2048
  askHumanTool: aigentflow_spec_default.orchestratorToolAllow.askHumanTool,
2012
- enableSignalsDefault: aigentflow_spec_default.orchestratorToolAllow.enableSignalsDefault,
2013
- lifecycleTools: aigentflow_spec_default.orchestratorToolAllow.lifecycleTools,
2014
- signalTools: aigentflow_spec_default.orchestratorToolAllow.signalTools,
2015
- campaignTools: aigentflow_spec_default.orchestratorToolAllow.campaignTools
2049
+ lifecycleTools: aigentflow_spec_default.orchestratorToolAllow.lifecycleTools
2016
2050
  };
2017
2051
  var EXONS = {
2018
2052
  /** The YAML frontmatter delimiter line. */
2019
2053
  frontmatterDelimiter: aigentflow_spec_default.exons.frontmatterDelimiter
2020
2054
  };
2055
+ var removed = aigentflow_spec_default.removedAgentSurfaces;
2056
+ var REMOVED_AGENT_SURFACES = {
2057
+ schemes: new Set(removed.removedSchemes),
2058
+ paramScheme: removed.paramScheme,
2059
+ aiParams: new Set(removed.aiParams),
2060
+ flowKeys: removed.flowKeys,
2061
+ executorRemovedCode: removed.executorRemovedCode,
2062
+ agenticParamRemovedCode: removed.agenticParamRemovedCode,
2063
+ schemeMessageFormat: removed.schemeMessageFormat,
2064
+ paramsMessageFormat: removed.paramsMessageFormat,
2065
+ flowKeyMessageFormat: removed.flowKeyMessageFormat,
2066
+ suggestion: removed.suggestion
2067
+ };
2068
+ var signal = aigentflow_spec_default.removedSignalSurfaces;
2069
+ var REMOVED_SIGNAL_SURFACES = {
2070
+ signalRemovedCode: signal.signalRemovedCode,
2071
+ flowModificationRemovedCode: signal.flowModificationRemovedCode,
2072
+ removedKeyIgnoredCode: signal.removedKeyIgnoredCode,
2073
+ tools: signal.tools,
2074
+ triggers: signal.triggers,
2075
+ stepKey: signal.stepKey,
2076
+ stepKeyScheme: signal.stepKeyScheme,
2077
+ flowModificationStepKey: signal.flowModificationStepKey,
2078
+ stepSkipSchemes: new Set(signal.stepSkipSchemes),
2079
+ enableSignalsMessage: signal.enableSignalsMessage,
2080
+ toolNamesMessageFormat: signal.toolNamesMessageFormat,
2081
+ stepKeyMessageFormat: signal.stepKeyMessageFormat,
2082
+ triggersMessage: signal.triggersMessage,
2083
+ flowModificationStepMessageFormat: signal.flowModificationStepMessageFormat,
2084
+ ignoredKeyMessageFormat: signal.ignoredKeyMessageFormat,
2085
+ ignoredKeySuggestion: signal.ignoredKeySuggestion,
2086
+ suggestion: signal.suggestion
2087
+ };
2088
+ var campaign = aigentflow_spec_default.removedCampaignSurfaces;
2089
+ var REMOVED_CAMPAIGN_SURFACES = {
2090
+ code: campaign.code,
2091
+ flowKey: campaign.flowKey,
2092
+ tools: campaign.tools,
2093
+ triggers: campaign.triggers,
2094
+ blockMessage: campaign.blockMessage,
2095
+ toolNamesMessageFormat: campaign.toolNamesMessageFormat,
2096
+ triggersMessageFormat: campaign.triggersMessageFormat,
2097
+ suggestion: campaign.suggestion
2098
+ };
2099
+ var REMOVED_ORCHESTRATOR_TOOLS = /* @__PURE__ */ new Set([
2100
+ ...signal.tools,
2101
+ ...campaign.tools
2102
+ ]);
2103
+ var REMOVED_ORCHESTRATOR_TRIGGERS = /* @__PURE__ */ new Set([
2104
+ ...signal.triggers,
2105
+ ...campaign.triggers
2106
+ ]);
2021
2107
  var EXECUTOR_CONFIG_ENV_SCOPES = {
2022
2108
  referencePrefix: aigentflow_spec_default.executorConfigEnvScopes.referencePrefix,
2023
2109
  referenceSuffix: aigentflow_spec_default.executorConfigEnvScopes.referenceSuffix,
@@ -2110,7 +2196,8 @@ var FUNCTION_OPERATIONS = {
2110
2196
  };
2111
2197
  var atlas = aigentflow_spec_default.atlasExecutor;
2112
2198
  var ATLAS_EXECUTOR = {
2113
- scheme: atlas.scheme,
2199
+ /** The canonical scheme (nexus) and its aliases (atlas): one set of rules. */
2200
+ schemes: /* @__PURE__ */ new Set([atlas.scheme, ...atlas.aliases]),
2114
2201
  providerAgent: atlas.providerAgent,
2115
2202
  providerCapability: atlas.providerCapability,
2116
2203
  agentResource: atlas.agentResource,
@@ -2139,6 +2226,220 @@ var RESERVED_INPUT_NAMES = {
2139
2226
  queryFieldFormat: aigentflow_spec_default.reservedInputNames.queryFieldFormat,
2140
2227
  schemaFieldFormat: aigentflow_spec_default.reservedInputNames.schemaFieldFormat
2141
2228
  };
2229
+ var CODE_PARSE_FAILED = "orchestrator_exons_parse_failed";
2230
+ var CODE_NO_PROVIDER = "orchestrator_exons_no_provider";
2231
+ var CODE_RESOURCES = "exons_resources_unhonoured";
2232
+ var CODE_TOOL_WITHHELD = "orchestrator_tool_withheld";
2233
+ var EXONS_OPEN_DELIM = "{~";
2234
+ var EXONS_LEGACY_CONFIG_OPEN = "{~exons.config~}";
2235
+ var UTF8_BOM = "\uFEFF";
2236
+ var MAX_ALIAS_COUNT2 = 100;
2237
+ var UNREAD = {
2238
+ parseError: null,
2239
+ specRead: false,
2240
+ hasSpec: false,
2241
+ provider: "",
2242
+ toolsAllowDeclared: false,
2243
+ toolsAllow: [],
2244
+ resources: []
2245
+ };
2246
+ var GO_SPACE = "\\t\\n\\v\\f\\r \\u0085\\u00a0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000";
2247
+ var GO_TRIM_SPACE = new RegExp(`^[${GO_SPACE}]+|[${GO_SPACE}]+$`, "g");
2248
+ function goTrimSpace(s) {
2249
+ return s.replace(GO_TRIM_SPACE, "");
2250
+ }
2251
+ function extractExonsFrontmatter(source) {
2252
+ const delim = EXONS.frontmatterDelimiter;
2253
+ let start = source.startsWith(UTF8_BOM) ? source.slice(UTF8_BOM.length) : source;
2254
+ start = start.replace(/^[ \t]+/, "");
2255
+ if (start.startsWith(EXONS_LEGACY_CONFIG_OPEN)) {
2256
+ return {
2257
+ frontmatter: "",
2258
+ hasFrontmatter: false,
2259
+ parseError: "legacy JSON config block detected; use YAML frontmatter"
2260
+ };
2261
+ }
2262
+ if (!start.startsWith(delim)) return { frontmatter: "", hasFrontmatter: false, parseError: null };
2263
+ let rest = start.slice(delim.length);
2264
+ if (rest.startsWith("\n")) rest = rest.slice(1);
2265
+ else if (rest.startsWith("\r\n")) rest = rest.slice(2);
2266
+ else return { frontmatter: "", hasFrontmatter: false, parseError: null };
2267
+ let pos = 0;
2268
+ while (pos <= rest.length) {
2269
+ const line = rest.slice(pos);
2270
+ if (line.startsWith(delim)) {
2271
+ const after = line.slice(delim.length);
2272
+ if (after === "" || after[0] === "\n" || after[0] === "\r") {
2273
+ return {
2274
+ frontmatter: goTrimSpace(rest.slice(0, pos)),
2275
+ hasFrontmatter: true,
2276
+ parseError: null
2277
+ };
2278
+ }
2279
+ }
2280
+ const nl = line.indexOf("\n");
2281
+ if (nl < 0) break;
2282
+ pos += nl + 1;
2283
+ }
2284
+ return {
2285
+ frontmatter: "",
2286
+ hasFrontmatter: true,
2287
+ parseError: "YAML frontmatter not properly closed"
2288
+ };
2289
+ }
2290
+ function retagScalars(node) {
2291
+ if (isMap(node)) {
2292
+ for (const pair of node.items) {
2293
+ retagScalars(pair.key);
2294
+ retagScalars(pair.value);
2295
+ }
2296
+ } else if (isSeq(node)) {
2297
+ for (const item of node.items) retagScalars(item);
2298
+ } else if (isScalar(node) && (typeof node.value === "number" || typeof node.value === "boolean") && typeof node.source === "string") {
2299
+ node.value = node.source;
2300
+ }
2301
+ }
2302
+ var DecodeError = class extends Error {
2303
+ };
2304
+ function goString(v) {
2305
+ if (v === null || v === void 0) return "";
2306
+ if (isString(v)) return v;
2307
+ throw new DecodeError("not a string");
2308
+ }
2309
+ function goStringList(v) {
2310
+ if (v === null || v === void 0) return null;
2311
+ if (!isArray(v)) throw new DecodeError("not a list");
2312
+ return v.filter((item) => item !== null).map(goString);
2313
+ }
2314
+ function goStruct(v) {
2315
+ if (v === null || v === void 0) return null;
2316
+ if (!isRecord(v)) throw new DecodeError("not a mapping");
2317
+ return v;
2318
+ }
2319
+ function decodeSpecFields(frontmatter) {
2320
+ let value;
2321
+ try {
2322
+ const docs = parseAllDocuments(frontmatter, { merge: true });
2323
+ const doc = Array.isArray(docs) ? docs[0] : void 0;
2324
+ if (doc === void 0) {
2325
+ value = null;
2326
+ } else {
2327
+ if (doc.errors.length > 0) return null;
2328
+ retagScalars(doc.contents);
2329
+ value = doc.toJS({ maxAliasCount: MAX_ALIAS_COUNT2 });
2330
+ }
2331
+ } catch {
2332
+ return null;
2333
+ }
2334
+ try {
2335
+ const root = goStruct(value) ?? {};
2336
+ const execution = goStruct(root.execution);
2337
+ const tools = goStruct(root.tools);
2338
+ const requirements = goStruct(root.requirements);
2339
+ const allow = tools === null ? null : goStringList(tools.allow);
2340
+ let resources = [];
2341
+ if (requirements !== null) {
2342
+ const raw = requirements.resources;
2343
+ if (raw !== null && raw !== void 0) {
2344
+ if (!isArray(raw)) throw new DecodeError("not a list");
2345
+ resources = raw.filter((entry) => entry !== null).map((entry) => {
2346
+ const r = goStruct(entry) ?? {};
2347
+ return { ref: goString(r.ref), kind: goString(r.kind), scope: goString(r.scope) };
2348
+ });
2349
+ }
2350
+ }
2351
+ return {
2352
+ provider: execution === null ? "" : goString(execution.provider),
2353
+ toolsAllowDeclared: allow !== null,
2354
+ toolsAllow: allow ?? [],
2355
+ resources
2356
+ };
2357
+ } catch (e) {
2358
+ if (e instanceof DecodeError) return null;
2359
+ throw e;
2360
+ }
2361
+ }
2362
+ function inspectExonsFrontmatter(source) {
2363
+ const fm = extractExonsFrontmatter(source);
2364
+ if (fm.parseError !== null) return { ...UNREAD, parseError: fm.parseError };
2365
+ if (!fm.hasFrontmatter || fm.frontmatter === "") return { ...UNREAD, specRead: true };
2366
+ if (fm.frontmatter.includes(EXONS_OPEN_DELIM)) return { ...UNREAD };
2367
+ const fields = decodeSpecFields(fm.frontmatter);
2368
+ if (fields === null) return { ...UNREAD };
2369
+ return { parseError: null, specRead: true, hasSpec: true, ...fields };
2370
+ }
2371
+ function validateOrchestratorExons(flow, issues) {
2372
+ const orch = flow.orchestrator;
2373
+ if (!isRecord(orch)) return;
2374
+ const source = issues.stringOf(orch, "exons", "orchestrator");
2375
+ if (source === null || source === "") return;
2376
+ const field = "orchestrator.exons";
2377
+ const report = inspectExonsFrontmatter(source);
2378
+ if (report.parseError !== null) {
2379
+ issues.error({
2380
+ field,
2381
+ code: CODE_PARSE_FAILED,
2382
+ message: `orchestrator: failed to parse exons spec: ${report.parseError}`
2383
+ });
2384
+ return;
2385
+ }
2386
+ if (!report.specRead) return;
2387
+ if (!report.hasSpec) {
2388
+ issues.error({
2389
+ field,
2390
+ code: CODE_PARSE_FAILED,
2391
+ message: "orchestrator: failed to parse exons spec: the document has no frontmatter spec",
2392
+ suggestion: "Start the document with a --- frontmatter block that sets execution.provider"
2393
+ });
2394
+ return;
2395
+ }
2396
+ if (report.provider === "") {
2397
+ issues.error({
2398
+ field,
2399
+ code: CODE_NO_PROVIDER,
2400
+ message: "orchestrator: exons spec must have execution.provider set"
2401
+ });
2402
+ }
2403
+ if (report.resources.length > 0) {
2404
+ const refs = report.resources.map((r) => `${JSON.stringify(r.ref)} (kind ${r.kind})`);
2405
+ issues.error({
2406
+ field,
2407
+ code: CODE_RESOURCES,
2408
+ 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("; "),
2409
+ suggestion: "Remove requirements.resources, and narrow the agent with tools.mcp_servers and tools.allow"
2410
+ });
2411
+ }
2412
+ warnOrchestratorToolWithheld(orch, report, issues);
2413
+ }
2414
+ function warnOrchestratorToolWithheld(orch, report, issues) {
2415
+ if (!report.toolsAllowDeclared) return;
2416
+ const cfg = ORCHESTRATOR_TOOL_ALLOW;
2417
+ const exempt = new Set(cfg.lifecycleTools);
2418
+ const allow = new Set(report.toolsAllow);
2419
+ const withheld = (name) => !allow.has(name) && !exempt.has(name) && !REMOVED_ORCHESTRATOR_TOOLS.has(name);
2420
+ const warn = (message) => issues.warn({ field: cfg.warningField, code: CODE_TOOL_WITHHELD, message });
2421
+ const named = [];
2422
+ if (isArray(orch.tools)) {
2423
+ orch.tools.forEach((raw, i) => {
2424
+ const name = issues.stringAt(raw, `orchestrator.tools[${i}]`);
2425
+ if (name !== null) named.push(name);
2426
+ });
2427
+ }
2428
+ if (named.length > 0) {
2429
+ for (const name of named) {
2430
+ if (!withheld(name)) continue;
2431
+ warn(
2432
+ `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.`
2433
+ );
2434
+ }
2435
+ return;
2436
+ }
2437
+ if (withheld(cfg.askHumanTool)) {
2438
+ warn(
2439
+ `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.`
2440
+ );
2441
+ }
2442
+ }
2142
2443
 
2143
2444
  // src/validators/executors.ts
2144
2445
  var SCHEME_SEPARATOR = "://";
@@ -2202,11 +2503,61 @@ function checkExecutor(executor, field, stepID, issues) {
2202
2503
  }
2203
2504
  checkFunctionOperation(executor, field, stepID, templated, issues);
2204
2505
  }
2506
+ function removedExecutorScheme(executor) {
2507
+ if (executor.includes(TEMPLATE_ACTION_OPEN)) return "";
2508
+ const trimmed = goTrimSpace(executor);
2509
+ const at = trimmed.indexOf(SCHEME_SEPARATOR);
2510
+ return at < 0 ? "" : trimmed.slice(0, at).toLowerCase();
2511
+ }
2512
+ function checkRemovedSurfaces(executor, query, field, stepID, issues) {
2513
+ const cfg = REMOVED_AGENT_SURFACES;
2514
+ const quoted = JSON.stringify(stepID);
2515
+ const scheme = removedExecutorScheme(executor);
2516
+ if (cfg.schemes.has(scheme)) {
2517
+ issues.error({
2518
+ field,
2519
+ message: fillRemovedMessage(cfg.schemeMessageFormat, quoted, scheme),
2520
+ code: cfg.executorRemovedCode,
2521
+ stepId: stepID,
2522
+ suggestion: cfg.suggestion
2523
+ });
2524
+ return true;
2525
+ }
2526
+ if (scheme !== cfg.paramScheme || !isRecord(query)) return false;
2527
+ const found = Object.keys(query).filter((key) => cfg.aiParams.has(key)).sort();
2528
+ if (found.length > 0) {
2529
+ issues.error({
2530
+ field,
2531
+ message: fillRemovedMessage(cfg.paramsMessageFormat, quoted, found.join(", ")),
2532
+ code: cfg.agenticParamRemovedCode,
2533
+ stepId: stepID,
2534
+ suggestion: cfg.suggestion
2535
+ });
2536
+ }
2537
+ return false;
2538
+ }
2539
+ function fillRemovedMessage(format, ...args) {
2540
+ let i = 0;
2541
+ return format.replace(/%[qs]/g, () => args[i++] ?? "");
2542
+ }
2543
+ function validateRemovedFlowKeys(flow, issues) {
2544
+ const cfg = REMOVED_AGENT_SURFACES;
2545
+ for (const key of cfg.flowKeys) {
2546
+ if (Object.prototype.hasOwnProperty.call(flow, key)) {
2547
+ issues.error({
2548
+ field: key,
2549
+ message: fillRemovedMessage(cfg.flowKeyMessageFormat, JSON.stringify(key)),
2550
+ code: cfg.executorRemovedCode,
2551
+ suggestion: cfg.suggestion
2552
+ });
2553
+ }
2554
+ }
2555
+ }
2205
2556
  function checkAtlasStep(executor, query, stepField, stepID, issues) {
2206
2557
  if (executor.includes(TEMPLATE_ACTION_OPEN)) return;
2207
- const prefix = `${ATLAS_EXECUTOR.scheme}://`;
2208
- if (!executor.startsWith(prefix)) return;
2209
- const rest = executor.slice(prefix.length);
2558
+ const sep = executor.indexOf(SCHEME_SEPARATOR);
2559
+ if (sep < 0 || !ATLAS_EXECUTOR.schemes.has(executor.slice(0, sep))) return;
2560
+ const rest = executor.slice(sep + SCHEME_SEPARATOR.length);
2210
2561
  const slash = rest.indexOf("/");
2211
2562
  const provider = slash < 0 ? rest : rest.slice(0, slash);
2212
2563
  const resource = slash < 0 ? "" : rest.slice(slash + 1);
@@ -2282,7 +2633,9 @@ function validateExecutors(flow, issues) {
2282
2633
  const step = rawStep;
2283
2634
  const executor = issues.stringOf(step, "executor", `steps.${stepID}`);
2284
2635
  if (executor !== null && executor !== "") {
2285
- checkExecutor(executor, `steps.${stepID}.executor`, stepID, issues);
2636
+ if (!checkRemovedSurfaces(executor, step.query, `steps.${stepID}`, stepID, issues)) {
2637
+ checkExecutor(executor, `steps.${stepID}.executor`, stepID, issues);
2638
+ }
2286
2639
  checkAtlasStep(executor, step.query, `steps.${stepID}`, stepID, issues);
2287
2640
  }
2288
2641
  const loop = step.loop;
@@ -2294,12 +2647,14 @@ function validateExecutors(flow, issues) {
2294
2647
  const subID = issues.stringOf(subStep, "id", subPath) ?? String(i);
2295
2648
  const subExecutor = issues.stringOf(subStep, "executor", subPath);
2296
2649
  if (subExecutor !== null && subExecutor !== "") {
2297
- checkExecutor(
2298
- subExecutor,
2299
- `steps.${stepID}.loop.steps[${i}].executor`,
2300
- `${stepID}.${subID}`,
2301
- issues
2302
- );
2650
+ if (!checkRemovedSurfaces(subExecutor, subStep.query, subPath, `${stepID}.${subID}`, issues)) {
2651
+ checkExecutor(
2652
+ subExecutor,
2653
+ `steps.${stepID}.loop.steps[${i}].executor`,
2654
+ `${stepID}.${subID}`,
2655
+ issues
2656
+ );
2657
+ }
2303
2658
  checkAtlasStep(subExecutor, subStep.query, subPath, `${stepID}.${subID}`, issues);
2304
2659
  }
2305
2660
  });
@@ -3415,49 +3770,6 @@ function flowHasOrchestratorYieldEdge(flow) {
3415
3770
  }
3416
3771
  return false;
3417
3772
  }
3418
- var GO_INT64_FLOAT_LIMIT = 2 ** 63;
3419
- function decodeGoInt(v) {
3420
- if (typeof v !== "number" || Number.isNaN(v)) return null;
3421
- if (v >= GO_INT64_FLOAT_LIMIT) return null;
3422
- if (v === -Infinity) return -GO_INT64_FLOAT_LIMIT;
3423
- return Math.trunc(v);
3424
- }
3425
- function validateCampaignChildFlows(campaign, issues) {
3426
- const field = "campaign.child_flows";
3427
- const raw = campaign.child_flows;
3428
- if (raw !== void 0 && raw !== null && !Array.isArray(raw)) {
3429
- issues.error({ field, message: "campaign.child_flows must be a list", code: "invalid_type" });
3430
- return;
3431
- }
3432
- const entries = (Array.isArray(raw) ? raw : []).map((entry, i) => ({ entry, i })).filter(({ entry }) => entry !== null);
3433
- if (entries.length === 0) {
3434
- issues.error({
3435
- field,
3436
- message: "campaign requires at least one entry in child_flows",
3437
- code: "campaign_no_child_flows"
3438
- });
3439
- return;
3440
- }
3441
- for (const { entry, i } of entries) {
3442
- if (!isRecord(entry)) {
3443
- issues.error({
3444
- field: `${field}[${i}]`,
3445
- message: "each campaign.child_flows entry must be a mapping",
3446
- code: "invalid_type"
3447
- });
3448
- continue;
3449
- }
3450
- const id = issues.stringOf(entry, "flow_id", `${field}[${i}]`);
3451
- const name = issues.stringOf(entry, "flow_name", `${field}[${i}]`);
3452
- if (id !== null && id !== "" || name !== null && name !== "") continue;
3453
- issues.error({
3454
- field: `${field}[${i}]`,
3455
- message: `campaign.child_flows[${i}] needs a flow_id or a flow_name`,
3456
- code: "campaign_child_flow_no_id",
3457
- suggestion: "Name the child flow with flow_name, or pin it with flow_id"
3458
- });
3459
- }
3460
- }
3461
3773
  function validateOrchestratorCampaign(flow, issues, opts) {
3462
3774
  const hasOrchestrator = isRecord(flow.orchestrator);
3463
3775
  if (hasOrchestrator) {
@@ -3513,6 +3825,7 @@ function validateOrchestratorCampaign(flow, issues, opts) {
3513
3825
  const trigger = rawTrigger;
3514
3826
  const base = `orchestrator.triggers[${i}]`;
3515
3827
  const type = issues.stringOf(trigger, "type", base);
3828
+ if (type !== null && REMOVED_ORCHESTRATOR_TRIGGERS.has(type)) return;
3516
3829
  if (type === null || !ORCHESTRATOR_TRIGGERS.has(type)) {
3517
3830
  issues.error({
3518
3831
  field: `${base}.type`,
@@ -3543,66 +3856,27 @@ function validateOrchestratorCampaign(flow, issues, opts) {
3543
3856
  if (Array.isArray(orch.tools)) {
3544
3857
  orch.tools.forEach((raw, i) => {
3545
3858
  const tool = issues.stringAt(raw, `orchestrator.tools[${i}]`);
3546
- if (tool === null || ORCHESTRATOR_TOOLS.has(tool)) return;
3859
+ if (tool === null || ORCHESTRATOR_TOOLS.has(tool) || REMOVED_ORCHESTRATOR_TOOLS.has(tool)) {
3860
+ return;
3861
+ }
3547
3862
  const finding = {
3548
3863
  field: `orchestrator.tools[${i}]`,
3549
3864
  message: `Unrecognised orchestrator tool name '${tool}'`,
3550
3865
  code: "orchestrator_tool_unknown"
3551
3866
  };
3552
3867
  if (opts.strictRegistries) {
3553
- issues.error(finding);
3554
- } else {
3555
- issues.warn(finding);
3556
- }
3557
- });
3558
- }
3559
- if (orch.agentic === false) {
3560
- issues.warn({
3561
- field: "orchestrator.agentic",
3562
- message: "orchestrator is most useful with agentic: true",
3563
- code: "orchestrator_not_agentic"
3564
- });
3565
- }
3566
- }
3567
- if (flow.campaign !== void 0 && flow.campaign !== null) {
3568
- if (!hasOrchestrator) {
3569
- issues.error({
3570
- field: "campaign",
3571
- message: "campaign requires an orchestrator block",
3572
- code: "campaign_requires_orchestrator"
3573
- });
3574
- }
3575
- const campaign = flow.campaign;
3576
- if (isRecord(campaign)) {
3577
- const cap = campaign.max_credits_per_child;
3578
- if (cap !== void 0 && cap !== null) {
3579
- const decoded = decodeGoInt(cap);
3580
- if (decoded === null) {
3581
- issues.error({
3582
- field: "campaign.max_credits_per_child",
3583
- message: `campaign.max_credits_per_child must be a number of credits, got '${String(cap)}'`,
3584
- code: "invalid_type"
3585
- });
3586
- } else if (decoded < 0) {
3587
- issues.error({
3588
- field: "campaign.max_credits_per_child",
3589
- message: "campaign: max_credits_per_child must be >= 0",
3590
- code: "campaign_invalid_max_credits_per_child"
3591
- });
3868
+ issues.error(finding);
3869
+ } else {
3870
+ issues.warn(finding);
3592
3871
  }
3593
- }
3594
- validateCampaignChildFlows(campaign, issues);
3872
+ });
3595
3873
  }
3596
- const target = isRecord(campaign) ? issues.stringOf(campaign, "on_children_complete", "campaign") : null;
3597
- if (target !== null && target !== "") {
3598
- const steps = isRecord(flow.steps) ? flow.steps : {};
3599
- if (!(target in steps)) {
3600
- issues.error({
3601
- field: "campaign.on_children_complete",
3602
- message: `campaign.on_children_complete references unknown step '${target}'`,
3603
- code: "campaign_handoff_step_unknown"
3604
- });
3605
- }
3874
+ if (orch.agentic === false) {
3875
+ issues.warn({
3876
+ field: "orchestrator.agentic",
3877
+ message: "orchestrator is most useful with agentic: true",
3878
+ code: "orchestrator_not_agentic"
3879
+ });
3606
3880
  }
3607
3881
  }
3608
3882
  }
@@ -4730,10 +5004,6 @@ var CODE_UNKNOWN_YAML_KEY = "unknown_yaml_key";
4730
5004
  var KEY_BUDGET = "budget";
4731
5005
  var KEY_MAX_RETRIES = "max_retries";
4732
5006
  var REMOVED_IN = "v2.721.0";
4733
- var KEY_BUDGET_MAX_PER_CHILD = "budget_max_per_child";
4734
- var BUDGET_MAX_PER_CHILD_REMOVED_IN = "v2.728.0";
4735
- 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.`;
4736
- 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`.";
4737
5007
  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.`;
4738
5008
  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.";
4739
5009
  function maxRetriesMessage(owner) {
@@ -4761,15 +5031,6 @@ function validateRetiredKeys(flow, issues) {
4761
5031
  suggestion: MAX_RETRIES_SUGGESTION
4762
5032
  });
4763
5033
  }
4764
- const campaign = root.campaign;
4765
- if (isRecord(campaign) && declares(campaign, KEY_BUDGET_MAX_PER_CHILD)) {
4766
- issues.error({
4767
- field: `campaign.${KEY_BUDGET_MAX_PER_CHILD}`,
4768
- message: BUDGET_MAX_PER_CHILD_MESSAGE,
4769
- code: CODE_UNKNOWN_YAML_KEY,
4770
- suggestion: BUDGET_MAX_PER_CHILD_SUGGESTION
4771
- });
4772
- }
4773
5034
  const steps = flow.steps;
4774
5035
  if (!isRecord(steps)) return;
4775
5036
  for (const [stepID, rawStep] of Object.entries(steps)) {
@@ -5137,9 +5398,9 @@ function refuse(issues, stepId, key, field) {
5137
5398
  }
5138
5399
 
5139
5400
  // src/validators/goUrl.ts
5140
- var GO_SPACE = "\\t\\n\\v\\f\\r \\u0085\\u00A0\\u1680\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000";
5141
- var GO_TRIM_SPACE_RE = new RegExp(`^[${GO_SPACE}]+|[${GO_SPACE}]+$`, "g");
5142
- function goTrimSpace(s) {
5401
+ var GO_SPACE2 = "\\t\\n\\v\\f\\r \\u0085\\u00A0\\u1680\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000";
5402
+ var GO_TRIM_SPACE_RE = new RegExp(`^[${GO_SPACE2}]+|[${GO_SPACE2}]+$`, "g");
5403
+ function goTrimSpace2(s) {
5143
5404
  return s.replace(GO_TRIM_SPACE_RE, "");
5144
5405
  }
5145
5406
  function goToLower(s) {
@@ -5360,7 +5621,7 @@ function goQueryKeys(rawQuery) {
5360
5621
  return keys;
5361
5622
  }
5362
5623
  function goEndpointOrigin(raw, defaultPorts, protocolSeparator) {
5363
- const trimmed = goTrimSpace(raw);
5624
+ const trimmed = goTrimSpace2(raw);
5364
5625
  const u = goParseSchemeHost(trimmed);
5365
5626
  if (u === null || u.scheme === "" || u.host === "") {
5366
5627
  return goToLower(trimmed.replace(/\/+$/, ""));
@@ -5594,225 +5855,165 @@ function validateFamilies(flow, steps, issues) {
5594
5855
  });
5595
5856
  }
5596
5857
  }
5597
- var CODE_PARSE_FAILED = "orchestrator_exons_parse_failed";
5598
- var CODE_NO_PROVIDER = "orchestrator_exons_no_provider";
5599
- var CODE_RESOURCES = "exons_resources_unhonoured";
5600
- var CODE_TOOL_WITHHELD = "orchestrator_tool_withheld";
5601
- var EXONS_OPEN_DELIM = "{~";
5602
- var EXONS_LEGACY_CONFIG_OPEN = "{~exons.config~}";
5603
- var UTF8_BOM = "\uFEFF";
5604
- var MAX_ALIAS_COUNT2 = 100;
5605
- var UNREAD = {
5606
- parseError: null,
5607
- specRead: false,
5608
- hasSpec: false,
5609
- provider: "",
5610
- toolsAllowDeclared: false,
5611
- toolsAllow: [],
5612
- resources: []
5613
- };
5614
- var GO_SPACE2 = "\\t\\n\\v\\f\\r \\u0085\\u00a0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000";
5615
- var GO_TRIM_SPACE = new RegExp(`^[${GO_SPACE2}]+|[${GO_SPACE2}]+$`, "g");
5616
- function goTrimSpace2(s) {
5617
- return s.replace(GO_TRIM_SPACE, "");
5858
+
5859
+ // src/validators/removedSignalCampaign.ts
5860
+ var TEMPLATE_ACTION_OPEN3 = "{{";
5861
+ function fill(format, ...args) {
5862
+ let i = 0;
5863
+ return format.replace(/%[qs]/g, () => args[i++] ?? "");
5618
5864
  }
5619
- function extractExonsFrontmatter(source) {
5620
- const delim = EXONS.frontmatterDelimiter;
5621
- let start = source.startsWith(UTF8_BOM) ? source.slice(UTF8_BOM.length) : source;
5622
- start = start.replace(/^[ \t]+/, "");
5623
- if (start.startsWith(EXONS_LEGACY_CONFIG_OPEN)) {
5624
- return {
5625
- frontmatter: "",
5626
- hasFrontmatter: false,
5627
- parseError: "legacy JSON config block detected; use YAML frontmatter"
5628
- };
5629
- }
5630
- if (!start.startsWith(delim)) return { frontmatter: "", hasFrontmatter: false, parseError: null };
5631
- let rest = start.slice(delim.length);
5632
- if (rest.startsWith("\n")) rest = rest.slice(1);
5633
- else if (rest.startsWith("\r\n")) rest = rest.slice(2);
5634
- else return { frontmatter: "", hasFrontmatter: false, parseError: null };
5635
- let pos = 0;
5636
- while (pos <= rest.length) {
5637
- const line = rest.slice(pos);
5638
- if (line.startsWith(delim)) {
5639
- const after = line.slice(delim.length);
5640
- if (after === "" || after[0] === "\n" || after[0] === "\r") {
5641
- return {
5642
- frontmatter: goTrimSpace2(rest.slice(0, pos)),
5643
- hasFrontmatter: true,
5644
- parseError: null
5645
- };
5646
- }
5647
- }
5648
- const nl = line.indexOf("\n");
5649
- if (nl < 0) break;
5650
- pos += nl + 1;
5651
- }
5652
- return {
5653
- frontmatter: "",
5654
- hasFrontmatter: true,
5655
- parseError: "YAML frontmatter not properly closed"
5656
- };
5865
+ function stringEntries(m, key) {
5866
+ const raw = m[key];
5867
+ return Array.isArray(raw) ? raw.filter((v) => typeof v === "string") : [];
5657
5868
  }
5658
- function retagScalars(node) {
5659
- if (isMap(node)) {
5660
- for (const pair of node.items) {
5661
- retagScalars(pair.key);
5662
- retagScalars(pair.value);
5663
- }
5664
- } else if (isSeq(node)) {
5665
- for (const item of node.items) retagScalars(item);
5666
- } else if (isScalar(node) && (typeof node.value === "number" || typeof node.value === "boolean") && typeof node.source === "string") {
5667
- node.value = node.source;
5869
+ function triggerTypes(orch) {
5870
+ const raw = orch.triggers;
5871
+ if (!Array.isArray(raw)) return [];
5872
+ const out = [];
5873
+ for (const t of raw) {
5874
+ if (isRecord(t) && typeof t.type === "string") out.push(t.type);
5668
5875
  }
5876
+ return out;
5669
5877
  }
5670
- var DecodeError = class extends Error {
5671
- };
5672
- function goString(v) {
5673
- if (v === null || v === void 0) return "";
5674
- if (isString(v)) return v;
5675
- throw new DecodeError("not a string");
5676
- }
5677
- function goStringList(v) {
5678
- if (v === null || v === void 0) return null;
5679
- if (!isArray(v)) throw new DecodeError("not a list");
5680
- return v.filter((item) => item !== null).map(goString);
5681
- }
5682
- function goStruct(v) {
5683
- if (v === null || v === void 0) return null;
5684
- if (!isRecord(v)) throw new DecodeError("not a mapping");
5685
- return v;
5686
- }
5687
- function decodeSpecFields(frontmatter) {
5688
- let value;
5689
- try {
5690
- const docs = parseAllDocuments(frontmatter, { merge: true });
5691
- const doc = Array.isArray(docs) ? docs[0] : void 0;
5692
- if (doc === void 0) {
5693
- value = null;
5694
- } else {
5695
- if (doc.errors.length > 0) return null;
5696
- retagScalars(doc.contents);
5697
- value = doc.toJS({ maxAliasCount: MAX_ALIAS_COUNT2 });
5698
- }
5699
- } catch {
5700
- return null;
5701
- }
5702
- try {
5703
- const root = goStruct(value) ?? {};
5704
- const execution = goStruct(root.execution);
5705
- const tools = goStruct(root.tools);
5706
- const requirements = goStruct(root.requirements);
5707
- const allow = tools === null ? null : goStringList(tools.allow);
5708
- let resources = [];
5709
- if (requirements !== null) {
5710
- const raw = requirements.resources;
5711
- if (raw !== null && raw !== void 0) {
5712
- if (!isArray(raw)) throw new DecodeError("not a list");
5713
- resources = raw.filter((entry) => entry !== null).map((entry) => {
5714
- const r = goStruct(entry) ?? {};
5715
- return { ref: goString(r.ref), kind: goString(r.kind), scope: goString(r.scope) };
5716
- });
5717
- }
5718
- }
5719
- return {
5720
- provider: execution === null ? "" : goString(execution.provider),
5721
- toolsAllowDeclared: allow !== null,
5722
- toolsAllow: allow ?? [],
5723
- resources
5724
- };
5725
- } catch (e) {
5726
- if (e instanceof DecodeError) return null;
5727
- throw e;
5878
+ function namesIn(names, removed2) {
5879
+ const found = [];
5880
+ for (const name of names) {
5881
+ if (removed2.includes(name) && !found.includes(name)) found.push(name);
5728
5882
  }
5883
+ return found;
5729
5884
  }
5730
- function inspectExonsFrontmatter(source) {
5731
- const fm = extractExonsFrontmatter(source);
5732
- if (fm.parseError !== null) return { ...UNREAD, parseError: fm.parseError };
5733
- if (!fm.hasFrontmatter || fm.frontmatter === "") return { ...UNREAD, specRead: true };
5734
- if (fm.frontmatter.includes(EXONS_OPEN_DELIM)) return { ...UNREAD };
5735
- const fields = decodeSpecFields(fm.frontmatter);
5736
- if (fields === null) return { ...UNREAD };
5737
- return { parseError: null, specRead: true, hasSpec: true, ...fields };
5738
- }
5739
- function validateOrchestratorExons(flow, issues) {
5740
- const orch = flow.orchestrator;
5741
- if (!isRecord(orch)) return;
5742
- const source = issues.stringOf(orch, "exons", "orchestrator");
5743
- if (source === null || source === "") return;
5744
- const field = "orchestrator.exons";
5885
+ function orchestratorToolsAllow(orch) {
5886
+ const source = orch.exons;
5887
+ if (typeof source !== "string" || source === "") return [];
5745
5888
  const report = inspectExonsFrontmatter(source);
5746
- if (report.parseError !== null) {
5747
- issues.error({
5748
- field,
5749
- code: CODE_PARSE_FAILED,
5750
- message: `orchestrator: failed to parse exons spec: ${report.parseError}`
5751
- });
5752
- return;
5753
- }
5754
- if (!report.specRead) return;
5755
- if (!report.hasSpec) {
5756
- issues.error({
5757
- field,
5758
- code: CODE_PARSE_FAILED,
5759
- message: "orchestrator: failed to parse exons spec: the document has no frontmatter spec",
5760
- suggestion: "Start the document with a --- frontmatter block that sets execution.provider"
5761
- });
5762
- return;
5763
- }
5764
- if (report.provider === "") {
5765
- issues.error({
5766
- field,
5767
- code: CODE_NO_PROVIDER,
5768
- message: "orchestrator: exons spec must have execution.provider set"
5769
- });
5889
+ if (report.parseError !== null || !report.specRead || !report.toolsAllowDeclared) return [];
5890
+ return [...report.toolsAllow];
5891
+ }
5892
+ function validateRemovedOrchestratorSurfaces(flow, issues) {
5893
+ const sig = REMOVED_SIGNAL_SURFACES;
5894
+ const camp = REMOVED_CAMPAIGN_SURFACES;
5895
+ const orch = isRecord(flow.orchestrator) ? flow.orchestrator : null;
5896
+ const lists = orch === null ? [] : [
5897
+ ...["tools", "deny_tools", "require_approval_for"].map((key) => ({
5898
+ field: `orchestrator.${key}`,
5899
+ names: stringEntries(orch, key)
5900
+ })),
5901
+ { field: "orchestrator.exons", names: orchestratorToolsAllow(orch) }
5902
+ ];
5903
+ if (orch !== null) {
5904
+ if (orch.enable_signals === true) {
5905
+ issues.error({
5906
+ field: "orchestrator.enable_signals",
5907
+ code: sig.signalRemovedCode,
5908
+ message: sig.enableSignalsMessage,
5909
+ suggestion: sig.suggestion
5910
+ });
5911
+ }
5912
+ for (const list of lists) {
5913
+ const named = namesIn(list.names, sig.tools);
5914
+ if (named.length === 0) continue;
5915
+ issues.error({
5916
+ field: list.field,
5917
+ code: sig.signalRemovedCode,
5918
+ message: fill(sig.toolNamesMessageFormat, list.field, named.join(", ")),
5919
+ suggestion: sig.suggestion
5920
+ });
5921
+ }
5922
+ if (namesIn(triggerTypes(orch), sig.triggers).length > 0) {
5923
+ issues.error({
5924
+ field: "orchestrator.triggers",
5925
+ code: sig.flowModificationRemovedCode,
5926
+ message: sig.triggersMessage,
5927
+ suggestion: sig.suggestion
5928
+ });
5929
+ }
5770
5930
  }
5771
- if (report.resources.length > 0) {
5772
- const refs = report.resources.map((r) => `${JSON.stringify(r.ref)} (kind ${r.kind})`);
5931
+ const block = flow[camp.flowKey];
5932
+ if (block !== void 0 && block !== null) {
5773
5933
  issues.error({
5774
- field,
5775
- code: CODE_RESOURCES,
5776
- 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("; "),
5777
- suggestion: "Remove requirements.resources, and narrow the agent with tools.mcp_servers and tools.allow"
5778
- });
5779
- }
5780
- warnOrchestratorToolWithheld(flow, orch, report, issues);
5781
- }
5782
- function warnOrchestratorToolWithheld(flow, orch, report, issues) {
5783
- if (!report.toolsAllowDeclared) return;
5784
- const cfg = ORCHESTRATOR_TOOL_ALLOW;
5785
- const exempt = new Set(cfg.lifecycleTools);
5786
- const signals = typeof orch.enable_signals === "boolean" ? orch.enable_signals : cfg.enableSignalsDefault;
5787
- if (signals) for (const t of cfg.signalTools) exempt.add(t);
5788
- const allow = new Set(report.toolsAllow);
5789
- const withheld = (name) => !allow.has(name) && !exempt.has(name);
5790
- const warn = (message) => issues.warn({ field: cfg.warningField, code: CODE_TOOL_WITHHELD, message });
5791
- const named = [];
5792
- if (isArray(orch.tools)) {
5793
- orch.tools.forEach((raw, i) => {
5794
- const name = issues.stringAt(raw, `orchestrator.tools[${i}]`);
5795
- if (name !== null) named.push(name);
5934
+ field: camp.flowKey,
5935
+ code: camp.code,
5936
+ message: camp.blockMessage,
5937
+ suggestion: camp.suggestion
5796
5938
  });
5797
5939
  }
5798
- if (named.length > 0) {
5799
- for (const name of named) {
5800
- if (!withheld(name)) continue;
5801
- warn(
5802
- `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.`
5803
- );
5940
+ if (orch !== null) {
5941
+ for (const list of lists) {
5942
+ const named = namesIn(list.names, camp.tools);
5943
+ if (named.length === 0) continue;
5944
+ issues.error({
5945
+ field: list.field,
5946
+ code: camp.code,
5947
+ message: fill(camp.toolNamesMessageFormat, list.field, named.join(", ")),
5948
+ suggestion: camp.suggestion
5949
+ });
5950
+ }
5951
+ const triggers = namesIn(triggerTypes(orch), camp.triggers);
5952
+ if (triggers.length > 0) {
5953
+ issues.error({
5954
+ field: "orchestrator.triggers",
5955
+ code: camp.code,
5956
+ message: fill(camp.triggersMessageFormat, triggers.join(", ")),
5957
+ suggestion: camp.suggestion
5958
+ });
5804
5959
  }
5805
- return;
5806
5960
  }
5807
- if (withheld(cfg.askHumanTool)) {
5808
- warn(
5809
- `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.`
5810
- );
5961
+ const judgeStep = (executor, query, field, stepID) => {
5962
+ if (typeof executor !== "string" || executor === "") return;
5963
+ if (executor.includes(TEMPLATE_ACTION_OPEN3)) return;
5964
+ const scheme = removedExecutorScheme(executor);
5965
+ if (REMOVED_AGENT_SURFACES.schemes.has(scheme) || sig.stepSkipSchemes.has(scheme)) return;
5966
+ if (!isRecord(query)) return;
5967
+ const quoted = JSON.stringify(stepID);
5968
+ if (Object.prototype.hasOwnProperty.call(query, sig.flowModificationStepKey)) {
5969
+ issues.error({
5970
+ field,
5971
+ code: sig.flowModificationRemovedCode,
5972
+ stepId: stepID,
5973
+ message: fill(sig.flowModificationStepMessageFormat, quoted),
5974
+ suggestion: sig.suggestion
5975
+ });
5976
+ }
5977
+ if (scheme === sig.stepKeyScheme && Object.prototype.hasOwnProperty.call(query, sig.stepKey)) {
5978
+ issues.error({
5979
+ field,
5980
+ code: sig.signalRemovedCode,
5981
+ stepId: stepID,
5982
+ message: fill(sig.stepKeyMessageFormat, quoted),
5983
+ suggestion: sig.suggestion
5984
+ });
5985
+ }
5986
+ };
5987
+ const steps = flow.steps;
5988
+ if (isRecord(steps)) {
5989
+ for (const stepID of Object.keys(steps).sort()) {
5990
+ const step = steps[stepID];
5991
+ if (!isRecord(step)) continue;
5992
+ judgeStep(step.executor, step.query, `steps.${stepID}`, stepID);
5993
+ const loop = step.loop;
5994
+ if (!isRecord(loop) || !Array.isArray(loop.steps)) continue;
5995
+ loop.steps.forEach((sub, i) => {
5996
+ if (!isRecord(sub)) return;
5997
+ const subID = issues.stringOf(sub, "id", `steps.${stepID}.loop.steps[${i}]`) ?? String(i);
5998
+ judgeStep(
5999
+ sub.executor,
6000
+ sub.query,
6001
+ `steps.${stepID}.loop.steps[${i}]`,
6002
+ `${stepID}.${subID}`
6003
+ );
6004
+ });
6005
+ }
5811
6006
  }
5812
- if (isRecord(flow.campaign) && !cfg.campaignTools.some((name) => !withheld(name))) {
5813
- warn(
5814
- `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.`
5815
- );
6007
+ const ignored = (field) => issues.warn({
6008
+ field,
6009
+ code: sig.removedKeyIgnoredCode,
6010
+ message: fill(sig.ignoredKeyMessageFormat, field),
6011
+ suggestion: sig.ignoredKeySuggestion
6012
+ });
6013
+ if (orch !== null && orch.enable_signals === false) ignored("orchestrator.enable_signals");
6014
+ const checkpoint = flow.checkpoint;
6015
+ if (isRecord(checkpoint) && checkpoint.pre_dfm !== void 0 && checkpoint.pre_dfm !== null) {
6016
+ ignored("checkpoint.pre_dfm");
5816
6017
  }
5817
6018
  }
5818
6019
 
@@ -5829,7 +6030,7 @@ function sprintf(format, ...args) {
5829
6030
  });
5830
6031
  }
5831
6032
  function literalOrEmpty(s) {
5832
- const t = goTrimSpace2(s);
6033
+ const t = goTrimSpace(s);
5833
6034
  return t.includes(TEMPLATE_ACTION_OPEN) ? "" : t;
5834
6035
  }
5835
6036
  function euOnlyResidency(flow, issues) {
@@ -5844,7 +6045,7 @@ function euOnlyResidency(flow, issues) {
5844
6045
  }
5845
6046
  if (codes.length === 0) return null;
5846
6047
  const eu = new Set(COMPLIANCE_RESIDENCY.euResidencyCodes);
5847
- return codes.every((c) => eu.has(goTrimSpace2(c).toUpperCase())) ? codes : null;
6048
+ return codes.every((c) => eu.has(goTrimSpace(c).toUpperCase())) ? codes : null;
5848
6049
  }
5849
6050
  function aiProvider(executor) {
5850
6051
  const prefix = COMPLIANCE_RESIDENCY.aiProtocol + COMPLIANCE_RESIDENCY.protocolSeparator;
@@ -5860,7 +6061,7 @@ function isRegionLane(provider) {
5860
6061
  }
5861
6062
  function nonBlankString(m, key) {
5862
6063
  const v = own2(m, key);
5863
- return typeof v === "string" && goTrimSpace2(v) !== "";
6064
+ return typeof v === "string" && goTrimSpace(v) !== "";
5864
6065
  }
5865
6066
  function queryEnforcesRegion(query) {
5866
6067
  const v = own2(query, COMPLIANCE_RESIDENCY.enforceRegionParam);
@@ -5872,7 +6073,7 @@ function flowDirectsEndpoint(flow, provider, issues) {
5872
6073
  const inst = own2(own2(flow, "executor_config"), key);
5873
6074
  if (!isRecord(inst)) return false;
5874
6075
  const baseUrl = issues.stringOf(inst, "base_url", `executor_config.${key}`);
5875
- return baseUrl !== null && goTrimSpace2(baseUrl) !== "";
6076
+ return baseUrl !== null && goTrimSpace(baseUrl) !== "";
5876
6077
  }
5877
6078
  function callPinsEndpoint(flow, provider, query, issues) {
5878
6079
  if (nonBlankString(query, COMPLIANCE_RESIDENCY.regionParam)) return true;
@@ -5945,7 +6146,7 @@ function warnOrchestrator(flow, issues, warn) {
5945
6146
  const provider = literalOrEmpty(report.provider);
5946
6147
  if (!isRegionLane(provider)) return;
5947
6148
  const region = issues.stringOf(orch, "region", "orchestrator");
5948
- if (region !== null && goTrimSpace2(region) !== "") return;
6149
+ if (region !== null && goTrimSpace(region) !== "") return;
5949
6150
  if (orch.enforce_region === true) return;
5950
6151
  if (flowDirectsEndpoint(flow, provider, issues)) return;
5951
6152
  warn(COMPLIANCE_RESIDENCY.siteOrchestrator, COMPLIANCE_RESIDENCY.orchestratorField, void 0, provider);
@@ -6111,7 +6312,7 @@ function asFileRef(v) {
6111
6312
  };
6112
6313
  }
6113
6314
  function isTextualMediaType(mediaType) {
6114
- const base = goToLower(goTrimSpace((mediaType.split(";")[0] ?? "").toString()));
6315
+ const base = goToLower(goTrimSpace2((mediaType.split(";")[0] ?? "").toString()));
6115
6316
  if (base.startsWith("text/")) return true;
6116
6317
  if (base.endsWith("+json") || base.endsWith("+xml")) return true;
6117
6318
  return TEXTUAL_MEDIA_BASES.has(base);
@@ -6215,7 +6416,7 @@ var Walker = class _Walker {
6215
6416
  }
6216
6417
  }
6217
6418
  const title = this.s(ex, "title", path);
6218
- if (goTrimSpace(title) === "") {
6419
+ if (goTrimSpace2(title) === "") {
6219
6420
  this.err(
6220
6421
  "example_title_missing",
6221
6422
  `${path}.title`,
@@ -6232,7 +6433,7 @@ var Walker = class _Walker {
6232
6433
  }
6233
6434
  }
6234
6435
  const guidance = this.s(ex, "guidance", path);
6235
- if (goTrimSpace(guidance) === "") {
6436
+ if (goTrimSpace2(guidance) === "") {
6236
6437
  this.warn(
6237
6438
  "example_guidance_missing",
6238
6439
  `${path}.guidance`,
@@ -6659,7 +6860,7 @@ var Walker = class _Walker {
6659
6860
  }
6660
6861
  const rubric = this.s(e, "rubric", path);
6661
6862
  if (rubric !== "") {
6662
- if (goTrimSpace(rubric) === "") {
6863
+ if (goTrimSpace2(rubric) === "") {
6663
6864
  this.err(
6664
6865
  "example_rubric_empty",
6665
6866
  `${path}.rubric`,
@@ -6714,7 +6915,7 @@ var Walker = class _Walker {
6714
6915
  this.err("example_must_not_invalid", `${path}.must_not`, mustNotMsg);
6715
6916
  }
6716
6917
  mustNot.forEach((item, j) => {
6717
- if (goTrimSpace(item) === "" || runeCount(item) > L.maxMustNotRunes) {
6918
+ if (goTrimSpace2(item) === "" || runeCount(item) > L.maxMustNotRunes) {
6718
6919
  this.err("example_must_not_invalid", `${path}.must_not[${j}]`, mustNotMsg);
6719
6920
  }
6720
6921
  });
@@ -7272,6 +7473,7 @@ function validateParsed(flow, opts, sources) {
7272
7473
  const f = flow;
7273
7474
  validateBasicStructure(f, issues);
7274
7475
  validateRetiredKeys(f, issues);
7476
+ validateRemovedFlowKeys(f, issues);
7275
7477
  validateExecutors(f, issues);
7276
7478
  validateQuerySchema(f, issues);
7277
7479
  validateResponseExpectations(f, issues);
@@ -7295,6 +7497,7 @@ function validateParsed(flow, opts, sources) {
7295
7497
  validateCredentialEndpointPairing(f, issues);
7296
7498
  validateExamples(f, issues);
7297
7499
  validateOrchestratorExons(f, issues);
7500
+ validateRemovedOrchestratorSurfaces(f, issues);
7298
7501
  validateResidencyEnforcement(f, issues);
7299
7502
  validateUnknownKeys(f, issues);
7300
7503
  const templateStats = validateTemplates(f, issues, opts);