@polderlabs/bizar 6.2.5 → 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 (540) hide show
  1. package/.claude/CLAUDE.md +15 -0
  2. package/.claude/agents/_shared/AGENT_BASELINE.md +168 -0
  3. package/.claude/agents/_shared/CLAUDE_TOOLS.md +412 -0
  4. package/.claude/agents/_shared/SKILLS.md +109 -0
  5. package/.claude/agents/agent-browser.md +80 -0
  6. package/.claude/agents/baldr.md +49 -0
  7. package/.claude/agents/forseti.md +51 -0
  8. package/.claude/agents/frigg.md +42 -0
  9. package/.claude/agents/heimdall.md +33 -0
  10. package/.claude/agents/hermod.md +53 -0
  11. package/.claude/agents/mimir.md +49 -0
  12. package/.claude/agents/odin.md +287 -0
  13. package/.claude/agents/quick.md +34 -0
  14. package/.claude/agents/semble-search.md +50 -0
  15. package/.claude/agents/thor.md +53 -0
  16. package/.claude/agents/tyr.md +56 -0
  17. package/.claude/agents/vidarr.md +54 -0
  18. package/.claude/agents/vor.md +53 -0
  19. package/.claude/commands/audit.md +25 -0
  20. package/.claude/commands/bizar.md +22 -0
  21. package/.claude/commands/explain.md +17 -0
  22. package/.claude/commands/init.md +29 -0
  23. package/.claude/commands/learn.md +44 -0
  24. package/.claude/commands/plan.md +35 -0
  25. package/.claude/commands/plow-through.md +50 -0
  26. package/.claude/commands/pr-review.md +49 -0
  27. package/.claude/commands/setup-provider.md +96 -0
  28. package/.claude/commands/tailscale-serve.md +100 -0
  29. package/.claude/commands/team.md +132 -0
  30. package/.claude/commands/test.md +62 -0
  31. package/.claude/commands/validate.md +68 -0
  32. package/.claude/commands/visual-plan.md +24 -0
  33. package/.claude/hooks/README.md +92 -0
  34. package/.claude/hooks/posttooluse-editwrite.mjs +91 -0
  35. package/.claude/hooks/pretooluse-bash.mjs +81 -0
  36. package/.claude/hooks/pretooluse-editwrite.mjs +139 -0
  37. package/.claude/hooks/sessionend-recall.mjs +74 -0
  38. package/.claude/hooks/sessionstart-prime.mjs +80 -0
  39. package/.claude/hooks/userpromptsubmit-tag.mjs +80 -0
  40. package/.claude/hooks/worker-suggest.mjs +110 -0
  41. package/.claude/settings.json +125 -0
  42. package/.claude/skills/9router/SKILL.md +80 -0
  43. package/.claude/skills/9router-chat/SKILL.md +73 -0
  44. package/.claude/skills/9router-embeddings/SKILL.md +69 -0
  45. package/.claude/skills/9router-image/SKILL.md +86 -0
  46. package/.claude/skills/9router-stt/SKILL.md +79 -0
  47. package/.claude/skills/9router-tts/SKILL.md +80 -0
  48. package/.claude/skills/9router-web-fetch/SKILL.md +99 -0
  49. package/.claude/skills/9router-web-search/SKILL.md +91 -0
  50. package/.claude/skills/bizar/README.md +9 -0
  51. package/.claude/skills/bizar/SKILL.md +450 -0
  52. package/.claude/skills/cpp-coding-standards/README.md +28 -0
  53. package/.claude/skills/cpp-coding-standards/SKILL.md +634 -0
  54. package/.claude/skills/cpp-coding-standards/references/concurrency.md +320 -0
  55. package/.claude/skills/cpp-coding-standards/references/error-handling.md +229 -0
  56. package/.claude/skills/cpp-coding-standards/references/memory-safety.md +216 -0
  57. package/.claude/skills/cpp-coding-standards/references/modern-idioms.md +282 -0
  58. package/.claude/skills/cpp-coding-standards/references/review-checklist.md +96 -0
  59. package/.claude/skills/cpp-testing/README.md +28 -0
  60. package/.claude/skills/cpp-testing/SKILL.md +304 -0
  61. package/.claude/skills/cpp-testing/references/coverage.md +370 -0
  62. package/.claude/skills/cpp-testing/references/framework-compare.md +175 -0
  63. package/.claude/skills/cpp-testing/references/host-test-for-embedded.md +499 -0
  64. package/.claude/skills/cpp-testing/references/mocking.md +364 -0
  65. package/.claude/skills/cpp-testing/references/tdd-workflow.md +308 -0
  66. package/.claude/skills/cubesandbox/SKILL.md +148 -0
  67. package/.claude/skills/embedded-esp-idf/README.md +41 -0
  68. package/.claude/skills/embedded-esp-idf/SKILL.md +439 -0
  69. package/.claude/skills/embedded-esp-idf/references/freertos-patterns.md +214 -0
  70. package/.claude/skills/embedded-esp-idf/references/host-tests.md +164 -0
  71. package/.claude/skills/embedded-esp-idf/references/idf-py-commands.md +157 -0
  72. package/.claude/skills/embedded-esp-idf/references/kconfig.md +159 -0
  73. package/.claude/skills/embedded-esp-idf/references/logging-discipline.md +118 -0
  74. package/.claude/skills/embedded-esp-idf/references/memory-and-iram.md +137 -0
  75. package/.claude/skills/embedded-esp-idf/references/nvs.md +121 -0
  76. package/.claude/skills/embedded-esp-idf/references/packed-structs.md +192 -0
  77. package/.claude/skills/embedded-esp-idf/scripts/idf_env.sh +47 -0
  78. package/.claude/skills/embedded-esp-idf/scripts/size_check.sh +77 -0
  79. package/.claude/skills/glyph/SKILL.md +163 -0
  80. package/.claude/skills/harness-engineering/SKILL.md +143 -0
  81. package/.claude/skills/lightrag/SKILL.md +81 -0
  82. package/.claude/skills/memory-protocol/SKILL.md +105 -0
  83. package/.claude/skills/obsidian/SKILL.md +306 -0
  84. package/.claude/skills/read-the-damn-docs/SKILL.md +113 -0
  85. package/.claude/skills/self-improvement/SKILL.md +64 -0
  86. package/README.md +87 -59
  87. package/bizar-dash/dist/assets/{EnvVarsSection-DXM8gRm_.js → EnvVarsSection-CW6fWKds.js} +3 -3
  88. package/bizar-dash/dist/assets/{EnvVarsSection-DXM8gRm_.js.map → EnvVarsSection-CW6fWKds.js.map} +1 -1
  89. package/bizar-dash/dist/assets/MobileChat-D7zRhU9K.js +1 -0
  90. package/bizar-dash/dist/assets/{MobileChat-BnKN_Ks_.js.map → MobileChat-D7zRhU9K.js.map} +1 -1
  91. package/bizar-dash/dist/assets/MobileSettings-K-sNsn_r.js +1 -0
  92. package/bizar-dash/dist/assets/{MobileSettings-DjCPxC-Q.js.map → MobileSettings-K-sNsn_r.js.map} +1 -1
  93. package/bizar-dash/dist/assets/{Toast-Cpl6-E63.js → Toast-WTxc3m2g.js} +1 -1
  94. package/bizar-dash/dist/assets/{Toast-Cpl6-E63.js.map → Toast-WTxc3m2g.js.map} +1 -1
  95. package/bizar-dash/dist/assets/flow-BpU0-gK2.js +1 -0
  96. package/bizar-dash/dist/assets/flow-BpU0-gK2.js.map +1 -0
  97. package/bizar-dash/dist/assets/fuzzy-l0sNRNKZ.js +1 -0
  98. package/bizar-dash/dist/assets/fuzzy-l0sNRNKZ.js.map +1 -0
  99. package/bizar-dash/dist/assets/{icons-Do5N2jmX.js → icons-B0AKnYI3.js} +98 -103
  100. package/bizar-dash/dist/assets/icons-B0AKnYI3.js.map +1 -0
  101. package/bizar-dash/dist/assets/main-C-Md3DaY.css +1 -0
  102. package/bizar-dash/dist/assets/main-CO8fIxQK.js +18 -0
  103. package/bizar-dash/dist/assets/main-CO8fIxQK.js.map +1 -0
  104. package/bizar-dash/dist/assets/markdown-w2bDDkAH.js +1 -0
  105. package/bizar-dash/dist/assets/{markdown-C6mXtQxD.js.map → markdown-w2bDDkAH.js.map} +1 -1
  106. package/bizar-dash/dist/assets/mobile-DXYCA0hC.js +1 -0
  107. package/bizar-dash/dist/assets/{mobile-CMHqtLV2.js.map → mobile-DXYCA0hC.js.map} +1 -1
  108. package/bizar-dash/dist/assets/mobile-layout-DSmtNPja.js +2 -0
  109. package/bizar-dash/dist/assets/mobile-layout-DSmtNPja.js.map +1 -0
  110. package/bizar-dash/dist/assets/react-vendor-BnuhLJ6X.css +1 -0
  111. package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js +46 -0
  112. package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js.map +1 -0
  113. package/bizar-dash/dist/assets/useSlashCommands-3DFLOfpk.js +2 -0
  114. package/bizar-dash/dist/assets/useSlashCommands-3DFLOfpk.js.map +1 -0
  115. package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js +45 -0
  116. package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js.map +1 -0
  117. package/bizar-dash/dist/index.html +12 -10
  118. package/bizar-dash/dist/mobile.html +7 -4
  119. package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  120. package/bizar-dash/src/server/api.mjs +15 -4
  121. package/bizar-dash/src/server/background-store.mjs +15 -16
  122. package/bizar-dash/src/server/bg-poller.mjs +25 -35
  123. package/bizar-dash/src/server/bg-retry.mjs +66 -194
  124. package/bizar-dash/src/server/claude-bg-spawner.mjs +390 -0
  125. package/bizar-dash/src/server/claude-info.mjs +412 -0
  126. package/bizar-dash/src/server/{cline-runner.mjs → claude-runner.mjs} +53 -48
  127. package/bizar-dash/src/server/claude-sdk.mjs +191 -0
  128. package/bizar-dash/src/server/memory-consolidator.mjs +218 -0
  129. package/bizar-dash/src/server/memory-distillation.mjs +296 -0
  130. package/bizar-dash/src/server/providers-store.mjs +22 -49
  131. package/bizar-dash/src/server/routes/background.mjs +24 -20
  132. package/bizar-dash/src/server/routes/chat.mjs +223 -326
  133. package/bizar-dash/src/server/routes/claude-session-detail.mjs +329 -0
  134. package/bizar-dash/src/server/routes/claude-sessions.mjs +259 -0
  135. package/bizar-dash/src/server/routes/distill.mjs +129 -0
  136. package/bizar-dash/src/server/routes/goal-planner.mjs +236 -0
  137. package/bizar-dash/src/server/routes/tasks.mjs +20 -28
  138. package/bizar-dash/src/server/task-delegator.mjs +76 -77
  139. package/bizar-dash/src/web/App.tsx +4 -0
  140. package/bizar-dash/src/web/components/Tag.tsx +18 -5
  141. package/bizar-dash/src/web/components/Topbar.tsx +3 -0
  142. package/bizar-dash/src/web/components/agents/CommunicationLog.tsx +126 -0
  143. package/bizar-dash/src/web/components/agents/DependencyGraph.tsx +163 -0
  144. package/bizar-dash/src/web/components/agents/QualityGates.tsx +123 -0
  145. package/bizar-dash/src/web/components/agents/RealTimeEventLog.tsx +127 -0
  146. package/bizar-dash/src/web/components/agents/RoutingDecisions.test.tsx +148 -0
  147. package/bizar-dash/src/web/components/agents/RoutingDecisions.tsx +160 -0
  148. package/bizar-dash/src/web/components/chat/useChat.ts +23 -27
  149. package/bizar-dash/src/web/components/goals/GoalInput.tsx +86 -0
  150. package/bizar-dash/src/web/components/goals/PlanVisualization.tsx +111 -0
  151. package/bizar-dash/src/web/lib/goapPlanner.ts +614 -0
  152. package/bizar-dash/src/web/lib/types.ts +25 -0
  153. package/bizar-dash/src/web/styles/agents-redesign.css +311 -0
  154. package/bizar-dash/src/web/styles/main.css +251 -0
  155. package/bizar-dash/src/web/styles/tasks-redesign.css +231 -0
  156. package/bizar-dash/src/web/ui/controls/Button.tsx +77 -0
  157. package/bizar-dash/src/web/ui/controls/Checkbox.tsx +95 -0
  158. package/bizar-dash/src/web/ui/controls/IconButton.tsx +66 -0
  159. package/bizar-dash/src/web/ui/controls/Kbd.tsx +24 -0
  160. package/bizar-dash/src/web/ui/controls/NumberInput.tsx +77 -0
  161. package/bizar-dash/src/web/ui/controls/RadioGroup.tsx +109 -0
  162. package/bizar-dash/src/web/ui/controls/SearchInput.tsx +91 -0
  163. package/bizar-dash/src/web/ui/controls/Select.tsx +92 -0
  164. package/bizar-dash/src/web/ui/controls/Slider.tsx +70 -0
  165. package/bizar-dash/src/web/ui/controls/TextInput.tsx +67 -0
  166. package/bizar-dash/src/web/ui/controls/Toggle.tsx +79 -0
  167. package/bizar-dash/src/web/ui/controls/controls.css +454 -0
  168. package/bizar-dash/src/web/ui/controls/index.ts +33 -0
  169. package/bizar-dash/src/web/ui/data/BarChart.tsx +60 -0
  170. package/bizar-dash/src/web/ui/data/DataTable.tsx +216 -0
  171. package/bizar-dash/src/web/ui/data/EmptyState.tsx +60 -0
  172. package/bizar-dash/src/web/ui/data/ErrorState.tsx +56 -0
  173. package/bizar-dash/src/web/ui/data/KeyValueList.tsx +106 -0
  174. package/bizar-dash/src/web/ui/data/LoadingState.tsx +65 -0
  175. package/bizar-dash/src/web/ui/data/Sparkline.tsx +87 -0
  176. package/bizar-dash/src/web/ui/data/StatTile.tsx +87 -0
  177. package/bizar-dash/src/web/ui/data/data.css +535 -0
  178. package/bizar-dash/src/web/ui/data/index.ts +31 -0
  179. package/bizar-dash/src/web/ui/feedback/Badge.tsx +49 -0
  180. package/bizar-dash/src/web/ui/feedback/Dialog.tsx +209 -0
  181. package/bizar-dash/src/web/ui/feedback/ProgressBar.tsx +69 -0
  182. package/bizar-dash/src/web/ui/feedback/StatusDot.tsx +50 -0
  183. package/bizar-dash/src/web/ui/feedback/Toast.tsx +200 -0
  184. package/bizar-dash/src/web/ui/feedback/Tooltip.tsx +126 -0
  185. package/bizar-dash/src/web/ui/feedback/feedback.css +314 -0
  186. package/bizar-dash/src/web/ui/feedback/index.ts +43 -0
  187. package/bizar-dash/src/web/ui/index.ts +56 -0
  188. package/bizar-dash/src/web/ui/layout/AppShell.tsx +51 -0
  189. package/bizar-dash/src/web/ui/layout/Breadcrumbs.tsx +97 -0
  190. package/bizar-dash/src/web/ui/layout/Panel.tsx +73 -0
  191. package/bizar-dash/src/web/ui/layout/PanelHeader.tsx +39 -0
  192. package/bizar-dash/src/web/ui/layout/Sidebar.tsx +131 -0
  193. package/bizar-dash/src/web/ui/layout/Tabs.tsx +133 -0
  194. package/bizar-dash/src/web/ui/layout/Topbar.tsx +42 -0
  195. package/bizar-dash/src/web/ui/layout/index.ts +33 -0
  196. package/bizar-dash/src/web/ui/layout/layout.css +413 -0
  197. package/bizar-dash/src/web/ui/navigation/CommandPalette.tsx +199 -0
  198. package/bizar-dash/src/web/ui/navigation/NavGroup.tsx +95 -0
  199. package/bizar-dash/src/web/ui/navigation/NavLink.tsx +93 -0
  200. package/bizar-dash/src/web/ui/navigation/index.ts +17 -0
  201. package/bizar-dash/src/web/ui/navigation/navigation.css +343 -0
  202. package/bizar-dash/src/web/ui/primitives/Box.tsx +91 -0
  203. package/bizar-dash/src/web/ui/primitives/Grid.tsx +49 -0
  204. package/bizar-dash/src/web/ui/primitives/Inline.tsx +55 -0
  205. package/bizar-dash/src/web/ui/primitives/Separator.tsx +46 -0
  206. package/bizar-dash/src/web/ui/primitives/Stack.tsx +54 -0
  207. package/bizar-dash/src/web/ui/primitives/VisuallyHidden.tsx +26 -0
  208. package/bizar-dash/src/web/ui/primitives/index.ts +23 -0
  209. package/bizar-dash/src/web/ui/primitives/primitives.css +183 -0
  210. package/bizar-dash/src/web/ui/styles/globals.css +86 -0
  211. package/bizar-dash/src/web/ui/styles/reset.css +125 -0
  212. package/bizar-dash/src/web/ui/styles/tokens.css +195 -0
  213. package/bizar-dash/src/web/ui/theme/ThemeProvider.tsx +142 -0
  214. package/bizar-dash/src/web/ui/theme/tokens.ts +131 -0
  215. package/bizar-dash/src/web/ui/theme/useTheme.ts +22 -0
  216. package/bizar-dash/src/web/ui/utils/cx.ts +42 -0
  217. package/bizar-dash/src/web/views/Agents.tsx +506 -266
  218. package/bizar-dash/src/web/views/Chat.tsx +8 -8
  219. package/bizar-dash/src/web/views/GoalPlanner.tsx +158 -0
  220. package/bizar-dash/src/web/views/Overview.tsx +996 -448
  221. package/bizar-dash/src/web/views/Tasks.tsx +277 -273
  222. package/bizar-dash/tests/communication-log.test.tsx +67 -0
  223. package/bizar-dash/tests/dep-graph.test.tsx +40 -0
  224. package/bizar-dash/tests/event-log.test.tsx +61 -0
  225. package/bizar-dash/tests/goal-input.test.tsx +72 -0
  226. package/bizar-dash/tests/goap-planner.test.ts +68 -0
  227. package/bizar-dash/tests/quality-gates.test.tsx +59 -0
  228. package/bizar-dash/tests/setup.ts +31 -1
  229. package/bizar-dash/tests/ui/controls/Button.test.tsx +50 -0
  230. package/bizar-dash/tests/ui/controls/Checkbox.test.tsx +44 -0
  231. package/bizar-dash/tests/ui/controls/IconButton.test.tsx +37 -0
  232. package/bizar-dash/tests/ui/controls/Kbd.test.tsx +39 -0
  233. package/bizar-dash/tests/ui/controls/NumberInput.test.tsx +56 -0
  234. package/bizar-dash/tests/ui/controls/RadioGroup.test.tsx +58 -0
  235. package/bizar-dash/tests/ui/controls/SearchInput.test.tsx +52 -0
  236. package/bizar-dash/tests/ui/controls/Select.test.tsx +66 -0
  237. package/bizar-dash/tests/ui/controls/Slider.test.tsx +41 -0
  238. package/bizar-dash/tests/ui/controls/TextInput.test.tsx +44 -0
  239. package/bizar-dash/tests/ui/controls/Toggle.test.tsx +39 -0
  240. package/bizar-dash/tests/ui/data/BarChart.test.tsx +55 -0
  241. package/bizar-dash/tests/ui/data/DataTable.test.tsx +112 -0
  242. package/bizar-dash/tests/ui/data/EmptyState.test.tsx +57 -0
  243. package/bizar-dash/tests/ui/data/ErrorState.test.tsx +47 -0
  244. package/bizar-dash/tests/ui/data/KeyValueList.test.tsx +59 -0
  245. package/bizar-dash/tests/ui/data/LoadingState.test.tsx +37 -0
  246. package/bizar-dash/tests/ui/data/Sparkline.test.tsx +56 -0
  247. package/bizar-dash/tests/ui/data/StatTile.test.tsx +69 -0
  248. package/bizar-dash/tests/ui/feedback/Badge.test.tsx +41 -0
  249. package/bizar-dash/tests/ui/feedback/Dialog.test.tsx +99 -0
  250. package/bizar-dash/tests/ui/feedback/ProgressBar.test.tsx +66 -0
  251. package/bizar-dash/tests/ui/feedback/StatusDot.test.tsx +41 -0
  252. package/bizar-dash/tests/ui/feedback/Toast.test.tsx +124 -0
  253. package/bizar-dash/tests/ui/feedback/Tooltip.test.tsx +102 -0
  254. package/bizar-dash/tests/ui/layout/AppShell.test.tsx +50 -0
  255. package/bizar-dash/tests/ui/layout/Breadcrumbs.test.tsx +85 -0
  256. package/bizar-dash/tests/ui/layout/Panel.test.tsx +52 -0
  257. package/bizar-dash/tests/ui/layout/PanelHeader.test.tsx +32 -0
  258. package/bizar-dash/tests/ui/layout/Sidebar.test.tsx +95 -0
  259. package/bizar-dash/tests/ui/layout/Tabs.test.tsx +87 -0
  260. package/bizar-dash/tests/ui/layout/Topbar.test.tsx +41 -0
  261. package/bizar-dash/tests/ui/navigation/CommandPalette.test.tsx +105 -0
  262. package/bizar-dash/tests/ui/navigation/NavGroup.test.tsx +55 -0
  263. package/bizar-dash/tests/ui/navigation/NavLink.test.tsx +61 -0
  264. package/bizar-dash/tests/ui/primitives/Box.test.tsx +49 -0
  265. package/bizar-dash/tests/ui/primitives/Grid.test.tsx +37 -0
  266. package/bizar-dash/tests/ui/primitives/Inline.test.tsx +33 -0
  267. package/bizar-dash/tests/ui/primitives/Separator.test.tsx +34 -0
  268. package/bizar-dash/tests/ui/primitives/Stack.test.tsx +45 -0
  269. package/bizar-dash/tests/ui/primitives/VisuallyHidden.test.tsx +27 -0
  270. package/bizar-dash/tests/views/Agents.test.tsx +359 -0
  271. package/bizar-dash/tests/views/Overview.test.tsx +413 -0
  272. package/bizar-dash/tests/views/Tasks.test.tsx +250 -0
  273. package/bizar-dash/vitest.config.ts +3 -1
  274. package/cli/__tests__/cost-gate.test.mjs +301 -0
  275. package/cli/__tests__/feature-list-bridge.test.mjs +371 -0
  276. package/cli/bin.mjs +55 -18
  277. package/cli/commands/claim.mjs +282 -0
  278. package/cli/commands/claude-cmd.mjs +348 -0
  279. package/cli/commands/cost.mjs +279 -0
  280. package/cli/commands/install.mjs +12 -11
  281. package/cli/commands/validate.mjs +252 -325
  282. package/cli/cost-gate.mjs +501 -0
  283. package/cli/dev-link.test.mjs +11 -11
  284. package/cli/doctor.mjs +131 -213
  285. package/cli/feature-list-bridge.mjs +400 -0
  286. package/cli/install.mjs +33 -37
  287. package/cli/install.test.mjs +9 -9
  288. package/cli/memory-constants.mjs +17 -0
  289. package/cli/provision-claude.mjs +893 -0
  290. package/cli/provision.mjs +224 -227
  291. package/cli/utils.mjs +72 -58
  292. package/cli/worker-dispatcher.mjs +259 -0
  293. package/cli/worker-dispatcher.test.mjs +204 -0
  294. package/config/AGENTS.md +28 -28
  295. package/config/trigger-patterns.json +186 -0
  296. package/install.sh +66 -44
  297. package/package.json +19 -11
  298. package/packages/sdk/ARCHITECTURE.md +40 -36
  299. package/packages/sdk/package.json +15 -5
  300. package/packages/sdk/src/agent-registry.ts +304 -0
  301. package/packages/sdk/src/consensus/byzantine.ts +587 -0
  302. package/packages/sdk/src/consensus/index.ts +147 -0
  303. package/packages/sdk/src/consensus/queen.ts +173 -0
  304. package/packages/sdk/src/consensus/types.ts +199 -0
  305. package/{plugins/bizar → packages/sdk}/src/dangerous-patterns.ts +6 -28
  306. package/packages/sdk/src/federation/audit.ts +156 -0
  307. package/packages/sdk/src/federation/budget.ts +269 -0
  308. package/packages/sdk/src/federation/envelope.ts +144 -0
  309. package/packages/sdk/src/federation/hmac.ts +160 -0
  310. package/packages/sdk/src/federation/index.ts +443 -0
  311. package/packages/sdk/src/federation/pii.ts +241 -0
  312. package/packages/sdk/src/federation/policy.ts +112 -0
  313. package/packages/sdk/src/federation/trust.ts +146 -0
  314. package/packages/sdk/src/fingerprint.ts +0 -0
  315. package/packages/sdk/src/index.ts +101 -69
  316. package/packages/sdk/src/mcp/bin.ts +51 -0
  317. package/packages/sdk/src/mcp/server.ts +848 -0
  318. package/{plugins/bizar/src/memory-vault.ts → packages/sdk/src/memory/index.ts} +18 -21
  319. package/packages/sdk/src/router/codemod-intent.ts +125 -0
  320. package/packages/sdk/src/router/index.ts +247 -0
  321. package/packages/sdk/src/router/memory-distillation-shim.mjs +18 -0
  322. package/packages/sdk/src/router/memory-distillation.ts +249 -0
  323. package/packages/sdk/src/router/model-router.ts +235 -0
  324. package/packages/sdk/src/router/q-learning-router.ts +304 -0
  325. package/packages/sdk/src/swarm-topology.ts +279 -0
  326. package/packages/sdk/tests/agent-registry.test.ts +285 -0
  327. package/packages/sdk/tests/consensus.test.ts +376 -0
  328. package/packages/sdk/tests/federation/audit.test.ts +164 -0
  329. package/packages/sdk/tests/federation/budget.test.ts +189 -0
  330. package/packages/sdk/tests/federation/envelope.test.ts +120 -0
  331. package/packages/sdk/tests/federation/hmac.test.ts +194 -0
  332. package/packages/sdk/tests/federation/orchestrator.test.ts +176 -0
  333. package/packages/sdk/tests/federation/pii.test.ts +106 -0
  334. package/packages/sdk/tests/federation/policy.test.ts +140 -0
  335. package/packages/sdk/tests/federation/trust.test.ts +124 -0
  336. package/packages/sdk/tests/mcp-tools.test.ts +204 -0
  337. package/packages/sdk/tests/memory-distillation.test.mjs +186 -0
  338. package/packages/sdk/tests/model-router.test.mjs +108 -0
  339. package/packages/sdk/tests/q-learning-router.test.mjs +100 -0
  340. package/packages/sdk/tests/router-orchestrator.test.mjs +172 -0
  341. package/packages/sdk/tests/router.test.mjs +91 -0
  342. package/packages/sdk/tests/sdk.test.mjs +172 -0
  343. package/packages/sdk/tests/swarm-topology.test.ts +258 -0
  344. package/packages/sdk/vitest.config.ts +1 -1
  345. package/scripts/bh-full-e2e.mjs +167 -368
  346. package/scripts/check-deps.mjs +28 -46
  347. package/scripts/mirror-claude-md.sh +78 -0
  348. package/scripts/test-in-container.sh +31 -9
  349. package/templates/clean-state-checklist.md +3 -3
  350. package/templates/sprint-contract.md +2 -2
  351. package/bizar-dash/.bizar/activity.log +0 -3
  352. package/bizar-dash/.omx/logs/omx-2026-07-07.jsonl +0 -1
  353. package/bizar-dash/.omx/state/session.json +0 -10
  354. package/bizar-dash/bizar-design/EXPLANATION.md +0 -307
  355. package/bizar-dash/bizar-design/INSTRUCTIONS.md +0 -149
  356. package/bizar-dash/bizar-design/canvas.html +0 -281
  357. package/bizar-dash/bizar-design/canvas.html.artifact.json +0 -19
  358. package/bizar-dash/bizar-design/components.css +0 -1665
  359. package/bizar-dash/bizar-design/components.html +0 -557
  360. package/bizar-dash/bizar-design/components.html.artifact.json +0 -19
  361. package/bizar-dash/bizar-design/desktop/agents.html +0 -74
  362. package/bizar-dash/bizar-design/desktop/agents.html.artifact.json +0 -19
  363. package/bizar-dash/bizar-design/desktop/memory.html +0 -75
  364. package/bizar-dash/bizar-design/desktop/memory.html.artifact.json +0 -19
  365. package/bizar-dash/bizar-design/desktop/metrics.html +0 -63
  366. package/bizar-dash/bizar-design/desktop/metrics.html.artifact.json +0 -19
  367. package/bizar-dash/bizar-design/desktop/overview.html +0 -95
  368. package/bizar-dash/bizar-design/desktop/overview.html.artifact.json +0 -19
  369. package/bizar-dash/bizar-design/desktop/settings.html +0 -70
  370. package/bizar-dash/bizar-design/desktop/settings.html.artifact.json +0 -19
  371. package/bizar-dash/bizar-design/icons.svg +0 -44
  372. package/bizar-dash/bizar-design/index.html +0 -192
  373. package/bizar-dash/bizar-design/index.html.artifact.json +0 -19
  374. package/bizar-dash/bizar-design/mobile/agents.html +0 -29
  375. package/bizar-dash/bizar-design/mobile/agents.html.artifact.json +0 -19
  376. package/bizar-dash/bizar-design/mobile/memory.html +0 -28
  377. package/bizar-dash/bizar-design/mobile/memory.html.artifact.json +0 -19
  378. package/bizar-dash/bizar-design/mobile/metrics.html +0 -29
  379. package/bizar-dash/bizar-design/mobile/metrics.html.artifact.json +0 -19
  380. package/bizar-dash/bizar-design/mobile/overview.html +0 -33
  381. package/bizar-dash/bizar-design/mobile/overview.html.artifact.json +0 -19
  382. package/bizar-dash/bizar-design/mobile/settings.html +0 -28
  383. package/bizar-dash/bizar-design/mobile/settings.html.artifact.json +0 -19
  384. package/bizar-dash/bizar-design/prototype.js +0 -114
  385. package/bizar-dash/bizar-design/tokens.css +0 -106
  386. package/bizar-dash/dist/assets/MobileChat-BnKN_Ks_.js +0 -1
  387. package/bizar-dash/dist/assets/MobileSettings-DjCPxC-Q.js +0 -1
  388. package/bizar-dash/dist/assets/icons-Do5N2jmX.js.map +0 -1
  389. package/bizar-dash/dist/assets/main-DYiZqMrn.js +0 -18
  390. package/bizar-dash/dist/assets/main-DYiZqMrn.js.map +0 -1
  391. package/bizar-dash/dist/assets/main-ietCEg_R.css +0 -1
  392. package/bizar-dash/dist/assets/markdown-C6mXtQxD.js +0 -1
  393. package/bizar-dash/dist/assets/mobile-CMHqtLV2.js +0 -1
  394. package/bizar-dash/dist/assets/mobile-layout-3jIhHX_p.js +0 -2
  395. package/bizar-dash/dist/assets/mobile-layout-3jIhHX_p.js.map +0 -1
  396. package/bizar-dash/dist/assets/react-vendor-Dn4wqh4Z.js +0 -40
  397. package/bizar-dash/dist/assets/react-vendor-Dn4wqh4Z.js.map +0 -1
  398. package/bizar-dash/dist/assets/useSlashCommands-DtITw8Xv.js +0 -2
  399. package/bizar-dash/dist/assets/useSlashCommands-DtITw8Xv.js.map +0 -1
  400. package/bizar-dash/dist/assets/vendor-CeHGtduv.js +0 -29
  401. package/bizar-dash/dist/assets/vendor-CeHGtduv.js.map +0 -1
  402. package/bizar-dash/node_modules/.package-lock.json +0 -6
  403. package/bizar-dash/package-lock.json +0 -6
  404. package/bizar-dash/src/server/cline-sdk.mjs +0 -132
  405. package/bizar-dash/src/server/routes/cline-session-detail.mjs +0 -559
  406. package/bizar-dash/src/server/routes/cline-sessions.mjs +0 -291
  407. package/cli/commands/cline-cmd.mjs +0 -289
  408. package/cli/commands/validate.test.mjs +0 -379
  409. package/cli/doctor.test.mjs +0 -350
  410. package/config/cline.json.template +0 -342
  411. package/packages/sdk/src/client.ts +0 -188
  412. package/packages/sdk/src/cline-events.ts +0 -134
  413. package/packages/sdk/src/cline-types.ts +0 -66
  414. package/packages/sdk/src/cline.ts +0 -339
  415. package/packages/sdk/src/errors.ts +0 -129
  416. package/packages/sdk/src/events.ts +0 -153
  417. package/packages/sdk/src/types.ts +0 -176
  418. package/packages/sdk/tests/client.test.ts +0 -217
  419. package/packages/sdk/tests/errors.test.ts +0 -108
  420. package/packages/sdk/tests/events.test.ts +0 -139
  421. package/packages/sdk/tests/fixtures/fetch-mock.ts +0 -152
  422. package/packages/sdk/tests/fixtures/sse-mock.ts +0 -30
  423. package/plugins/bizar/ARCHITECTURE.md +0 -142
  424. package/plugins/bizar/CONSTRAINTS.md +0 -67
  425. package/plugins/bizar/LICENSE +0 -21
  426. package/plugins/bizar/README.md +0 -448
  427. package/plugins/bizar/index.ts +0 -925
  428. package/plugins/bizar/package.json +0 -39
  429. package/plugins/bizar/scripts/check-forbidden-imports.sh +0 -33
  430. package/plugins/bizar/src/background-state.ts +0 -641
  431. package/plugins/bizar/src/background.ts +0 -1806
  432. package/plugins/bizar/src/cline-runner.ts +0 -203
  433. package/plugins/bizar/src/clineruntime.ts +0 -227
  434. package/plugins/bizar/src/commands-impl.ts +0 -151
  435. package/plugins/bizar/src/commands.ts +0 -1799
  436. package/plugins/bizar/src/compaction.d.mts +0 -48
  437. package/plugins/bizar/src/compaction.mjs +0 -192
  438. package/plugins/bizar/src/dashboard-client.ts +0 -233
  439. package/plugins/bizar/src/event-stream.ts +0 -606
  440. package/plugins/bizar/src/fingerprint.ts +0 -120
  441. package/plugins/bizar/src/handoff.ts +0 -79
  442. package/plugins/bizar/src/hooks/memory-flush-on-compact.ts +0 -123
  443. package/plugins/bizar/src/hooks/memory-inject.ts +0 -247
  444. package/plugins/bizar/src/hooks/memory-write-on-end.ts +0 -188
  445. package/plugins/bizar/src/hooks/skill-curator.ts +0 -180
  446. package/plugins/bizar/src/http-client.ts +0 -467
  447. package/plugins/bizar/src/key-rotation.ts +0 -218
  448. package/plugins/bizar/src/logger.ts +0 -144
  449. package/plugins/bizar/src/loop-engineering.ts +0 -241
  450. package/plugins/bizar/src/loop.ts +0 -176
  451. package/plugins/bizar/src/mistake-recovery.ts +0 -98
  452. package/plugins/bizar/src/odin.ts +0 -227
  453. package/plugins/bizar/src/options.ts +0 -470
  454. package/plugins/bizar/src/plan-fs.ts +0 -323
  455. package/plugins/bizar/src/reasoning-clean.ts +0 -454
  456. package/plugins/bizar/src/report.ts +0 -178
  457. package/plugins/bizar/src/research-prompt.ts +0 -35
  458. package/plugins/bizar/src/serve-info.ts +0 -228
  459. package/plugins/bizar/src/serve.ts +0 -496
  460. package/plugins/bizar/src/settings.ts +0 -349
  461. package/plugins/bizar/src/state.ts +0 -298
  462. package/plugins/bizar/src/tool-discipline.ts +0 -105
  463. package/plugins/bizar/src/tools/agent-browser.ts +0 -315
  464. package/plugins/bizar/src/tools/bg-collect.ts +0 -131
  465. package/plugins/bizar/src/tools/bg-get-comments.ts +0 -266
  466. package/plugins/bizar/src/tools/bg-kill.ts +0 -116
  467. package/plugins/bizar/src/tools/bg-pause.ts +0 -99
  468. package/plugins/bizar/src/tools/bg-report-progress.ts +0 -115
  469. package/plugins/bizar/src/tools/bg-resume.ts +0 -94
  470. package/plugins/bizar/src/tools/bg-send-message.ts +0 -223
  471. package/plugins/bizar/src/tools/bg-spawn.ts +0 -502
  472. package/plugins/bizar/src/tools/bg-status.ts +0 -130
  473. package/plugins/bizar/src/tools/graph-query.ts +0 -278
  474. package/plugins/bizar/src/tools/loop-engineering.ts +0 -193
  475. package/plugins/bizar/src/tools/memory-list.ts +0 -43
  476. package/plugins/bizar/src/tools/memory-read.ts +0 -69
  477. package/plugins/bizar/src/tools/memory-search.ts +0 -47
  478. package/plugins/bizar/src/tools/memory-write.ts +0 -54
  479. package/plugins/bizar/src/tools/open-kb.ts +0 -198
  480. package/plugins/bizar/src/tools/plan-action.ts +0 -785
  481. package/plugins/bizar/src/tools/read-glyph-feedback.ts +0 -191
  482. package/plugins/bizar/src/tools/sandbox.ts +0 -232
  483. package/plugins/bizar/src/tools/team-spawn.ts +0 -73
  484. package/plugins/bizar/src/tools/team-status.ts +0 -76
  485. package/plugins/bizar/src/tools/wait-for-feedback.ts +0 -415
  486. package/plugins/bizar/src/trajectory.ts +0 -104
  487. package/plugins/bizar/tests/README.md +0 -99
  488. package/plugins/bizar/tests/attach-handler-bug.test.ts +0 -169
  489. package/plugins/bizar/tests/background-state.test.ts +0 -277
  490. package/plugins/bizar/tests/background.test.ts +0 -402
  491. package/plugins/bizar/tests/block.test.ts +0 -195
  492. package/plugins/bizar/tests/canonical-key-order.test.ts +0 -75
  493. package/plugins/bizar/tests/clineruntime-config.test.ts +0 -283
  494. package/plugins/bizar/tests/commands-impl.test.ts +0 -316
  495. package/plugins/bizar/tests/commands.test.ts +0 -584
  496. package/plugins/bizar/tests/compaction.test.ts +0 -264
  497. package/plugins/bizar/tests/config.test.ts +0 -128
  498. package/plugins/bizar/tests/dashboard-client.test.ts +0 -159
  499. package/plugins/bizar/tests/dispose.test.ts +0 -336
  500. package/plugins/bizar/tests/event-stream.test.ts +0 -409
  501. package/plugins/bizar/tests/event.test.ts +0 -262
  502. package/plugins/bizar/tests/fingerprint.test.ts +0 -190
  503. package/plugins/bizar/tests/http-client.test.ts +0 -404
  504. package/plugins/bizar/tests/init-helpers.test.ts +0 -203
  505. package/plugins/bizar/tests/integration/slash-command.test.ts +0 -349
  506. package/plugins/bizar/tests/integration/tool-routing.test.ts +0 -98
  507. package/plugins/bizar/tests/key-rotation.test.ts +0 -396
  508. package/plugins/bizar/tests/loop-engineering.test.ts +0 -168
  509. package/plugins/bizar/tests/loop.test.ts +0 -397
  510. package/plugins/bizar/tests/memory-write-on-end.test.ts +0 -92
  511. package/plugins/bizar/tests/mistake-recovery.test.ts +0 -116
  512. package/plugins/bizar/tests/odin.test.ts +0 -125
  513. package/plugins/bizar/tests/options.test.ts +0 -329
  514. package/plugins/bizar/tests/reasoning-clean.test.ts +0 -422
  515. package/plugins/bizar/tests/safety.test.ts +0 -281
  516. package/plugins/bizar/tests/serve.test.ts +0 -339
  517. package/plugins/bizar/tests/settings.test.ts +0 -351
  518. package/plugins/bizar/tests/stall-think.test.ts +0 -750
  519. package/plugins/bizar/tests/state.test.ts +0 -276
  520. package/plugins/bizar/tests/tool-discipline.test.ts +0 -77
  521. package/plugins/bizar/tests/tools/agent-browser.test.ts +0 -98
  522. package/plugins/bizar/tests/tools/bg-collect.test.ts +0 -337
  523. package/plugins/bizar/tests/tools/bg-get-comments.test.ts +0 -485
  524. package/plugins/bizar/tests/tools/bg-kill.test.ts +0 -235
  525. package/plugins/bizar/tests/tools/bg-pause.test.ts +0 -61
  526. package/plugins/bizar/tests/tools/bg-report-progress.test.ts +0 -79
  527. package/plugins/bizar/tests/tools/bg-resume.test.ts +0 -40
  528. package/plugins/bizar/tests/tools/bg-send-message.test.ts +0 -116
  529. package/plugins/bizar/tests/tools/bg-spawn-delegation.test.ts +0 -147
  530. package/plugins/bizar/tests/tools/bg-spawn-http.test.ts +0 -233
  531. package/plugins/bizar/tests/tools/bg-spawn.test.ts +0 -311
  532. package/plugins/bizar/tests/tools/bg-status.test.ts +0 -217
  533. package/plugins/bizar/tests/tools/cline-runner.test.ts +0 -115
  534. package/plugins/bizar/tests/tools/plan-action.test.ts +0 -599
  535. package/plugins/bizar/tests/tools/read-glyph-feedback.test.ts +0 -253
  536. package/plugins/bizar/tests/tools/sandbox.test.ts +0 -117
  537. package/plugins/bizar/tests/tools/wait-for-feedback.test.ts +0 -390
  538. package/plugins/bizar/tests/update-deadlock.test.ts +0 -151
  539. package/plugins/bizar/tsconfig.json +0 -29
  540. package/scripts/mirror-agents-md.sh +0 -69
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Tests for the router orchestrator (F-033).
3
+ *
4
+ * Verifies the precedence chain:
5
+ * explicit > codemod > q-learning > bandit > default
6
+ *
7
+ * Plus the surfaced-tags contract:
8
+ * [CODEMOD_AVAILABLE] <intent>
9
+ * [TASK_MODEL_RECOMMENDATION] <tier>
10
+ *
11
+ * Plus persistence: saveTo() writes state files; loadFrom() rehydrates
12
+ * the same priors.
13
+ */
14
+
15
+ import { describe, test, expect } from "vitest";
16
+ import { mkdtempSync, existsSync, readFileSync } from "node:fs";
17
+ import { tmpdir } from "node:os";
18
+ import { join } from "node:path";
19
+
20
+ import {
21
+ decideAgentWith,
22
+ getRouter,
23
+ ModelRouter,
24
+ QLearningRouter,
25
+ codemodTag,
26
+ tierTag,
27
+ DEFAULT_MODEL_STATE_PATH,
28
+ DEFAULT_Q_STATE_PATH,
29
+ } from "../src/router/index.js";
30
+
31
+ describe("decideAgentWith — precedence chain", () => {
32
+ test("explicit agent wins (when known)", () => {
33
+ const mr = new ModelRouter({ seed: 42 });
34
+ const qr = new QLearningRouter({ seed: 42 });
35
+ const d = decideAgentWith(mr, qr, {
36
+ task: "convert var to const",
37
+ explicitAgent: "odin",
38
+ });
39
+ expect(d.agent).toBe("odin");
40
+ expect(d.agentConfidence).toBe(1.0);
41
+ expect(d.codemodIntent).toBeNull();
42
+ // Explicit path still emits a [TASK_MODEL_RECOMMENDATION] tag.
43
+ expect(d.surfacedTags.some((t) => t.startsWith("[TASK_MODEL_RECOMMENDATION]"))).toBe(true);
44
+ expect(d.surfacedTags.some((t) => t.startsWith("[CODEMOD_AVAILABLE]"))).toBe(false);
45
+ });
46
+
47
+ test("codemod intent short-circuits before q-learning", () => {
48
+ const mr = new ModelRouter({ seed: 42 });
49
+ const qr = new QLearningRouter({ seed: 42 });
50
+ const d = decideAgentWith(mr, qr, { task: "convert var to const" });
51
+ expect(d.agent).toBe("heimdall");
52
+ expect(d.agentConfidence).toBe(1.0);
53
+ expect(d.codemodIntent).toBe("var-to-const");
54
+ expect(d.modelTier).toBe("flash");
55
+ // Both tags should be surfaced for a codemod hit.
56
+ expect(d.surfacedTags.some((t) => t.includes("CODEMOD_AVAILABLE"))).toBe(true);
57
+ expect(d.surfacedTags.some((t) => t.includes("TASK_MODEL_RECOMMENDATION"))).toBe(true);
58
+ });
59
+
60
+ test("non-codemod task routes via q-learning + bandit", () => {
61
+ const mr = new ModelRouter({ seed: 42 });
62
+ const qr = new QLearningRouter({ seed: 42 });
63
+ const d = decideAgentWith(mr, qr, { task: "design a database schema" });
64
+ expect(["odin", "frigg", "vor", "mimir", "heimdall", "thor", "tyr", "forseti"])
65
+ .toContain(d.agent);
66
+ expect(d.codemodIntent).toBeNull();
67
+ expect(["flash", "mid", "expensive"]).toContain(d.modelTier);
68
+ expect(d.surfacedTags.length).toBe(1);
69
+ expect(d.surfacedTags[0]).toMatch(/^\[TASK_MODEL_RECOMMENDATION\]/);
70
+ });
71
+
72
+ test("unknown explicit agent falls through to q-learning", () => {
73
+ const mr = new ModelRouter({ seed: 42 });
74
+ const qr = new QLearningRouter({ seed: 42 });
75
+ const d = decideAgentWith(mr, qr, { task: "hello world", explicitAgent: "totally-unknown" });
76
+ expect(d.agent).not.toBe("totally-unknown");
77
+ expect(AGENT_SET.has(d.agent)).toBe(true);
78
+ });
79
+ });
80
+
81
+ const AGENT_SET = new Set(["odin", "frigg", "vor", "mimir", "heimdall", "thor", "tyr", "forseti"]);
82
+
83
+ describe("tag formatters", () => {
84
+ test("codemodTag includes intent and confidence", () => {
85
+ const tag = codemodTag("var-to-const", 0.71);
86
+ expect(tag).toContain("[CODEMOD_AVAILABLE]");
87
+ expect(tag).toContain("var-to-const");
88
+ expect(tag).toMatch(/conf=0\.71/);
89
+ });
90
+
91
+ test("tierTag includes tier and confidence", () => {
92
+ const tag = tierTag({ tier: "flash", confidence: 0.42 });
93
+ expect(tag).toContain("[TASK_MODEL_RECOMMENDATION]");
94
+ expect(tag).toContain("flash");
95
+ expect(tag).toMatch(/conf=0\.42/);
96
+ });
97
+ });
98
+
99
+ describe("getRouter() — factory + persistence", () => {
100
+ test("with custom routers is in-memory only", () => {
101
+ const mr = new ModelRouter({ seed: 1 });
102
+ const qr = new QLearningRouter({ seed: 1 });
103
+ const bundle = getRouter({ modelRouter: mr, qRouter: qr });
104
+ expect(bundle.modelRouter).toBe(mr);
105
+ expect(bundle.qRouter).toBe(qr);
106
+ const d = bundle.decideAgent({ task: "convert var to const" });
107
+ expect(d.codemodIntent).toBe("var-to-const");
108
+ });
109
+
110
+ test("default state paths are .harness/router-state.{model,q}", () => {
111
+ expect(DEFAULT_MODEL_STATE_PATH).toBe(".harness/router-state.json");
112
+ expect(DEFAULT_Q_STATE_PATH).toBe(".harness/q-router-state.json");
113
+ });
114
+
115
+ test("saveTo writes the bundle pointer + per-router state files", () => {
116
+ const dir = mkdtempSync(join(tmpdir(), "bizar-router-"));
117
+ try {
118
+ const mr = new ModelRouter({ seed: 5 });
119
+ const qr = new QLearningRouter({ seed: 5 });
120
+ // Force the persistence files into a temp dir by manipulating
121
+ // the bundle basePath through saveTo's sibling files.
122
+ const bundle = getRouter({ modelRouter: mr, qRouter: qr });
123
+ // Override the cwd-derived basePath so the save lands in `dir`.
124
+ const basePath = join(dir, "router-state");
125
+ const modelPath = `${basePath}.model`;
126
+ const qPath = `${basePath}.q`;
127
+ mr.saveTo(modelPath);
128
+ qr.saveTo(qPath);
129
+ expect(existsSync(modelPath)).toBe(true);
130
+ expect(existsSync(qPath)).toBe(true);
131
+ const state = JSON.parse(readFileSync(modelPath, "utf-8"));
132
+ expect(state.version).toBe(1);
133
+ expect(state.priors.flash.alpha).toBe(2);
134
+ } finally {
135
+ // mkdtempSync + recursive rm
136
+ const { rmSync } = require("node:fs");
137
+ rmSync(dir, { recursive: true, force: true });
138
+ }
139
+ });
140
+
141
+ test("ModelRouter.loadFrom rehydrates priors", () => {
142
+ const dir = mkdtempSync(join(tmpdir(), "bizar-router-"));
143
+ try {
144
+ const fp = join(dir, "router.json");
145
+ const original = new ModelRouter({ seed: 42 });
146
+ original.recordOutcome("flash", true);
147
+ original.recordOutcome("flash", true);
148
+ original.saveTo(fp);
149
+
150
+ const loaded = ModelRouter.loadFrom(fp);
151
+ const priors = loaded.getPriors();
152
+ expect(priors.flash.alpha).toBeGreaterThan(2);
153
+ } finally {
154
+ const { rmSync } = require("node:fs");
155
+ rmSync(dir, { recursive: true, force: true });
156
+ }
157
+ });
158
+ });
159
+
160
+ describe("decideAgent — codemod short-circuit covers all three intents", () => {
161
+ test.each([
162
+ ["convert var to const", "var-to-const"],
163
+ ["remove console.log calls", "remove-console"],
164
+ ["add logging to the handler", "add-logging"],
165
+ ])("codemod surface for: %s", (prompt, expected) => {
166
+ const mr = new ModelRouter({ seed: 42 });
167
+ const qr = new QLearningRouter({ seed: 42 });
168
+ const d = decideAgentWith(mr, qr, { task: prompt });
169
+ expect(d.codemodIntent).toBe(expected);
170
+ expect(d.agent).toBe("heimdall");
171
+ });
172
+ });
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Tests for codemod intent detection (F-033).
3
+ *
4
+ * 5+ positive prompts per deterministic intent (var-to-const,
5
+ * remove-console, add-logging) plus 5+ negatives that should NOT
6
+ * be codemod-eligible (the caller falls through to the bandit).
7
+ */
8
+
9
+ import { describe, test, expect } from "vitest";
10
+ import { detectCodemodIntent } from "../src/router/codemod-intent.js";
11
+
12
+ describe("detectCodemodIntent — var-to-const", () => {
13
+ test("matches canonical phrasing", () => {
14
+ const hit = detectCodemodIntent("convert var to const");
15
+ expect(hit).not.toBeNull();
16
+ expect(hit?.intent).toBe("var-to-const");
17
+ expect(hit?.confidence).toBeGreaterThan(0);
18
+ });
19
+
20
+ test.each([
21
+ "change var to const in this file",
22
+ "replace var with const",
23
+ "use const instead of var",
24
+ "var to const for all declarations",
25
+ "convert var declarations to const",
26
+ "change var declarations to const everywhere",
27
+ ])("matches var-to-const phrasing: %s", (prompt) => {
28
+ const hit = detectCodemodIntent(prompt);
29
+ expect(hit?.intent).toBe("var-to-const");
30
+ });
31
+ });
32
+
33
+ describe("detectCodemodIntent — remove-console", () => {
34
+ test.each([
35
+ "remove console.log calls",
36
+ "delete console statements",
37
+ "strip console logs",
38
+ "clean up console",
39
+ "remove all debug logs",
40
+ "remove console.log statements everywhere",
41
+ ])("matches remove-console phrasing: %s", (prompt) => {
42
+ const hit = detectCodemodIntent(prompt);
43
+ expect(hit?.intent).toBe("remove-console");
44
+ });
45
+ });
46
+
47
+ describe("detectCodemodIntent — add-logging", () => {
48
+ test.each([
49
+ "add logging to the handler",
50
+ "add console.log statements",
51
+ "add debug logs to the module",
52
+ "log this function for diagnostics",
53
+ "add trace logging",
54
+ "instrument with logs",
55
+ ])("matches add-logging phrasing: %s", (prompt) => {
56
+ const hit = detectCodemodIntent(prompt);
57
+ expect(hit?.intent).toBe("add-logging");
58
+ });
59
+ });
60
+
61
+ describe("detectCodemodIntent — negatives", () => {
62
+ test.each([
63
+ "design a database schema for inventory",
64
+ "implement OAuth2 PKCE flow with refresh-token rotation",
65
+ "debug a memory leak in the cache",
66
+ "explain what this function does",
67
+ "refactor the http client to use async await",
68
+ "add type annotations to the API",
69
+ "wrap the parser in try/catch",
70
+ ])("rejects non-codemod prompt: %s", (prompt) => {
71
+ expect(detectCodemodIntent(prompt)).toBeNull();
72
+ });
73
+
74
+ test("returns null for empty input", () => {
75
+ expect(detectCodemodIntent("")).toBeNull();
76
+ });
77
+
78
+ test("returns null for non-string input", () => {
79
+ expect(detectCodemodIntent(null)).toBeNull();
80
+ expect(detectCodemodIntent(undefined)).toBeNull();
81
+ expect(detectCodemodIntent(42)).toBeNull();
82
+ });
83
+ });
84
+
85
+ describe("detectCodemodIntent — confidence ordering", () => {
86
+ test("multi-match returns the highest-confidence intent", () => {
87
+ const hit = detectCodemodIntent("convert var to const, replace var with const, change var declarations to const");
88
+ expect(hit?.intent).toBe("var-to-const");
89
+ expect(hit?.confidence).toBeGreaterThan(0.4);
90
+ });
91
+ });
@@ -0,0 +1,172 @@
1
+ /**
2
+ * SDK smoke tests — exercise the public surface.
3
+ *
4
+ * Run via: npm run test:sdk
5
+ * (or: node --test packages/sdk/tests/sdk.test.mjs)
6
+ *
7
+ * Uses vitest. The suite covers:
8
+ * - dist artefacts present after build:sdk
9
+ * - package.json exports map
10
+ * - SDK module surfaces (memory, fingerprint, dangerous-patterns, mcp)
11
+ * - dangerous-pattern behaviour (rm -rf / denied)
12
+ * - fingerprint stability
13
+ * - frontmatter round-trip
14
+ */
15
+
16
+ import { describe, test, expect } from "vitest";
17
+ import { existsSync, readFileSync } from "node:fs";
18
+ import { join } from "node:path";
19
+ import { fileURLToPath } from "node:url";
20
+
21
+ const here = fileURLToPath(new URL(".", import.meta.url));
22
+ const distDir = join(here, "..", "dist");
23
+ const pkgPath = join(here, "..", "package.json");
24
+
25
+ describe("SDK build", () => {
26
+ test("all dist artefacts present", () => {
27
+ for (const f of [
28
+ "index.js", "index.d.ts",
29
+ "dangerous-patterns.js", "dangerous-patterns.d.ts",
30
+ "fingerprint.js", "fingerprint.d.ts",
31
+ "memory/index.js", "memory/index.d.ts",
32
+ "mcp/server.js", "mcp/server.d.ts",
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",
39
+ ]) {
40
+ expect(existsSync(join(distDir, f))).toBe(true);
41
+ }
42
+ });
43
+
44
+ test("package.json exports map declares memory, mcp, dangerous-patterns", async () => {
45
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
46
+ expect(pkg.name).toBe("@polderlabs/bizar-sdk");
47
+ expect(pkg.exports["."]).toBeTruthy();
48
+ expect(pkg.exports["./memory"]).toBeTruthy();
49
+ expect(pkg.exports["./mcp"]).toBeTruthy();
50
+ expect(pkg.exports["./dangerous-patterns"]).toBeTruthy();
51
+ });
52
+ });
53
+
54
+ describe("SDK module surface", () => {
55
+ test("dangerous-patterns module exports the expected API", async () => {
56
+ const mod = await import("../dist/dangerous-patterns.js");
57
+ expect(typeof mod.checkDangerous).toBe("function");
58
+ expect(typeof mod.listDangerousPatterns).toBe("function");
59
+ expect(typeof mod.getDangerousPatternStats).toBe("function");
60
+ });
61
+
62
+ test("fingerprint module exports a function", async () => {
63
+ const mod = await import("../dist/fingerprint.js");
64
+ expect(typeof mod.fingerprint).toBe("function");
65
+ });
66
+
67
+ test("memory module exports vault helpers", async () => {
68
+ const mod = await import("../dist/memory/index.js");
69
+ expect(typeof mod.readNote).toBe("function");
70
+ expect(typeof mod.writeNote).toBe("function");
71
+ expect(typeof mod.listNotes).toBe("function");
72
+ expect(typeof mod.searchNotes).toBe("function");
73
+ expect(typeof mod.parseFrontmatter).toBe("function");
74
+ expect(typeof mod.serializeFrontmatter).toBe("function");
75
+ expect(typeof mod.resolveVaultRoot).toBe("function");
76
+ expect(typeof mod.DEFAULT_MEMORY_VAULT).toBe("string");
77
+ });
78
+
79
+ test("MCP server module exposes ≥20 BIZAR_TOOLS (13 core + 4 swarm + 3 self-learning)", async () => {
80
+ const mod = await import("../dist/mcp/server.js");
81
+ expect(Array.isArray(mod.BIZAR_TOOLS)).toBe(true);
82
+ expect(mod.BIZAR_TOOLS.length).toBeGreaterThanOrEqual(20);
83
+ expect(typeof mod.createBizarMcpServer).toBe("function");
84
+ expect(typeof mod.createBizarMcpServerConfig).toBe("function");
85
+ expect(typeof mod.defineTool).toBe("function");
86
+ expect(typeof mod.getBizarMcpToolSummary).toBe("function");
87
+
88
+ const expectedNames = new Set([
89
+ "memory_read", "memory_write", "memory_list", "memory_search",
90
+ "plan_action", "open_kb",
91
+ "loop_list", "loop_status", "loop_start", "loop_stop",
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",
97
+ ]);
98
+ const have = new Set(mod.BIZAR_TOOLS.map((t) => t.name));
99
+ for (const n of expectedNames) {
100
+ expect(have.has(n)).toBe(true);
101
+ }
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
+ });
118
+ });
119
+
120
+ describe("dangerous-patterns behaviour", () => {
121
+ test("denies rm -rf /", async () => {
122
+ const { checkDangerous } = await import("../dist/dangerous-patterns.js");
123
+ const result = checkDangerous("rm -rf /");
124
+ expect(result.decision).toBe("deny");
125
+ expect(typeof result.reason).toBe("string");
126
+ expect(result.reason.length).toBeGreaterThan(0);
127
+ });
128
+
129
+ test("allows echo", async () => {
130
+ const { checkDangerous } = await import("../dist/dangerous-patterns.js");
131
+ const result = checkDangerous("echo hello world");
132
+ expect(result.decision).toBe("allow");
133
+ });
134
+ });
135
+
136
+ describe("fingerprint stability", () => {
137
+ test("same input produces same fingerprint", async () => {
138
+ const { fingerprint } = await import("../dist/fingerprint.js");
139
+ const a = fingerprint("Bash", { command: "ls -la /tmp" });
140
+ const b = fingerprint("Bash", { command: "ls -la /tmp" });
141
+ expect(a).toBe(b);
142
+ expect(typeof a).toBe("string");
143
+ expect(a.length).toBeGreaterThanOrEqual(16);
144
+ });
145
+
146
+ test("different tools produce different fingerprints", async () => {
147
+ const { fingerprint } = await import("../dist/fingerprint.js");
148
+ const a = fingerprint("Bash", { command: "ls" });
149
+ const b = fingerprint("Edit", { command: "ls" });
150
+ expect(a).not.toBe(b);
151
+ });
152
+ });
153
+
154
+ describe("frontmatter round-trip", () => {
155
+ test("parses simple values", async () => {
156
+ const { parseFrontmatter } = await import("../dist/memory/index.js");
157
+ const md = "---\ntitle: Test\ncount: 3\n---\nHello world";
158
+ const parsed = parseFrontmatter(md);
159
+ expect(parsed.frontmatter.title).toBe("Test");
160
+ expect(parsed.frontmatter.count).toBe("3");
161
+ expect(parsed.body.trim()).toBe("Hello world");
162
+ });
163
+
164
+ test("serialize + parse preserves array form", async () => {
165
+ const { parseFrontmatter, serializeFrontmatter } = await import("../dist/memory/index.js");
166
+ const md = serializeFrontmatter({ title: "Test", tags: ["a", "b"] }, "\nbody");
167
+ expect(md.startsWith("---")).toBe(true);
168
+ const parsed = parseFrontmatter(md);
169
+ expect(parsed.frontmatter.title).toBe("Test");
170
+ expect(md.includes("tags: [a, b]")).toBe(true);
171
+ });
172
+ });
@@ -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
+ });