@polderlabs/bizar 6.3.0 → 7.0.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 (245) hide show
  1. package/.claude/hooks/sessionend-recall.mjs +3 -3
  2. package/.claude/hooks/worker-suggest.mjs +110 -0
  3. package/.claude/settings.json +9 -0
  4. package/bizar-dash/dist/assets/{EnvVarsSection-B58aiJiE.js → EnvVarsSection-CW6fWKds.js} +3 -3
  5. package/bizar-dash/dist/assets/{EnvVarsSection-B58aiJiE.js.map → EnvVarsSection-CW6fWKds.js.map} +1 -1
  6. package/bizar-dash/dist/assets/MobileChat-D7zRhU9K.js +1 -0
  7. package/bizar-dash/dist/assets/{MobileChat-BJrqwVDd.js.map → MobileChat-D7zRhU9K.js.map} +1 -1
  8. package/bizar-dash/dist/assets/MobileSettings-K-sNsn_r.js +1 -0
  9. package/bizar-dash/dist/assets/{MobileSettings-CEQNJNLJ.js.map → MobileSettings-K-sNsn_r.js.map} +1 -1
  10. package/bizar-dash/dist/assets/{Toast-Cpl6-E63.js → Toast-WTxc3m2g.js} +1 -1
  11. package/bizar-dash/dist/assets/{Toast-Cpl6-E63.js.map → Toast-WTxc3m2g.js.map} +1 -1
  12. package/bizar-dash/dist/assets/flow-BpU0-gK2.js +1 -0
  13. package/bizar-dash/dist/assets/flow-BpU0-gK2.js.map +1 -0
  14. package/bizar-dash/dist/assets/fuzzy-l0sNRNKZ.js +1 -0
  15. package/bizar-dash/dist/assets/fuzzy-l0sNRNKZ.js.map +1 -0
  16. package/bizar-dash/dist/assets/{icons-Do5N2jmX.js → icons-B0AKnYI3.js} +98 -103
  17. package/bizar-dash/dist/assets/icons-B0AKnYI3.js.map +1 -0
  18. package/bizar-dash/dist/assets/main-C-Md3DaY.css +1 -0
  19. package/bizar-dash/dist/assets/main-CO8fIxQK.js +18 -0
  20. package/bizar-dash/dist/assets/main-CO8fIxQK.js.map +1 -0
  21. package/bizar-dash/dist/assets/markdown-w2bDDkAH.js +1 -0
  22. package/bizar-dash/dist/assets/{markdown-tOLaD6nm.js.map → markdown-w2bDDkAH.js.map} +1 -1
  23. package/bizar-dash/dist/assets/mobile-DXYCA0hC.js +1 -0
  24. package/bizar-dash/dist/assets/{mobile-DYCHcUpq.js.map → mobile-DXYCA0hC.js.map} +1 -1
  25. package/bizar-dash/dist/assets/mobile-layout-DSmtNPja.js +2 -0
  26. package/bizar-dash/dist/assets/mobile-layout-DSmtNPja.js.map +1 -0
  27. package/bizar-dash/dist/assets/react-vendor-BnuhLJ6X.css +1 -0
  28. package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js +46 -0
  29. package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js.map +1 -0
  30. package/bizar-dash/dist/assets/useSlashCommands-3DFLOfpk.js +2 -0
  31. package/bizar-dash/dist/assets/useSlashCommands-3DFLOfpk.js.map +1 -0
  32. package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js +45 -0
  33. package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js.map +1 -0
  34. package/bizar-dash/dist/index.html +12 -10
  35. package/bizar-dash/dist/mobile.html +7 -4
  36. package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  37. package/bizar-dash/src/server/api.mjs +11 -0
  38. package/bizar-dash/src/server/memory-consolidator.mjs +218 -0
  39. package/bizar-dash/src/server/memory-distillation.mjs +296 -0
  40. package/bizar-dash/src/server/routes/distill.mjs +129 -0
  41. package/bizar-dash/src/server/routes/goal-planner.mjs +236 -0
  42. package/bizar-dash/src/web/App.tsx +4 -0
  43. package/bizar-dash/src/web/components/Tag.tsx +18 -5
  44. package/bizar-dash/src/web/components/Topbar.tsx +3 -0
  45. package/bizar-dash/src/web/components/agents/CommunicationLog.tsx +126 -0
  46. package/bizar-dash/src/web/components/agents/DependencyGraph.tsx +163 -0
  47. package/bizar-dash/src/web/components/agents/QualityGates.tsx +123 -0
  48. package/bizar-dash/src/web/components/agents/RealTimeEventLog.tsx +127 -0
  49. package/bizar-dash/src/web/components/agents/RoutingDecisions.test.tsx +148 -0
  50. package/bizar-dash/src/web/components/agents/RoutingDecisions.tsx +160 -0
  51. package/bizar-dash/src/web/components/goals/GoalInput.tsx +86 -0
  52. package/bizar-dash/src/web/components/goals/PlanVisualization.tsx +111 -0
  53. package/bizar-dash/src/web/lib/goapPlanner.ts +614 -0
  54. package/bizar-dash/src/web/lib/types.ts +25 -0
  55. package/bizar-dash/src/web/styles/agents-redesign.css +311 -0
  56. package/bizar-dash/src/web/styles/main.css +251 -0
  57. package/bizar-dash/src/web/styles/tasks-redesign.css +231 -0
  58. package/bizar-dash/src/web/ui/controls/Button.tsx +77 -0
  59. package/bizar-dash/src/web/ui/controls/Checkbox.tsx +95 -0
  60. package/bizar-dash/src/web/ui/controls/IconButton.tsx +66 -0
  61. package/bizar-dash/src/web/ui/controls/Kbd.tsx +24 -0
  62. package/bizar-dash/src/web/ui/controls/NumberInput.tsx +77 -0
  63. package/bizar-dash/src/web/ui/controls/RadioGroup.tsx +109 -0
  64. package/bizar-dash/src/web/ui/controls/SearchInput.tsx +91 -0
  65. package/bizar-dash/src/web/ui/controls/Select.tsx +92 -0
  66. package/bizar-dash/src/web/ui/controls/Slider.tsx +70 -0
  67. package/bizar-dash/src/web/ui/controls/TextInput.tsx +67 -0
  68. package/bizar-dash/src/web/ui/controls/Toggle.tsx +79 -0
  69. package/bizar-dash/src/web/ui/controls/controls.css +454 -0
  70. package/bizar-dash/src/web/ui/controls/index.ts +33 -0
  71. package/bizar-dash/src/web/ui/data/BarChart.tsx +60 -0
  72. package/bizar-dash/src/web/ui/data/DataTable.tsx +216 -0
  73. package/bizar-dash/src/web/ui/data/EmptyState.tsx +60 -0
  74. package/bizar-dash/src/web/ui/data/ErrorState.tsx +56 -0
  75. package/bizar-dash/src/web/ui/data/KeyValueList.tsx +106 -0
  76. package/bizar-dash/src/web/ui/data/LoadingState.tsx +65 -0
  77. package/bizar-dash/src/web/ui/data/Sparkline.tsx +87 -0
  78. package/bizar-dash/src/web/ui/data/StatTile.tsx +87 -0
  79. package/bizar-dash/src/web/ui/data/data.css +535 -0
  80. package/bizar-dash/src/web/ui/data/index.ts +31 -0
  81. package/bizar-dash/src/web/ui/feedback/Badge.tsx +49 -0
  82. package/bizar-dash/src/web/ui/feedback/Dialog.tsx +209 -0
  83. package/bizar-dash/src/web/ui/feedback/ProgressBar.tsx +69 -0
  84. package/bizar-dash/src/web/ui/feedback/StatusDot.tsx +50 -0
  85. package/bizar-dash/src/web/ui/feedback/Toast.tsx +200 -0
  86. package/bizar-dash/src/web/ui/feedback/Tooltip.tsx +126 -0
  87. package/bizar-dash/src/web/ui/feedback/feedback.css +314 -0
  88. package/bizar-dash/src/web/ui/feedback/index.ts +43 -0
  89. package/bizar-dash/src/web/ui/index.ts +56 -0
  90. package/bizar-dash/src/web/ui/layout/AppShell.tsx +51 -0
  91. package/bizar-dash/src/web/ui/layout/Breadcrumbs.tsx +97 -0
  92. package/bizar-dash/src/web/ui/layout/Panel.tsx +73 -0
  93. package/bizar-dash/src/web/ui/layout/PanelHeader.tsx +39 -0
  94. package/bizar-dash/src/web/ui/layout/Sidebar.tsx +131 -0
  95. package/bizar-dash/src/web/ui/layout/Tabs.tsx +133 -0
  96. package/bizar-dash/src/web/ui/layout/Topbar.tsx +42 -0
  97. package/bizar-dash/src/web/ui/layout/index.ts +33 -0
  98. package/bizar-dash/src/web/ui/layout/layout.css +413 -0
  99. package/bizar-dash/src/web/ui/navigation/CommandPalette.tsx +199 -0
  100. package/bizar-dash/src/web/ui/navigation/NavGroup.tsx +95 -0
  101. package/bizar-dash/src/web/ui/navigation/NavLink.tsx +93 -0
  102. package/bizar-dash/src/web/ui/navigation/index.ts +17 -0
  103. package/bizar-dash/src/web/ui/navigation/navigation.css +343 -0
  104. package/bizar-dash/src/web/ui/primitives/Box.tsx +91 -0
  105. package/bizar-dash/src/web/ui/primitives/Grid.tsx +49 -0
  106. package/bizar-dash/src/web/ui/primitives/Inline.tsx +55 -0
  107. package/bizar-dash/src/web/ui/primitives/Separator.tsx +46 -0
  108. package/bizar-dash/src/web/ui/primitives/Stack.tsx +54 -0
  109. package/bizar-dash/src/web/ui/primitives/VisuallyHidden.tsx +26 -0
  110. package/bizar-dash/src/web/ui/primitives/index.ts +23 -0
  111. package/bizar-dash/src/web/ui/primitives/primitives.css +183 -0
  112. package/bizar-dash/src/web/ui/styles/globals.css +86 -0
  113. package/bizar-dash/src/web/ui/styles/reset.css +125 -0
  114. package/bizar-dash/src/web/ui/styles/tokens.css +195 -0
  115. package/bizar-dash/src/web/ui/theme/ThemeProvider.tsx +142 -0
  116. package/bizar-dash/src/web/ui/theme/tokens.ts +131 -0
  117. package/bizar-dash/src/web/ui/theme/useTheme.ts +22 -0
  118. package/bizar-dash/src/web/ui/utils/cx.ts +42 -0
  119. package/bizar-dash/src/web/views/Agents.tsx +506 -266
  120. package/bizar-dash/src/web/views/GoalPlanner.tsx +158 -0
  121. package/bizar-dash/src/web/views/Overview.tsx +996 -448
  122. package/bizar-dash/src/web/views/Tasks.tsx +277 -273
  123. package/bizar-dash/tests/communication-log.test.tsx +67 -0
  124. package/bizar-dash/tests/dep-graph.test.tsx +40 -0
  125. package/bizar-dash/tests/event-log.test.tsx +61 -0
  126. package/bizar-dash/tests/goal-input.test.tsx +72 -0
  127. package/bizar-dash/tests/goap-planner.test.ts +68 -0
  128. package/bizar-dash/tests/quality-gates.test.tsx +59 -0
  129. package/bizar-dash/tests/setup.ts +31 -1
  130. package/bizar-dash/tests/ui/controls/Button.test.tsx +50 -0
  131. package/bizar-dash/tests/ui/controls/Checkbox.test.tsx +44 -0
  132. package/bizar-dash/tests/ui/controls/IconButton.test.tsx +37 -0
  133. package/bizar-dash/tests/ui/controls/Kbd.test.tsx +39 -0
  134. package/bizar-dash/tests/ui/controls/NumberInput.test.tsx +56 -0
  135. package/bizar-dash/tests/ui/controls/RadioGroup.test.tsx +58 -0
  136. package/bizar-dash/tests/ui/controls/SearchInput.test.tsx +52 -0
  137. package/bizar-dash/tests/ui/controls/Select.test.tsx +66 -0
  138. package/bizar-dash/tests/ui/controls/Slider.test.tsx +41 -0
  139. package/bizar-dash/tests/ui/controls/TextInput.test.tsx +44 -0
  140. package/bizar-dash/tests/ui/controls/Toggle.test.tsx +39 -0
  141. package/bizar-dash/tests/ui/data/BarChart.test.tsx +55 -0
  142. package/bizar-dash/tests/ui/data/DataTable.test.tsx +112 -0
  143. package/bizar-dash/tests/ui/data/EmptyState.test.tsx +57 -0
  144. package/bizar-dash/tests/ui/data/ErrorState.test.tsx +47 -0
  145. package/bizar-dash/tests/ui/data/KeyValueList.test.tsx +59 -0
  146. package/bizar-dash/tests/ui/data/LoadingState.test.tsx +37 -0
  147. package/bizar-dash/tests/ui/data/Sparkline.test.tsx +56 -0
  148. package/bizar-dash/tests/ui/data/StatTile.test.tsx +69 -0
  149. package/bizar-dash/tests/ui/feedback/Badge.test.tsx +41 -0
  150. package/bizar-dash/tests/ui/feedback/Dialog.test.tsx +99 -0
  151. package/bizar-dash/tests/ui/feedback/ProgressBar.test.tsx +66 -0
  152. package/bizar-dash/tests/ui/feedback/StatusDot.test.tsx +41 -0
  153. package/bizar-dash/tests/ui/feedback/Toast.test.tsx +124 -0
  154. package/bizar-dash/tests/ui/feedback/Tooltip.test.tsx +102 -0
  155. package/bizar-dash/tests/ui/layout/AppShell.test.tsx +50 -0
  156. package/bizar-dash/tests/ui/layout/Breadcrumbs.test.tsx +85 -0
  157. package/bizar-dash/tests/ui/layout/Panel.test.tsx +52 -0
  158. package/bizar-dash/tests/ui/layout/PanelHeader.test.tsx +32 -0
  159. package/bizar-dash/tests/ui/layout/Sidebar.test.tsx +95 -0
  160. package/bizar-dash/tests/ui/layout/Tabs.test.tsx +87 -0
  161. package/bizar-dash/tests/ui/layout/Topbar.test.tsx +41 -0
  162. package/bizar-dash/tests/ui/navigation/CommandPalette.test.tsx +105 -0
  163. package/bizar-dash/tests/ui/navigation/NavGroup.test.tsx +55 -0
  164. package/bizar-dash/tests/ui/navigation/NavLink.test.tsx +61 -0
  165. package/bizar-dash/tests/ui/primitives/Box.test.tsx +49 -0
  166. package/bizar-dash/tests/ui/primitives/Grid.test.tsx +37 -0
  167. package/bizar-dash/tests/ui/primitives/Inline.test.tsx +33 -0
  168. package/bizar-dash/tests/ui/primitives/Separator.test.tsx +34 -0
  169. package/bizar-dash/tests/ui/primitives/Stack.test.tsx +45 -0
  170. package/bizar-dash/tests/ui/primitives/VisuallyHidden.test.tsx +27 -0
  171. package/bizar-dash/tests/views/Agents.test.tsx +359 -0
  172. package/bizar-dash/tests/views/Overview.test.tsx +413 -0
  173. package/bizar-dash/tests/views/Tasks.test.tsx +250 -0
  174. package/bizar-dash/vitest.config.ts +3 -1
  175. package/cli/__tests__/cost-gate.test.mjs +301 -0
  176. package/cli/__tests__/feature-list-bridge.test.mjs +371 -0
  177. package/cli/bin.mjs +38 -0
  178. package/cli/commands/claim.mjs +282 -0
  179. package/cli/commands/cost.mjs +279 -0
  180. package/cli/cost-gate.mjs +501 -0
  181. package/cli/feature-list-bridge.mjs +400 -0
  182. package/cli/memory-constants.mjs +17 -0
  183. package/cli/worker-dispatcher.mjs +259 -0
  184. package/cli/worker-dispatcher.test.mjs +204 -0
  185. package/config/trigger-patterns.json +186 -0
  186. package/package.json +2 -2
  187. package/packages/sdk/package.json +1 -1
  188. package/packages/sdk/src/agent-registry.ts +304 -0
  189. package/packages/sdk/src/consensus/byzantine.ts +587 -0
  190. package/packages/sdk/src/consensus/index.ts +147 -0
  191. package/packages/sdk/src/consensus/queen.ts +173 -0
  192. package/packages/sdk/src/consensus/types.ts +199 -0
  193. package/packages/sdk/src/federation/audit.ts +156 -0
  194. package/packages/sdk/src/federation/budget.ts +269 -0
  195. package/packages/sdk/src/federation/envelope.ts +144 -0
  196. package/packages/sdk/src/federation/hmac.ts +160 -0
  197. package/packages/sdk/src/federation/index.ts +443 -0
  198. package/packages/sdk/src/federation/pii.ts +241 -0
  199. package/packages/sdk/src/federation/policy.ts +112 -0
  200. package/packages/sdk/src/federation/trust.ts +146 -0
  201. package/packages/sdk/src/index.ts +67 -0
  202. package/packages/sdk/src/mcp/server.ts +351 -1
  203. package/packages/sdk/src/router/codemod-intent.ts +125 -0
  204. package/packages/sdk/src/router/index.ts +247 -0
  205. package/packages/sdk/src/router/memory-distillation-shim.mjs +18 -0
  206. package/packages/sdk/src/router/memory-distillation.ts +249 -0
  207. package/packages/sdk/src/router/model-router.ts +235 -0
  208. package/packages/sdk/src/router/q-learning-router.ts +304 -0
  209. package/packages/sdk/src/swarm-topology.ts +279 -0
  210. package/packages/sdk/tests/agent-registry.test.ts +285 -0
  211. package/packages/sdk/tests/consensus.test.ts +376 -0
  212. package/packages/sdk/tests/federation/audit.test.ts +164 -0
  213. package/packages/sdk/tests/federation/budget.test.ts +189 -0
  214. package/packages/sdk/tests/federation/envelope.test.ts +120 -0
  215. package/packages/sdk/tests/federation/hmac.test.ts +194 -0
  216. package/packages/sdk/tests/federation/orchestrator.test.ts +176 -0
  217. package/packages/sdk/tests/federation/pii.test.ts +106 -0
  218. package/packages/sdk/tests/federation/policy.test.ts +140 -0
  219. package/packages/sdk/tests/federation/trust.test.ts +124 -0
  220. package/packages/sdk/tests/mcp-tools.test.ts +204 -0
  221. package/packages/sdk/tests/memory-distillation.test.mjs +186 -0
  222. package/packages/sdk/tests/model-router.test.mjs +108 -0
  223. package/packages/sdk/tests/q-learning-router.test.mjs +100 -0
  224. package/packages/sdk/tests/router-orchestrator.test.mjs +172 -0
  225. package/packages/sdk/tests/router.test.mjs +91 -0
  226. package/packages/sdk/tests/sdk.test.mjs +26 -2
  227. package/packages/sdk/tests/swarm-topology.test.ts +258 -0
  228. package/scripts/bh-full-e2e.mjs +11 -15
  229. package/bizar-dash/dist/assets/MobileChat-BJrqwVDd.js +0 -1
  230. package/bizar-dash/dist/assets/MobileSettings-CEQNJNLJ.js +0 -1
  231. package/bizar-dash/dist/assets/icons-Do5N2jmX.js.map +0 -1
  232. package/bizar-dash/dist/assets/main-IvfQAOfy.js +0 -18
  233. package/bizar-dash/dist/assets/main-IvfQAOfy.js.map +0 -1
  234. package/bizar-dash/dist/assets/main-ietCEg_R.css +0 -1
  235. package/bizar-dash/dist/assets/markdown-tOLaD6nm.js +0 -1
  236. package/bizar-dash/dist/assets/mobile-DYCHcUpq.js +0 -1
  237. package/bizar-dash/dist/assets/mobile-layout-CBHjpwsb.js +0 -2
  238. package/bizar-dash/dist/assets/mobile-layout-CBHjpwsb.js.map +0 -1
  239. package/bizar-dash/dist/assets/react-vendor-Dn4wqh4Z.js +0 -40
  240. package/bizar-dash/dist/assets/react-vendor-Dn4wqh4Z.js.map +0 -1
  241. package/bizar-dash/dist/assets/useSlashCommands-Bd7_FA6U.js +0 -2
  242. package/bizar-dash/dist/assets/useSlashCommands-Bd7_FA6U.js.map +0 -1
  243. package/bizar-dash/dist/assets/vendor-C843201K.js +0 -29
  244. package/bizar-dash/dist/assets/vendor-C843201K.js.map +0 -1
  245. package/packages/sdk/package-lock.json +0 -32
