agentopology 0.9.2 → 0.10.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.
Files changed (168) hide show
  1. package/README.md +17 -19
  2. package/dist/__tests__/e2e/action-guard.test.d.ts +13 -0
  3. package/dist/__tests__/e2e/action-guard.test.d.ts.map +1 -0
  4. package/dist/__tests__/e2e/action-guard.test.js +299 -0
  5. package/dist/__tests__/e2e/action-guard.test.js.map +1 -0
  6. package/dist/__tests__/e2e/conformance.test.d.ts +13 -0
  7. package/dist/__tests__/e2e/conformance.test.d.ts.map +1 -0
  8. package/dist/__tests__/e2e/conformance.test.js +109 -0
  9. package/dist/__tests__/e2e/conformance.test.js.map +1 -0
  10. package/dist/__tests__/e2e/contract-drift.test.d.ts +11 -0
  11. package/dist/__tests__/e2e/contract-drift.test.d.ts.map +1 -0
  12. package/dist/__tests__/e2e/contract-drift.test.js +142 -0
  13. package/dist/__tests__/e2e/contract-drift.test.js.map +1 -0
  14. package/dist/__tests__/e2e/guard-check.test.d.ts +9 -0
  15. package/dist/__tests__/e2e/guard-check.test.d.ts.map +1 -0
  16. package/dist/__tests__/e2e/guard-check.test.js +316 -0
  17. package/dist/__tests__/e2e/guard-check.test.js.map +1 -0
  18. package/dist/__tests__/e2e/guard-target.test.d.ts +11 -0
  19. package/dist/__tests__/e2e/guard-target.test.d.ts.map +1 -0
  20. package/dist/__tests__/e2e/guard-target.test.js +198 -0
  21. package/dist/__tests__/e2e/guard-target.test.js.map +1 -0
  22. package/dist/__tests__/e2e/inventory.test.d.ts +18 -0
  23. package/dist/__tests__/e2e/inventory.test.d.ts.map +1 -0
  24. package/dist/__tests__/e2e/inventory.test.js +187 -0
  25. package/dist/__tests__/e2e/inventory.test.js.map +1 -0
  26. package/dist/__tests__/e2e/matcher.test.d.ts +14 -0
  27. package/dist/__tests__/e2e/matcher.test.d.ts.map +1 -0
  28. package/dist/__tests__/e2e/matcher.test.js +145 -0
  29. package/dist/__tests__/e2e/matcher.test.js.map +1 -0
  30. package/dist/__tests__/e2e/step-check.test.d.ts +9 -0
  31. package/dist/__tests__/e2e/step-check.test.d.ts.map +1 -0
  32. package/dist/__tests__/e2e/step-check.test.js +158 -0
  33. package/dist/__tests__/e2e/step-check.test.js.map +1 -0
  34. package/dist/__tests__/e2e/strength.test.d.ts +17 -0
  35. package/dist/__tests__/e2e/strength.test.d.ts.map +1 -0
  36. package/dist/__tests__/e2e/strength.test.js +130 -0
  37. package/dist/__tests__/e2e/strength.test.js.map +1 -0
  38. package/dist/__tests__/e2e/verified-on.test.d.ts +2 -0
  39. package/dist/__tests__/e2e/verified-on.test.d.ts.map +1 -0
  40. package/dist/__tests__/e2e/verified-on.test.js +57 -0
  41. package/dist/__tests__/e2e/verified-on.test.js.map +1 -0
  42. package/dist/__tests__/open-findings.test.js +1 -0
  43. package/dist/__tests__/open-findings.test.js.map +1 -1
  44. package/dist/bindings/__tests__/bindings.test.js +6 -686
  45. package/dist/bindings/__tests__/bindings.test.js.map +1 -1
  46. package/dist/bindings/__tests__/defaults-agreement.test.js +0 -12
  47. package/dist/bindings/__tests__/defaults-agreement.test.js.map +1 -1
  48. package/dist/bindings/__tests__/hook-command.test.js +2 -2
  49. package/dist/bindings/__tests__/hook-command.test.js.map +1 -1
  50. package/dist/bindings/__tests__/mod-plugin.test.js +2 -2
  51. package/dist/bindings/__tests__/mod-plugin.test.js.map +1 -1
  52. package/dist/bindings/claude-code.d.ts +45 -1
  53. package/dist/bindings/claude-code.d.ts.map +1 -1
  54. package/dist/bindings/claude-code.js +181 -15
  55. package/dist/bindings/claude-code.js.map +1 -1
  56. package/dist/bindings/index.d.ts +5 -5
  57. package/dist/bindings/index.d.ts.map +1 -1
  58. package/dist/bindings/index.js +9 -16
  59. package/dist/bindings/index.js.map +1 -1
  60. package/dist/bindings/types.d.ts +6 -1
  61. package/dist/bindings/types.d.ts.map +1 -1
  62. package/dist/cli/__tests__/cli.test.js +25 -0
  63. package/dist/cli/__tests__/cli.test.js.map +1 -1
  64. package/dist/cli/index.js +222 -8
  65. package/dist/cli/index.js.map +1 -1
  66. package/dist/conformance/claude-code.d.ts +99 -0
  67. package/dist/conformance/claude-code.d.ts.map +1 -0
  68. package/dist/conformance/claude-code.js +242 -0
  69. package/dist/conformance/claude-code.js.map +1 -0
  70. package/dist/conformance/extract.d.ts +70 -0
  71. package/dist/conformance/extract.d.ts.map +1 -0
  72. package/dist/conformance/extract.js +185 -0
  73. package/dist/conformance/extract.js.map +1 -0
  74. package/dist/docs/content.d.ts.map +1 -1
  75. package/dist/docs/content.js +57 -32
  76. package/dist/docs/content.js.map +1 -1
  77. package/dist/guard/check.d.ts +53 -0
  78. package/dist/guard/check.d.ts.map +1 -0
  79. package/dist/guard/check.js +366 -0
  80. package/dist/guard/check.js.map +1 -0
  81. package/dist/guard/target.d.ts +45 -0
  82. package/dist/guard/target.d.ts.map +1 -0
  83. package/dist/guard/target.js +168 -0
  84. package/dist/guard/target.js.map +1 -0
  85. package/dist/import/__tests__/import.test.js +0 -554
  86. package/dist/import/__tests__/import.test.js.map +1 -1
  87. package/dist/import/index.d.ts +0 -1
  88. package/dist/import/index.d.ts.map +1 -1
  89. package/dist/import/index.js +1 -7
  90. package/dist/import/index.js.map +1 -1
  91. package/dist/import/serializer.d.ts.map +1 -1
  92. package/dist/import/serializer.js +41 -0
  93. package/dist/import/serializer.js.map +1 -1
  94. package/dist/index.d.ts +0 -4
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +0 -4
  97. package/dist/index.js.map +1 -1
  98. package/dist/inventory/index.d.ts +65 -0
  99. package/dist/inventory/index.d.ts.map +1 -0
  100. package/dist/inventory/index.js +272 -0
  101. package/dist/inventory/index.js.map +1 -0
  102. package/dist/parser/ast.d.ts +46 -0
  103. package/dist/parser/ast.d.ts.map +1 -1
  104. package/dist/parser/ast.js.map +1 -1
  105. package/dist/parser/index.d.ts +6 -1
  106. package/dist/parser/index.d.ts.map +1 -1
  107. package/dist/parser/index.js +66 -4
  108. package/dist/parser/index.js.map +1 -1
  109. package/dist/parser/known-fields.d.ts +3 -0
  110. package/dist/parser/known-fields.d.ts.map +1 -1
  111. package/dist/parser/known-fields.js +5 -3
  112. package/dist/parser/known-fields.js.map +1 -1
  113. package/dist/parser/ledger.d.ts +36 -0
  114. package/dist/parser/ledger.d.ts.map +1 -0
  115. package/dist/parser/ledger.js +72 -0
  116. package/dist/parser/ledger.js.map +1 -0
  117. package/dist/parser/run.d.ts +6 -0
  118. package/dist/parser/run.d.ts.map +1 -1
  119. package/dist/parser/run.js +10 -0
  120. package/dist/parser/run.js.map +1 -1
  121. package/dist/parser/validator.d.ts.map +1 -1
  122. package/dist/parser/validator.js +255 -1
  123. package/dist/parser/validator.js.map +1 -1
  124. package/dist/scaffold/manifest.d.ts.map +1 -1
  125. package/dist/scaffold/manifest.js +0 -8
  126. package/dist/scaffold/manifest.js.map +1 -1
  127. package/dist/scaffold/merge.d.ts.map +1 -1
  128. package/dist/scaffold/merge.js +1 -3
  129. package/dist/scaffold/merge.js.map +1 -1
  130. package/dist/step/check.d.ts +56 -0
  131. package/dist/step/check.d.ts.map +1 -0
  132. package/dist/step/check.js +125 -0
  133. package/dist/step/check.js.map +1 -0
  134. package/dist/sync/index.d.ts +0 -2
  135. package/dist/sync/index.d.ts.map +1 -1
  136. package/dist/sync/index.js +0 -109
  137. package/dist/sync/index.js.map +1 -1
  138. package/package.json +3 -1
  139. package/spec/claude-code/README.md +19 -0
  140. package/spec/claude-code/hooks-contract.2.1.295.json +143 -0
  141. package/spec/claude-code/hooks-contract.json +142 -0
  142. package/spec/grammar.md +45 -7
  143. package/spec/testing.md +15 -0
  144. package/spec/validation.md +11 -2
  145. package/dist/__tests__/live/zz-debug.test.d.ts +0 -2
  146. package/dist/__tests__/live/zz-debug.test.d.ts.map +0 -1
  147. package/dist/__tests__/live/zz-debug.test.js +0 -4
  148. package/dist/__tests__/live/zz-debug.test.js.map +0 -1
  149. package/dist/bindings/copilot-cli.d.ts +0 -20
  150. package/dist/bindings/copilot-cli.d.ts.map +0 -1
  151. package/dist/bindings/copilot-cli.js +0 -1516
  152. package/dist/bindings/copilot-cli.js.map +0 -1
  153. package/dist/bindings/gemini-cli.d.ts +0 -18
  154. package/dist/bindings/gemini-cli.d.ts.map +0 -1
  155. package/dist/bindings/gemini-cli.js +0 -1318
  156. package/dist/bindings/gemini-cli.js.map +0 -1
  157. package/dist/bindings/kiro.d.ts +0 -17
  158. package/dist/bindings/kiro.d.ts.map +0 -1
  159. package/dist/bindings/kiro.js +0 -1615
  160. package/dist/bindings/kiro.js.map +0 -1
  161. package/dist/bindings/openclaw.d.ts +0 -18
  162. package/dist/bindings/openclaw.d.ts.map +0 -1
  163. package/dist/bindings/openclaw.js +0 -1947
  164. package/dist/bindings/openclaw.js.map +0 -1
  165. package/dist/import/openclaw.d.ts +0 -70
  166. package/dist/import/openclaw.d.ts.map +0 -1
  167. package/dist/import/openclaw.js +0 -1060
  168. package/dist/import/openclaw.js.map +0 -1