@@ -31,6 +31,11 @@ describe("SDK build", () => {
31
31
  "memory/index.js", "memory/index.d.ts",
32
32
  "mcp/server.js", "mcp/server.d.ts",
33
33
  "mcp/bin.js", "mcp/bin.d.ts",
34
+ // v6.4.0 — F-033 Self-Learning (codemod + bandit + distillation).
35
+ "router/codemod-intent.js", "router/codemod-intent.d.ts",
36
+ "router/model-router.js", "router/model-router.d.ts",
37
+ "router/q-learning-router.js", "router/q-learning-router.d.ts",
38
+ "router/memory-distillation.js", "router/memory-distillation.d.ts",
34
39
  ]) {
35
40
  expect(existsSync(join(distDir, f))).toBe(true);
36
41
  }
@@ -71,10 +76,10 @@ describe("SDK module surface", () => {
71
76
  expect(typeof mod.DEFAULT_MEMORY_VAULT).toBe("string");
72
77
  });
73
78
 
74
- test("MCP server module exposes ≥13 BIZAR_TOOLS", async () => {
79
+ test("MCP server module exposes ≥20 BIZAR_TOOLS (13 core + 4 swarm + 3 self-learning)", async () => {
75
80
  const mod = await import("../dist/mcp/server.js");
76
81
  expect(Array.isArray(mod.BIZAR_TOOLS)).toBe(true);
77
- expect(mod.BIZAR_TOOLS.length).toBeGreaterThanOrEqual(13);
82
+ expect(mod.BIZAR_TOOLS.length).toBeGreaterThanOrEqual(20);
78
83
  expect(typeof mod.createBizarMcpServer).toBe("function");
79
84
  expect(typeof mod.createBizarMcpServerConfig).toBe("function");
80
85
  expect(typeof mod.defineTool).toBe("function");
@@ -85,12 +90,31 @@ describe("SDK module surface", () => {
85
90
  "plan_action", "open_kb",
86
91
  "loop_list", "loop_status", "loop_start", "loop_stop",
87
92
  "graph_query", "graph_path", "danger_check",
93
+ // F-032 — Swarm coordination.
94
+ "agent_spawn", "agent_list", "agent_terminate", "swarm_init",
95
+ // F-033 — Self-learning.
96
+ "model_route", "agent_route", "memory_distill",
88
97
  ]);
89
98
  const have = new Set(mod.BIZAR_TOOLS.map((t) => t.name));
90
99
  for (const n of expectedNames) {
91
100
  expect(have.has(n)).toBe(true);
92
101
  }
93
102
  });
103
+
104
+ test("F-033 router modules export the expected API", async () => {
105
+ const cm = await import("../dist/router/codemod-intent.js");
106
+ expect(typeof cm.detectCodemodIntent).toBe("function");
107
+
108
+ const mr = await import("../dist/router/model-router.js");
109
+ expect(typeof mr.ModelRouter).toBe("function");
110
+
111
+ const ql = await import("../dist/router/q-learning-router.js");
112
+ expect(typeof ql.QLearningRouter).toBe("function");
113
+ expect(Array.isArray(ql.AGENT_ACTIONS)).toBe(true);
114
+
115
+ const ds = await import("../dist/router/memory-distillation.js");
116
+ expect(typeof ds.runDistillation).toBe("function");
117
+ });
94
118
  });