@@ -796,558 +796,4 @@ topology complex : [pipeline, fan-out] {
796
796
  expect(lead.outputs.confidence).toContain("high");
797
797
  });
798
798
  });
799
- // ===========================================================================
800
- // OpenClaw importer tests
801
- // ===========================================================================
802
- import { parseOpenClawJson, parseSoulMd, parseAgentsMd, parseSkillFile, parseMemoryMd, parseCronJobsJson, importOpenClaw, } from "../openclaw.js";
803
- // ---------------------------------------------------------------------------
804
- // OpenClaw test data
805
- // ---------------------------------------------------------------------------
806
- const OPENCLAW_JSON = `{
807
- "agents": {
808
- "defaults": {
809
- "workspace": "~/.openclaw/workspace",
810
- "model": { "primary": "openrouter/google/gemini-2.5-flash" }
811
- },
812
- "list": [
813
- {
814
- "id": "commander",
815
- "name": "Commander",
816
- "model": { "primary": "openrouter/google/gemini-2.5-flash" },
817
- "workspace": "~/.openclaw/workspace-commander",
818
- "agentDir": "~/.openclaw/config/agents/commander/agent",
819
- "subagents": { "allowAgents": ["researcher", "worker"] }
820
- },
821
- {
822
- "id": "researcher",
823
- "name": "Researcher",
824
- "model": {
825
- "primary": "openrouter/google/gemini-2.5-flash",
826
- "fallbacks": ["openrouter/google/gemini-2.5-flash-lite"]
827
- },
828
- "workspace": "~/.openclaw/workspace-researcher",
829
- "agentDir": "~/.openclaw/config/agents/researcher/agent"
830
- }
831
- ]
832
- },
833
- "gateway": { "port": 18789, "auth": { "mode": "token", "token": "\${OPENCLAW_AUTH_TOKEN}" } },
834
- "models": {
835
- "providers": {
836
- "openrouter": {
837
- "apiKey": "\${OPENROUTER_API_KEY}",
838
- "baseUrl": "https://openrouter.ai/api/v1",
839
- "models": [
840
- { "id": "openrouter/google/gemini-2.5-flash", "name": "Gemini 2.5 Flash" }
841
- ]
842
- }
843
- }
844
- },
845
- "tools": { "allow": [], "deny": ["dangerous-tool"], "agentToAgent": { "enabled": true } },
846
- "env": { "OPENROUTER_API_KEY": "\${OPENROUTER_API_KEY}", "MY_VAR": "hello" }
847
- }`;
848
- const SOUL_MD = `> A test topology for unit testing.
849
-
850
- ## Identity
851
- - Name: Test Topology
852
- - Version: 2.0.0
853
- - Patterns: supervisor, fan-out
854
-
855
- ## Mission
856
- A test topology for unit testing.
857
-
858
- ## Roles
859
- ### Strategy
860
- Plan and coordinate
861
-
862
- ### Research
863
- Find information
864
-
865
- ## Ethical Guardrails
866
- - Tool restrictions: none
867
-
868
- ## Parameters
869
-
870
- - **campaign**: string (required)
871
- - **mode**: string = intelligence
872
-
873
- ## Interface
874
-
875
- - Entry: commander
876
- - Exit: commander
877
-
878
- ## Triggers
879
-
880
- ### launch
881
- Pattern: \`/launch <CAMPAIGN>\`
882
- Argument: CAMPAIGN
883
-
884
- ### status
885
- Pattern: \`/status <CAMPAIGN>\`
886
- Argument: CAMPAIGN
887
-
888
- ## Environment
889
-
890
- - \`OPENROUTER_API_KEY\`: \${OPENROUTER_API_KEY}
891
- - \`MY_VAR\`: hello
892
- `;
893
- const AGENTS_MD = `# Sub-Agent Definitions
894
-
895
- ## Pipeline Overview
896
- Test Topology uses a supervisor + fan-out topology.
897
-
898
- ## Agents
899
-
900
- ### Commander
901
- - **Model:** openrouter/google/gemini-2.5-flash
902
- - **Phase:** 1
903
- - **Role:** Plan and coordinate
904
- - **Tools allowed:** Read, Write, Bash
905
- - **Tools denied:** none
906
- - **Reads:** workspace
907
- - **Writes:** workspace
908
- - **Skills:** campaign-management
909
- - **Max turns:** 100
910
-
911
- #### Instructions
912
-
913
- You are the commander.
914
-
915
- Do important things.
916
- - **Maximum execution time:** 30m
917
- - **Retry:** max 2 attempts
918
-
919
- ### Researcher
920
- - **Model:** openrouter/google/gemini-2.5-flash
921
- - **Phase:** 2
922
- - **Role:** Find information
923
- - **Tools allowed:** Read, Write
924
- - **Tools denied:** none
925
- - **Reads:** workspace, domains
926
- - **Writes:** workspace
927
- - **Rate limit:** 30/hour
928
- - **Circuit breaker:** threshold=5, window=30m, cooldown=1h
929
- - **On failure:** continue
930
-
931
- #### Instructions
932
-
933
- You are the researcher.
934
- - **Maximum execution time:** 45m
935
- - **Retry:** max 3 attempts
936
-
937
- ## Group Chat Coordination
938
-
939
- ### Strategy Session
940
- - **Members:** commander, researcher
941
- - **Speaker selection:** round-robin
942
- - **Max rounds:** 5
943
- - **Termination:** Commander confirms the plan
944
- - **Description:** Strategy planning session
945
- - **Timeout:** 30m
946
-
947
- ## Flow
948
-
949
- ### Execution Order
950
- 1. commander -> strategy-session
951
- 2. strategy-session -> researcher
952
- 3. researcher -x-> commander (max 3 iterations)
953
-
954
- ### Bounded Loops
955
- - researcher <-> commander: max 3 iterations (prevents infinite loops)
956
-
957
- ### Error Handling
958
- - On error in researcher: route to commander
959
-
960
- ## Gates
961
-
962
- ### Quality Check
963
- - **After:** researcher
964
- - **Before:** commander
965
- - **On failure:** bounce-back
966
- - **Behavior:** blocking
967
- - **Checks:** output-valid, data-complete
968
-
969
- ## Schedule
970
-
971
- ### Morning Run
972
- - **Agent:** commander
973
- - **Cron:** \`0 9 * * 0-4\`
974
- - **Enabled:** true
975
-
976
- ## Hooks
977
-
978
- ### Block Destructive
979
- - **Event:** PreToolUse
980
- - **Run:** exit 2
981
- - **Type:** command
982
-
983
- ### Log Completion
984
- - **Event:** AgentStop
985
- - **Run:** scripts/log.sh
986
- - **Type:** command
987
-
988
- ---
989
-
990
- ## Test Topology -- Topology Overview
991
-
992
- ### Architecture
993
- - Patterns: supervisor, fan-out
994
- - Agents: 2
995
- - Gates: 1
996
- - Version: 2.0.0
997
- `;
998
- const SKILL_MD = `---
999
- name: campaign-management
1000
- description: Manage campaigns end to end
1001
- user-invocable: true
1002
- ---
1003
-
1004
- This skill manages campaign lifecycle.
1005
- `;
1006
- const MEMORY_MD = `# Memory Structure
1007
-
1008
- ## Workspace
1009
- - Path: \`campaigns/{CAMPAIGN}/\`
1010
- - Structure: messages, outbox, logs
1011
-
1012
- ## Domains
1013
- - Path: \`campaigns/{CAMPAIGN}/intel/\`
1014
- `;
1015
- // ---------------------------------------------------------------------------
1016
- // parseOpenClawJson
1017
- // ---------------------------------------------------------------------------
1018
- describe("parseOpenClawJson", () => {
1019
- it("extracts agent list with models", () => {
1020
- const result = parseOpenClawJson(OPENCLAW_JSON);
1021
- expect(result.agents).toHaveLength(2);
1022
- expect(result.agents[0].id).toBe("commander");
1023
- expect(result.agents[0].model).toBe("openrouter/google/gemini-2.5-flash");
1024
- expect(result.agents[0].subagents).toEqual(["researcher", "worker"]);
1025
- });
1026
- it("extracts fallback chains", () => {
1027
- const result = parseOpenClawJson(OPENCLAW_JSON);
1028
- expect(result.agents[1].fallbacks).toEqual(["openrouter/google/gemini-2.5-flash-lite"]);
1029
- });
1030
- it("extracts providers", () => {
1031
- const result = parseOpenClawJson(OPENCLAW_JSON);
1032
- expect(result.providers).toHaveLength(1);
1033
- expect(result.providers[0].name).toBe("openrouter");
1034
- expect(result.providers[0].apiKey).toBe("${OPENROUTER_API_KEY}");
1035
- expect(result.providers[0].baseUrl).toBe("https://openrouter.ai/api/v1");
1036
- expect(result.providerNames).toEqual(["openrouter"]);
1037
- });
1038
- it("extracts tools settings and env", () => {
1039
- const result = parseOpenClawJson(OPENCLAW_JSON);
1040
- expect(result.settings.deny).toEqual(["dangerous-tool"]);
1041
- expect(result.env.MY_VAR).toBe("hello");
1042
- });
1043
- it("extracts gateway extensions", () => {
1044
- const result = parseOpenClawJson(OPENCLAW_JSON);
1045
- expect(result.extensions["gateway-port"]).toBe(18789);
1046
- expect(result.extensions["auth-mode"]).toBe("token");
1047
- });
1048
- });
1049
- // ---------------------------------------------------------------------------
1050
- // parseSoulMd
1051
- // ---------------------------------------------------------------------------
1052
- describe("parseSoulMd", () => {
1053
- it("extracts identity", () => {
1054
- const result = parseSoulMd(SOUL_MD);
1055
- expect(result.name).toBe("test-topology");
1056
- expect(result.version).toBe("2.0.0");
1057
- expect(result.patterns).toEqual(["supervisor", "fan-out"]);
1058
- });
1059
- it("extracts description from quote block", () => {
1060
- const result = parseSoulMd(SOUL_MD);
1061
- expect(result.description).toBe("A test topology for unit testing.");
1062
- });
1063
- it("extracts roles", () => {
1064
- const result = parseSoulMd(SOUL_MD);
1065
- expect(result.roles.strategy).toBe("Plan and coordinate");
1066
- expect(result.roles.research).toBe("Find information");
1067
- });
1068
- it("extracts parameters", () => {
1069
- const result = parseSoulMd(SOUL_MD);
1070
- expect(result.params).toHaveLength(2);
1071
- expect(result.params[0]).toEqual({ name: "campaign", type: "string", default: undefined, required: true });
1072
- expect(result.params[1]).toEqual({ name: "mode", type: "string", default: "intelligence", required: false });
1073
- });
1074
- it("extracts interface endpoints", () => {
1075
- const result = parseSoulMd(SOUL_MD);
1076
- expect(result.interfaceEndpoints).toEqual({ entry: "commander", exit: "commander" });
1077
- });
1078
- it("extracts triggers", () => {
1079
- const result = parseSoulMd(SOUL_MD);
1080
- expect(result.triggers).toHaveLength(2);
1081
- expect(result.triggers[0]).toEqual({ name: "launch", pattern: "/launch <CAMPAIGN>", argument: "CAMPAIGN" });
1082
- expect(result.triggers[1]).toEqual({ name: "status", pattern: "/status <CAMPAIGN>", argument: "CAMPAIGN" });
1083
- });
1084
- it("extracts environment variables", () => {
1085
- const result = parseSoulMd(SOUL_MD);
1086
- expect(result.env["OPENROUTER_API_KEY"]).toBe("${OPENROUTER_API_KEY}");
1087
- expect(result.env["MY_VAR"]).toBe("hello");
1088
- });
1089
- });
1090
- // ---------------------------------------------------------------------------
1091
- // parseAgentsMd
1092
- // ---------------------------------------------------------------------------
1093
- describe("parseAgentsMd", () => {
1094
- it("extracts agents with basic properties", () => {
1095
- const result = parseAgentsMd(AGENTS_MD);
1096
- expect(result.agents).toHaveLength(2);
1097
- const cmd = result.agents[0];
1098
- expect(cmd.id).toBe("commander");
1099
- expect(cmd.phase).toBe(1);
1100
- expect(cmd.tools).toEqual(["Read", "Write", "Bash"]);
1101
- expect(cmd.reads).toEqual(["workspace"]);
1102
- expect(cmd.writes).toEqual(["workspace"]);
1103
- expect(cmd.skills).toEqual(["campaign-management"]);
1104
- expect(cmd.maxTurns).toBe(100);
1105
- });
1106
- it("extracts agent prompt (instructions)", () => {
1107
- const result = parseAgentsMd(AGENTS_MD);
1108
- const cmd = result.agents[0];
1109
- expect(cmd.prompt).toContain("You are the commander.");
1110
- expect(cmd.prompt).toContain("Do important things.");
1111
- });
1112
- it("extracts trailing properties after instructions", () => {
1113
- const result = parseAgentsMd(AGENTS_MD);
1114
- const cmd = result.agents[0];
1115
- expect(cmd.timeout).toBe("30m");
1116
- expect(cmd.retry).toBe(2);
1117
- });
1118
- it("extracts rate limit and circuit breaker", () => {
1119
- const result = parseAgentsMd(AGENTS_MD);
1120
- const researcher = result.agents[1];
1121
- expect(researcher.rateLimit).toBe("30/hour");
1122
- expect(researcher.circuitBreaker).toEqual({
1123
- threshold: 5,
1124
- window: "30m",
1125
- cooldown: "1h",
1126
- });
1127
- expect(researcher.onFail).toBe("continue");
1128
- });
1129
- it("extracts group chat", () => {
1130
- const result = parseAgentsMd(AGENTS_MD);
1131
- expect(result.groups).toHaveLength(1);
1132
- const group = result.groups[0];
1133
- expect(group.id).toBe("strategy-session");
1134
- expect(group.members).toEqual(["commander", "researcher"]);
1135
- expect(group.speakerSelection).toBe("round-robin");
1136
- expect(group.maxRounds).toBe(5);
1137
- expect(group.termination).toBe("Commander confirms the plan");
1138
- expect(group.timeout).toBe("30m");
1139
- });
1140
- it("extracts flow edges including error edges", () => {
1141
- const result = parseAgentsMd(AGENTS_MD);
1142
- expect(result.edges).toHaveLength(3);
1143
- expect(result.edges[0]).toMatchObject({ from: "commander", to: "strategy-session" });
1144
- expect(result.edges[0].isError).toBeUndefined();
1145
- expect(result.edges[1]).toMatchObject({ from: "strategy-session", to: "researcher" });
1146
- expect(result.edges[1].isError).toBeUndefined();
1147
- expect(result.edges[2]).toMatchObject({ from: "researcher", to: "commander", isError: true, maxIterations: 3 });
1148
- });
1149
- it("extracts gates", () => {
1150
- const result = parseAgentsMd(AGENTS_MD);
1151
- expect(result.gates).toHaveLength(1);
1152
- const gate = result.gates[0];
1153
- expect(gate.id).toBe("quality-check");
1154
- expect(gate.after).toBe("researcher");
1155
- expect(gate.before).toBe("commander");
1156
- expect(gate.onFail).toBe("bounce-back");
1157
- expect(gate.behavior).toBe("blocking");
1158
- expect(gate.checks).toEqual(["output-valid", "data-complete"]);
1159
- });
1160
- it("extracts schedule", () => {
1161
- const result = parseAgentsMd(AGENTS_MD);
1162
- expect(result.schedules).toHaveLength(1);
1163
- expect(result.schedules[0]).toEqual({
1164
- id: "morning-run",
1165
- agent: "commander",
1166
- cron: "0 9 * * 0-4",
1167
- enabled: true,
1168
- });
1169
- });
1170
- it("extracts hooks", () => {
1171
- const result = parseAgentsMd(AGENTS_MD);
1172
- expect(result.hooks).toHaveLength(2);
1173
- expect(result.hooks[0].name).toBe("block-destructive");
1174
- expect(result.hooks[0].on).toBe("PreToolUse");
1175
- expect(result.hooks[0].run).toBe("exit 2");
1176
- expect(result.hooks[1].name).toBe("log-completion");
1177
- expect(result.hooks[1].on).toBe("AgentStop");
1178
- });
1179
- });
1180
- // ---------------------------------------------------------------------------
1181
- // parseSkillFile
1182
- // ---------------------------------------------------------------------------
1183
- describe("parseSkillFile (openclaw)", () => {
1184
- it("extracts skill from frontmatter", () => {
1185
- const skill = parseSkillFile("campaign-management", SKILL_MD);
1186
- expect(skill.id).toBe("campaign-management");
1187
- expect(skill.description).toBe("Manage campaigns end to end");
1188
- expect(skill.userInvocable).toBe(true);
1189
- });
1190
- it("handles skill without frontmatter", () => {
1191
- const skill = parseSkillFile("my-skill", "This skill does things.");
1192
- expect(skill.id).toBe("my-skill");
1193
- expect(skill.description).toBe("This skill does things.");
1194
- expect(skill.userInvocable).toBeUndefined();
1195
- });
1196
- });
1197
- // ---------------------------------------------------------------------------
1198
- // parseMemoryMd
1199
- // ---------------------------------------------------------------------------
1200
- describe("parseMemoryMd", () => {
1201
- it("extracts workspace and domains", () => {
1202
- const result = parseMemoryMd(MEMORY_MD);
1203
- const ws = result.memory.workspace;
1204
- expect(ws.path).toBe("campaigns/{CAMPAIGN}/");
1205
- expect(ws.structure).toEqual(["messages", "outbox", "logs"]);
1206
- const domains = result.memory.domains;
1207
- expect(domains.path).toBe("campaigns/{CAMPAIGN}/intel/");
1208
- });
1209
- });
1210
- // ---------------------------------------------------------------------------
1211
- // parseCronJobsJson
1212
- // ---------------------------------------------------------------------------
1213
- describe("parseCronJobsJson", () => {
1214
- it("parses cron jobs array", () => {
1215
- const json = JSON.stringify({
1216
- jobs: [
1217
- { id: "morning", agentId: "commander", schedule: { expr: "0 9 * * *" }, enabled: true },
1218
- { id: "evening", agentId: "reporter", cron: "0 18 * * *", enabled: false },
1219
- ],
1220
- });
1221
- const jobs = parseCronJobsJson(json);
1222
- expect(jobs).toHaveLength(2);
1223
- expect(jobs[0]).toEqual({ id: "morning", agent: "commander", cron: "0 9 * * *", enabled: true });
1224
- expect(jobs[1]).toEqual({ id: "evening", agent: "reporter", cron: "0 18 * * *", enabled: false });
1225
- });
1226
- });
1227
- // ---------------------------------------------------------------------------
1228
- // importOpenClaw (integration)
1229
- // ---------------------------------------------------------------------------
1230
- describe("importOpenClaw", () => {
1231
- const minimalFiles = [
1232
- { path: "openclaw.json", content: OPENCLAW_JSON },
1233
- { path: "SOUL.md", content: SOUL_MD },
1234
- { path: "AGENTS.md", content: AGENTS_MD },
1235
- { path: "MEMORY.md", content: MEMORY_MD },
1236
- { path: "skills/campaign-management/SKILL.md", content: SKILL_MD },
1237
- ];
1238
- it("produces a complete TopologyAST", () => {
1239
- const ast = importOpenClaw(minimalFiles, "test-topology");
1240
- expect(ast.topology.name).toBe("test-topology");
1241
- expect(ast.topology.version).toBe("2.0.0");
1242
- expect(ast.topology.patterns).toEqual(["supervisor", "fan-out"]);
1243
- expect(ast.topology.description).toBe("A test topology for unit testing.");
1244
- });
1245
- it("imports agents with reverse-mapped models", () => {
1246
- const ast = importOpenClaw(minimalFiles, "test-topology");
1247
- const agents = ast.nodes.filter((n) => n.type === "agent");
1248
- expect(agents).toHaveLength(2);
1249
- const cmd = agents.find((a) => a.id === "commander");
1250
- expect(cmd.model).toBe("google/gemini-2.5-flash");
1251
- expect(cmd.phase).toBe(1);
1252
- expect(cmd.timeout).toBe("30m");
1253
- const researcher = agents.find((a) => a.id === "researcher");
1254
- expect(researcher.model).toBe("google/gemini-2.5-flash");
1255
- expect(researcher.fallbackChain).toEqual(["google/gemini-2.5-flash-lite"]);
1256
- });
1257
- it("imports roles, matching agents to role keys", () => {
1258
- const ast = importOpenClaw(minimalFiles, "test-topology");
1259
- expect(ast.roles.strategy).toBe("Plan and coordinate");
1260
- expect(ast.roles.research).toBe("Find information");
1261
- const cmd = ast.nodes.find((n) => n.id === "commander");
1262
- expect(cmd.role).toBe("strategy");
1263
- });
1264
- it("imports groups, gates, edges, hooks, schedules", () => {
1265
- const ast = importOpenClaw(minimalFiles, "test-topology");
1266
- const groups = ast.nodes.filter((n) => n.type === "group");
1267
- expect(groups).toHaveLength(1);
1268
- const gates = ast.nodes.filter((n) => n.type === "gate");
1269
- expect(gates).toHaveLength(1);
1270
- expect(ast.edges).toHaveLength(3);
1271
- expect(ast.hooks).toHaveLength(2);
1272
- expect(ast.schedules).toHaveLength(1);
1273
- });
1274
- it("imports skills", () => {
1275
- const ast = importOpenClaw(minimalFiles, "test-topology");
1276
- expect(ast.skills).toHaveLength(1);
1277
- expect(ast.skills[0].id).toBe("campaign-management");
1278
- expect(ast.skills[0].userInvocable).toBe(true);
1279
- });
1280
- it("imports params, triggers, interface endpoints", () => {
1281
- const ast = importOpenClaw(minimalFiles, "test-topology");
1282
- expect(ast.params).toHaveLength(2);
1283
- expect(ast.triggers).toHaveLength(2);
1284
- expect(ast.interfaceEndpoints).toEqual({ entry: "commander", exit: "commander" });
1285
- });
1286
- it("imports providers with reverse-mapped models", () => {
1287
- const ast = importOpenClaw(minimalFiles, "test-topology");
1288
- expect(ast.providers).toHaveLength(1);
1289
- expect(ast.providers[0].name).toBe("openrouter");
1290
- expect(ast.providers[0].default).toBe(true);
1291
- });
1292
- it("imports env and settings", () => {
1293
- const ast = importOpenClaw(minimalFiles, "test-topology");
1294
- expect(ast.env["MY_VAR"]).toBe("hello");
1295
- expect(ast.settings.deny).toEqual(["dangerous-tool"]);
1296
- });
1297
- it("imports memory", () => {
1298
- const ast = importOpenClaw(minimalFiles, "test-topology");
1299
- expect(ast.memory.workspace?.path).toBe("campaigns/{CAMPAIGN}/");
1300
- });
1301
- });
1302
- // ---------------------------------------------------------------------------
1303
- // importFromPlatform — OpenClaw dispatch
1304
- // ---------------------------------------------------------------------------
1305
- describe("importFromPlatform (openclaw)", () => {
1306
- it("dispatches to openclaw", () => {
1307
- const files = [
1308
- { path: "openclaw.json", content: OPENCLAW_JSON },
1309
- { path: "SOUL.md", content: SOUL_MD },
1310
- { path: "AGENTS.md", content: AGENTS_MD },
1311
- ];
1312
- const atSource = importFromPlatform(files, "openclaw", "test");
1313
- // SOUL.md Identity overrides the topologyName param
1314
- expect(atSource).toContain("topology test-topology");
1315
- expect(atSource).toContain("agent commander");
1316
- expect(atSource).toContain("agent researcher");
1317
- });
1318
- it("falls back to topologyName when SOUL.md is missing", () => {
1319
- const files = [
1320
- { path: "openclaw.json", content: OPENCLAW_JSON },
1321
- { path: "AGENTS.md", content: AGENTS_MD },
1322
- ];
1323
- const atSource = importFromPlatform(files, "openclaw", "my-project");
1324
- expect(atSource).toContain("topology my-project");
1325
- });
1326
- it("generates parseable .at output", () => {
1327
- const files = [
1328
- { path: "openclaw.json", content: OPENCLAW_JSON },
1329
- { path: "SOUL.md", content: SOUL_MD },
1330
- { path: "AGENTS.md", content: AGENTS_MD },
1331
- { path: "MEMORY.md", content: MEMORY_MD },
1332
- { path: "skills/campaign-management/SKILL.md", content: SKILL_MD },
1333
- ];
1334
- const atSource = importFromPlatform(files, "openclaw", "test");
1335
- const ast = parse(atSource);
1336
- expect(ast.topology.name).toBe("test-topology");
1337
- expect(ast.nodes.filter((n) => n.type === "agent").length).toBe(2);
1338
- });
1339
- it("validates with zero errors (warnings allowed)", () => {
1340
- const files = [
1341
- { path: "openclaw.json", content: OPENCLAW_JSON },
1342
- { path: "SOUL.md", content: SOUL_MD },
1343
- { path: "AGENTS.md", content: AGENTS_MD },
1344
- { path: "MEMORY.md", content: MEMORY_MD },
1345
- { path: "skills/campaign-management/SKILL.md", content: SKILL_MD },
1346
- ];
1347
- const atSource = importFromPlatform(files, "openclaw", "test");
1348
- const ast = parse(atSource);
1349
- const errors = validate(ast).filter((e) => e.level === "error");
1350
- expect(errors).toEqual([]);
1351
- });
1352
- });
1353
799
  //# sourceMappingURL=import.test.js.map