95
119
 
96
120
  describe("dangerous-patterns behaviour", () => {
@@ -0,0 +1,258 @@
1
+ /**
2
+ * swarm-topology.test.ts — unit tests for the swarm topology registry.
3
+ *
4
+ * F-032 — covers initSwarm, maxAgents clamping, topology enum, the
5
+ * lazy default swarm, recordAgentInSwarm, decommissionSwarm, and
6
+ * optional JSON persistence.
7
+ */
8
+
9
+ import { describe, test, expect, beforeEach, afterEach } from "vitest";
10
+ import { mkdtempSync, rmSync, existsSync, readFileSync } from "node:fs";
11
+ import { tmpdir } from "node:os";
12
+ import { join } from "node:path";
13
+ import {
14
+ SwarmTopologyRegistry,
15
+ swarmTopologyRegistry,
16
+ initSwarm,
17
+ getSwarm,
18
+ listSwarms,
19
+ recordAgentInSwarm,
20
+ decommissionSwarm,
21
+ SWARM_TOPOLOGIES,
22
+ DEFAULT_TOPOLOGY,
23
+ DEFAULT_MAX_AGENTS,
24
+ DEFAULT_SWARM_ID,
25
+ type SwarmTopology,
26
+ } from "../src/swarm-topology.js";
27
+
28
+ describe("SwarmTopologyRegistry — class API", () => {
29
+ let reg: SwarmTopologyRegistry;
30
+
31
+ beforeEach(() => {
32
+ reg = new SwarmTopologyRegistry();
33
+ });
34
+
35
+ test("initSwarm creates a swarm with defaults when no args", () => {
36
+ const r = reg.initSwarm();
37
+ expect(r.topology).toBe(DEFAULT_TOPOLOGY);
38
+ expect(r.maxAgents).toBe(DEFAULT_MAX_AGENTS);
39
+ expect(r.swarmId).toMatch(/^swarm-\d+$/);
40
+ expect(typeof r.createdAt).toBe("string");
41
+ });
42
+
43
+ test("initSwarm accepts a custom swarmId", () => {
44
+ const r = reg.initSwarm({ swarmId: "alpha" });
45
+ expect(r.swarmId).toBe("alpha");
46
+ expect(reg.getSwarm("alpha")).toBeDefined();
47
+ });
48
+
49
+ test("initSwarm rejects duplicate swarmId", () => {
50
+ reg.initSwarm({ swarmId: "dup" });
51
+ expect(() => reg.initSwarm({ swarmId: "dup" })).toThrow(/already exists/);
52
+ });
53
+
54
+ test("topology enum is enforced — falls back to default for unknown", () => {
55
+ const r = reg.initSwarm({ topology: "unknown" as unknown as SwarmTopology });
56
+ expect(r.topology).toBe(DEFAULT_TOPOLOGY);
57
+ });
58
+
59
+ test("each valid topology is accepted", () => {
60
+ for (const topo of SWARM_TOPOLOGIES) {
61
+ const r = reg.initSwarm({ topology: topo });
62
+ expect(r.topology).toBe(topo);
63
+ }
64
+ });
65
+
66
+ test("maxAgents is clamped to [1, 1000]", () => {
67
+ const lo = reg.initSwarm({ maxAgents: -5 });
68
+ expect(lo.maxAgents).toBe(1);
69
+ const hi = reg.initSwarm({ maxAgents: 9999 });
70
+ expect(hi.maxAgents).toBe(1000);
71
+ const mid = reg.initSwarm({ maxAgents: 50 });
72
+ expect(mid.maxAgents).toBe(50);
73
+ });
74
+
75
+ test("maxAgents defaults to 15", () => {
76
+ const r = reg.initSwarm();
77
+ expect(r.maxAgents).toBe(15);
78
+ });
79
+
80
+ test("non-numeric maxAgents falls back to default", () => {
81
+ const r = reg.initSwarm({ maxAgents: NaN });
82
+ expect(r.maxAgents).toBe(15);
83
+ const r2 = reg.initSwarm({ maxAgents: "50" as unknown as number });
84
+ expect(r2.maxAgents).toBe(15);
85
+ });
86
+
87
+ test("metadata is round-tripped", () => {
88
+ const r = reg.initSwarm({ metadata: { owner: "F-032", region: "us" } });
89
+ expect(reg.getSwarm(r.swarmId)?.metadata).toEqual({ owner: "F-032", region: "us" });
90
+ });
91
+
92
+ test("the 'default' swarm is seeded lazily on first initSwarm call", () => {
93
+ expect(reg.getSwarm(DEFAULT_SWARM_ID)).toBeUndefined();
94
+ reg.initSwarm({ swarmId: "after" });
95
+ const def = reg.getSwarm(DEFAULT_SWARM_ID);
96
+ expect(def).toBeDefined();
97
+ expect(def?.topology).toBe(DEFAULT_TOPOLOGY);
98
+ expect(def?.maxAgents).toBe(DEFAULT_MAX_AGENTS);
99
+ });
100
+
101
+ test("listSwarms includes the default swarm after first initSwarm", () => {
102
+ reg.initSwarm({ swarmId: "x" });
103
+ const all = reg.listSwarms();
104
+ expect(all.map((s) => s.swarmId).sort()).toEqual([DEFAULT_SWARM_ID, "x"].sort());
105
+ });
106
+
107
+ test("getSwarm on missing id returns undefined", () => {
108
+ expect(reg.getSwarm("nope")).toBeUndefined();
109
+ });
110
+
111
+ test("recordAgentInSwarm appends agentIds and dedupes", () => {
112
+ const r = reg.initSwarm({ swarmId: "team", maxAgents: 3 });
113
+ recordAgentInSwarmOn(reg, "team", "a1");
114
+ recordAgentInSwarmOn(reg, "team", "a2");
115
+ recordAgentInSwarmOn(reg, "team", "a1"); // dedupe
116
+ const swarm = reg.getSwarm("team");
117
+ expect(swarm?.agentIds).toEqual(["a1", "a2"]);
118
+ });
119
+
120
+ test("recordAgentInSwarm refuses beyond maxAgents", () => {
121
+ const r = reg.initSwarm({ swarmId: "small", maxAgents: 2 });
122
+ recordAgentInSwarmOn(reg, "small", "a1");
123
+ recordAgentInSwarmOn(reg, "small", "a2");
124
+ expect(() => recordAgentInSwarmOn(reg, "small", "a3")).toThrow(/maxAgents/);
125
+ });
126
+
127
+ test("recordAgentInSwarm throws on missing swarm", () => {
128
+ expect(() => recordAgentInSwarmOn(reg, "ghost", "a1")).toThrow(/not found/);
129
+ });
130
+
131
+ test("decommissionSwarm marks the swarm and refuses further recordAgentInSwarm", () => {
132
+ reg.initSwarm({ swarmId: "doomed" });
133
+ reg.decommissionSwarm("doomed");
134
+ expect(reg.getSwarm("doomed")?.decommissionedAt).toBeDefined();
135
+ expect(() => recordAgentInSwarmOn(reg, "doomed", "a1")).toThrow(/decommissioned/);
136
+ });
137
+
138
+ test("decommissionSwarm refuses the default swarm", () => {
139
+ reg.initSwarm({ swarmId: "trigger" });
140
+ expect(() => reg.decommissionSwarm(DEFAULT_SWARM_ID)).toThrow(/default/);
141
+ });
142
+
143
+ test("decommissionSwarm throws on missing swarm", () => {
144
+ expect(() => reg.decommissionSwarm("ghost")).toThrow(/not found/);
145
+ });
146
+
147
+ test("reset() drops everything including the lazy default seed flag", () => {
148
+ reg.initSwarm({ swarmId: "x" });
149
+ expect(reg.listSwarms().length).toBeGreaterThan(0);
150
+ reg.reset();
151
+ expect(reg.listSwarms().length).toBe(0);
152
+ expect(reg.getSwarm(DEFAULT_SWARM_ID)).toBeUndefined();
153
+ });
154
+ });
155
+
156
+ // Helper — bypass module-level helper to operate on a per-test registry.
157
+ function recordAgentInSwarmOn(
158
+ reg: SwarmTopologyRegistry,
159
+ swarmId: string,
160
+ agentId: string,
161
+ ) {
162
+ return reg.recordAgentInSwarm(swarmId, agentId);
163
+ }
164
+
165
+ describe("SwarmTopologyRegistry — module-level helpers", () => {
166
+ test("initSwarm helper shares state with swarmTopologyRegistry", () => {
167
+ const r = initSwarm({ swarmId: "shared" });
168
+ expect(swarmTopologyRegistry.getSwarm(r.swarmId)).toBeDefined();
169
+ expect(getSwarm(r.swarmId)?.swarmId).toBe("shared");
170
+ expect(listSwarms().some((s) => s.swarmId === "shared")).toBe(true);
171
+ // cleanup
172
+ swarmTopologyRegistry.decommissionSwarm("shared");
173
+ });
174
+
175
+ test("recordAgentInSwarm helper reflects in swarmTopologyRegistry", () => {
176
+ const r = initSwarm({ swarmId: "record-helper" });
177
+ recordAgentInSwarm(r.swarmId, "agent-x");
178
+ expect(swarmTopologyRegistry.getSwarm(r.swarmId)?.agentIds).toContain("agent-x");
179
+ swarmTopologyRegistry.decommissionSwarm(r.swarmId);
180
+ });
181
+
182
+ test("decommissionSwarm helper reflects in swarmTopologyRegistry", () => {
183
+ const r = initSwarm({ swarmId: "decomm-helper" });
184
+ decommissionSwarm(r.swarmId);
185
+ expect(swarmTopologyRegistry.getSwarm(r.swarmId)?.decommissionedAt).toBeDefined();
186
+ });
187
+ });
188
+
189
+ describe("SwarmTopologyRegistry — persistence", () => {
190
+ let dir: string;
191
+ let persistPath: string;
192
+
193
+ beforeEach(() => {
194
+ dir = mkdtempSync(join(tmpdir(), "swarm-topology-"));
195
+ persistPath = join(dir, "topology.json");
196
+ });
197
+
198
+ afterEach(() => {
199
+ if (existsSync(dir)) rmSync(dir, { recursive: true, force: true });
200
+ });
201
+
202
+ test("a fresh registry with persistPath starts empty when file is missing", () => {
203
+ const reg = new SwarmTopologyRegistry({ persistPath });
204
+ expect(reg.listSwarms()).toEqual([]);
205
+ });
206
+
207
+ test("initSwarm writes a snapshot to disk after each call", () => {
208
+ const reg = new SwarmTopologyRegistry({ persistPath });
209
+ reg.initSwarm({ swarmId: "alpha", topology: "mesh" });
210
+ expect(existsSync(persistPath)).toBe(true);
211
+ const stored = JSON.parse(readFileSync(persistPath, "utf-8")) as {
212
+ swarms: Array<{ swarmId: string; topology: string }>;
213
+ };
214
+ expect(stored.swarms.some((s) => s.swarmId === "alpha" && s.topology === "mesh")).toBe(true);
215
+ });
216
+
217
+ test("a new registry reading the same path picks up persisted swarms", () => {
218
+ const reg1 = new SwarmTopologyRegistry({ persistPath });
219
+ reg1.initSwarm({ swarmId: "persisted", topology: "adaptive", maxAgents: 25 });
220
+ expect(reg1.listSwarms().some((s) => s.swarmId === "persisted")).toBe(true);
221
+
222
+ const reg2 = new SwarmTopologyRegistry({ persistPath });
223
+ const got = reg2.getSwarm("persisted");
224
+ expect(got?.topology).toBe("adaptive");
225
+ expect(got?.maxAgents).toBe(25);
226
+ });
227
+
228
+ test("setPersistPath switches the persistence target at runtime", () => {
229
+ const reg = new SwarmTopologyRegistry();
230
+ reg.setPersistPath(persistPath);
231
+ reg.initSwarm({ swarmId: "first" });
232
+ expect(existsSync(persistPath)).toBe(true);
233
+
234
+ const other = join(dir, "topology-other.json");
235
+ reg.setPersistPath(other);
236
+ reg.initSwarm({ swarmId: "second" });
237
+ expect(existsSync(other)).toBe(true);
238
+ });
239
+
240
+ test("a corrupt persistence file does not crash the registry", () => {
241
+ require("node:fs").writeFileSync(persistPath, "{not json", "utf-8");
242
+ const reg = new SwarmTopologyRegistry({ persistPath });
243
+ expect(reg.listSwarms()).toEqual([]);
244
+ reg.initSwarm({ swarmId: "recovered" });
245
+ expect(reg.getSwarm("recovered")).toBeDefined();
246
+ });
247
+
248
+ test("decommissionSwarm writes decommissionedAt to the persisted file", () => {
249
+ const reg = new SwarmTopologyRegistry({ persistPath });
250
+ reg.initSwarm({ swarmId: "doomed" });
251
+ reg.decommissionSwarm("doomed");
252
+ const stored = JSON.parse(readFileSync(persistPath, "utf-8")) as {
253
+ swarms: Array<{ swarmId: string; decommissionedAt?: string }>;
254
+ };
255
+ const row = stored.swarms.find((s) => s.swarmId === "doomed");
256
+ expect(row?.decommissionedAt).toBeDefined();
257
+ });
258
+ });
@@ -90,22 +90,18 @@ if (!existsSync(MCP_BIN_PATH)) {
90
90
  // ── 3. MCP server exposes the documented tool surface ──────────────────
91
91
  try {
92
92
  const serverSrc = readFileSync(join(REPO_ROOT, 'packages', 'sdk', 'src', 'mcp', 'server.ts'), 'utf8');
93
+ // Tool surface updated for v6.4.0 (was: bizar_* prefix + sandbox/glyph/plan_comment tools
94
+ // that were removed in the v6.3.0 migration; F-032 + F-033 added agent/router tools).
93
95
  const requiredTools = [
94
- 'bizar_memory_search',
95
- 'bizar_memory_read',
96
- 'bizar_memory_write',
97
- 'bizar_memory_list',
98
- 'bizar_plan_action',
99
- 'bizar_wait_for_feedback',
100
- 'bizar_get_plan_comments',
101
- 'bizar_read_glyph_feedback',
102
- 'bizar_open_kb',
103
- 'bizar_graph_query',
104
- 'bizar_graph_path',
105
- 'bizar_graph_explain',
106
- 'bizar_sandbox_run',
107
- 'bizar_sandbox_exec',
108
- 'bizar_loop_engineering',
96
+ // v6.3.0 core
97
+ 'memory_read', 'memory_write', 'memory_list', 'memory_search',
98
+ 'plan_action', 'open_kb',
99
+ 'loop_list', 'loop_status', 'loop_start', 'loop_stop',
100
+ 'graph_query', 'graph_path', 'danger_check',
101
+ // F-032 Swarm Coordination
102
+ 'agent_spawn', 'agent_list', 'agent_terminate', 'swarm_init',
103
+ // F-033 Self-Learning
104
+ 'model_route', 'agent_route', 'memory_distill', 'hooks_route',
109
105
  ];
110
106
  const missing = requiredTools.filter((t) => !serverSrc.includes(t));
111
107
  if (missing.length === 0) {
@@ -1 +0,0 @@
1
- import{j as n,r as o}from"./react-vendor-Dn4wqh4Z.js";import{C as O,u as T,a as k,b as R,c as E}from"./useSlashCommands-Bd7_FA6U.js";import{u as F}from"./Toast-Cpl6-E63.js";import"./icons-Do5N2jmX.js";import"./mobile-layout-CBHjpwsb.js";import"./markdown-tOLaD6nm.js";import"./vendor-C843201K.js";function $(t){const{activeSource:i}=t;return n.jsx("div",{className:`chat-composer-wrap chat-composer-source-${i??"none"} legacy`,children:n.jsx("div",{className:"chat-composer-pill",children:n.jsx(O,{...t})})})}function G({snapshot:t,settings:i,setActiveTab:g,initialTaskId:j,onClearTaskId:z}){const c=F(),s=T(t,i,j??"");o.useEffect(()=>{s.setToast({error:e=>c.error(e),success:e=>c.success(e),info:e=>c.info(e),warning:e=>c.warning(e)})},[s,c]);const[r,l]=o.useState(""),[f,x]=o.useState(i.defaultAgent||"odin"),[p,C]=o.useState(i.defaultModel||""),[h,v]=o.useState([]),u=o.useRef(null),{suggestions:y,setQuery:m}=k(t);o.useEffect(()=>{m(r)},[r,m]);const P=()=>{var e;return(e=u.current)==null?void 0:e.click()},w=e=>{const d=e.target.files;if(!d)return;const S=[];for(let a=0;a<d.length;a++)S.push(d[a].name);v(a=>{const M=S.filter(N=>!a.includes(N));return[...a,...M]}),u.current&&(u.current.value="")},I=async()=>{const e=r.trim();if(!e)return;l(""),m(""),(await s.onSend(e,f,p,h)).ok&&s.jumpToLatest()};return n.jsxs("div",{className:"chat-shell",children:[n.jsx(R,{activeProject:t.activeProject,sessionCount:s.sessions.length,sessionsOpen:!1,infoOpen:!1,onToggleSessions:()=>{},onToggleInfo:()=>{},onOpenOverview:()=>g==null?void 0:g("overview")}),n.jsx("div",{className:"chat-body",children:n.jsxs("main",{className:"chat-main",children:[n.jsx(E,{messages:s.activeSource==="cline"?s.clineMessages:s.bizarMessages,loading:s.loading,activeProject:t.activeProject,sessionId:s.activeSource==="cline"?s.activeClineSessionId??s.sessionId:s.sessionId,pinned:s.pinned,activeSource:s.activeSource,onPickSuggestion:e=>l(e),onCopy:e=>s.copyMessage(e),onDelete:()=>{},onTogglePin:s.togglePin,onRegenerate:s.onRegenerate}),n.jsx($,{agent:f,setAgent:x,model:p,setModel:C,text:r,setText:l,sending:s.sending,activeSource:s.activeSource,onSend:I,attachments:h,setAttachments:v,suggestions:y,onPickSuggestion:e=>l(`${e.split(" ")[0]} `),agents:t.agents||[],onAttach:P,sessionsOpen:!1,infoOpen:!1}),n.jsx("input",{ref:u,type:"file",multiple:!0,style:{display:"none"},onChange:w})]})})]})}export{G as MobileChat};
@@ -1 +0,0 @@
1
- import{r,j as e}from"./react-vendor-Dn4wqh4Z.js";import{a as y,S as E,c as R}from"./mobile-layout-CBHjpwsb.js";import{C as M,a as U,b as B,B as I,G,E as V,S as P,U as A,H as C,T as $,c as H}from"./EnvVarsSection-B58aiJiE.js";import{u as D}from"./Toast-Cpl6-E63.js";import{j as N,aY as k,aR as T,N as F,aa as K,s as O,aC as W,x as w,R as Y,aF as q,I as J,e as Q}from"./icons-Do5N2jmX.js";function X(){const s=D(),[i,t]=r.useState(!0),[c,a]=r.useState(!1),[o,d]=r.useState(!1),[u,g]=r.useState(""),[p,x]=r.useState(""),[f,h]=r.useState(!1),[j,n]=r.useState(0);r.useEffect(()=>{let l=!1;return(async()=>{var S,L;try{const[z,v]=await Promise.all([y.get("/memory/config/global").catch(()=>null),y.get("/memory/status").catch(()=>null)]);if(l)return;z&&g(((L=(S=z.config)==null?void 0:S.git)==null?void 0:L.remoteUrl)||""),v&&(x(v.vaultRoot||""),h(!!v.initialized))}finally{l||t(!1)}})(),()=>{l=!0}},[j]);const b=async()=>{a(!0);try{await y.put("/memory/config/global",{git:{remoteUrl:u}}),s.success("Memory config saved."),n(l=>l+1)}catch(l){s.error(`Save failed: ${l.message}`)}finally{a(!1)}},m=async()=>{d(!0);try{await y.post("/memory/init",{}),s.success("Vault initialised."),n(l=>l+1)}catch(l){s.error(`Initialise failed: ${l.message}`)}finally{d(!1)}};return e.jsxs(M,{id:"settings-memory","data-section":"memory",children:[e.jsxs(U,{children:[e.jsx(N,{size:14})," Memory vault"]}),e.jsx(B,{children:"The vault lives at a default location on disk. Only the git remote URL needs to be configured to enable sync across machines."}),i?e.jsx("div",{className:"flex-center",style:{padding:24},children:e.jsx(E,{size:"sm"})}):e.jsxs("div",{className:"settings-fields",children:[e.jsxs("div",{className:"field",children:[e.jsxs("label",{className:"field-label",children:[e.jsx(k,{size:12,style:{display:"inline",marginRight:4}}),"Vault path"]}),e.jsxs("div",{className:"field-readonly mono","data-testid":"settings-memory-vault-path",style:{fontSize:12,padding:"6px 0"},children:[p||e.jsx("span",{className:"muted",children:"not initialised"}),f?null:e.jsxs(I,{variant:"ghost",size:"sm",onClick:m,disabled:o,style:{marginLeft:12},children:[o?e.jsx(T,{size:12,className:"memory-spin"}):e.jsx(k,{size:12}),"Initialise"]})]})]}),e.jsxs("div",{className:"field",children:[e.jsxs("label",{className:"field-label",htmlFor:"set-memory-git-remote",children:[e.jsx(F,{size:12,style:{display:"inline",marginRight:4}}),"Git remote URL"]}),e.jsx("input",{id:"set-memory-git-remote",className:"input mono",type:"text",value:u,onChange:l=>g(l.target.value),placeholder:"git@github.com:org/repo.git"}),e.jsx("span",{className:"field-hint",children:"Leave empty to use local-only mode (no git sync)."})]}),e.jsx("div",{style:{marginTop:12},children:e.jsxs(I,{variant:"primary",size:"sm",onClick:b,disabled:c,children:[c?e.jsx(T,{size:12,className:"memory-spin"}):e.jsx(K,{size:12}),"Save memory config"]})})]})]})}function Z({settings:s,onSettingsChange:i,autoSave:t}){const c=(o,d)=>{i==null||i(o,d),t==null||t(o,d)},a=o=>{i==null||i("ui",{...s.ui,...o}),t==null||t("ui",{...s.ui,...o})};return e.jsx(G,{settings:s,patchUi:a,patchTop:c,autoSave:t})}function _({settings:s,onSettingsChange:i,autoSave:t}){return e.jsxs("div",{style:{fontSize:13,color:"var(--text-dim)",padding:"8px 0"},children:[e.jsx("p",{style:{marginBottom:8},children:"Provider management is available on desktop."}),e.jsx("a",{href:"/?desktop=1#settings-providers",className:"mobile-btn mobile-btn-secondary",style:{display:"inline-block",fontSize:12,padding:"6px 12px"},children:"Open desktop to manage providers"})]})}function ee(){return e.jsx(V,{})}function se(){return e.jsx(X,{})}function ie({settings:s,onSettingsChange:i,autoSave:t}){const c=(a,o)=>{i==null||i(a,o),t==null||t(a,o)};return e.jsx(P,{settings:s,patchTop:c})}function te(){return e.jsx(A,{})}function ne({settings:s}){const[i,t]=r.useState(s),[c,a]=r.useState(!1);return e.jsx(C,{settings:i,setSettings:t,setDirty:a})}function ae(){return e.jsx($,{})}function oe({settings:s}){const i=s.about||{version:"—",homepage:"https://github.com/DrB0rk/BizarHarness",license:"MIT"};return e.jsx(H,{about:i})}function pe({settings:s,onSettingsChange:i,autoSave:t,snapshot:c}){var x,f,h,j;const[a,o]=r.useState(""),[d,u]=r.useState(null),p=[{id:"general",title:"General",icon:O,description:"Theme, accent, layout",component:e.jsx(Z,{settings:s,onSettingsChange:i,autoSave:t})},{id:"providers",title:"AI Providers",icon:N,description:`${((x=c==null?void 0:c.providers)==null?void 0:x.length)??0} configured`,component:e.jsx(_,{settings:s,onSettingsChange:i,autoSave:t})},{id:"env-vars",title:"Environment Variables",icon:W,description:"API keys, secrets",component:e.jsx(ee,{})},{id:"memory",title:"Memory Vault",icon:N,description:"Git remote, LightRAG config",component:e.jsx(se,{})},{id:"system-llm",title:"System LLM",icon:w,description:((f=s.systemLlm)==null?void 0:f.model)||"not set",component:e.jsx(ie,{settings:s,onSettingsChange:i,autoSave:t})},{id:"updates",title:"Updates",icon:Y,description:"Check for updates",component:e.jsx(te,{})},{id:"headroom",title:"Headroom",icon:w,description:(h=s.headroom)!=null&&h.enabled?"Enabled":"Disabled",component:e.jsx(ne,{settings:s})},{id:"tailscale",title:"Tailscale",icon:q,description:"Tailscale serve & auth",component:e.jsx(ae,{})},{id:"about",title:"About",icon:J,description:`Bizar v${((j=s.about)==null?void 0:j.version)||"—"}`,component:e.jsx(oe,{settings:s})}].filter(n=>!a||n.title.toLowerCase().includes(a.toLowerCase())||n.description.toLowerCase().includes(a.toLowerCase()));return e.jsxs("div",{className:"mobile-settings",children:[e.jsxs("div",{className:"mobile-settings-search",children:[e.jsx(le,{}),e.jsx("input",{value:a,onChange:n=>o(n.target.value),placeholder:"Search settings...","aria-label":"Search settings"})]}),e.jsxs("div",{className:"mobile-settings-list",children:[p.map(n=>{const b=n.icon,m=d===n.id;return e.jsxs("div",{className:R("mobile-settings-item",m&&"is-expanded"),children:[e.jsxs("button",{className:"mobile-settings-item-head",onClick:()=>u(m?null:n.id),"aria-expanded":m,"aria-controls":`mobile-settings-body-${n.id}`,children:[e.jsx("div",{className:"mobile-settings-item-icon",children:e.jsx(b,{size:18})}),e.jsxs("div",{className:"mobile-settings-item-text",children:[e.jsx("div",{className:"mobile-settings-item-title",children:n.title}),e.jsx("div",{className:"mobile-settings-item-desc",children:n.description})]}),e.jsx(Q,{size:16,className:R("mobile-settings-item-chevron",m&&"is-rotated")})]}),m&&e.jsx("div",{id:`mobile-settings-body-${n.id}`,className:"mobile-settings-item-body",children:n.component})]},n.id)}),p.length===0&&e.jsxs("div",{className:"mobile-settings-empty",children:['No settings match "',a,'"']})]})]})}function le(){return e.jsxs("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[e.jsx("circle",{cx:"11",cy:"11",r:"8"}),e.jsx("path",{d:"m21 21-4.35-4.35"})]})}export{pe as MobileSettings};