@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.
- package/.claude/CLAUDE.md +15 -0
- package/.claude/agents/_shared/AGENT_BASELINE.md +168 -0
- package/.claude/agents/_shared/CLAUDE_TOOLS.md +412 -0
- package/.claude/agents/_shared/SKILLS.md +109 -0
- package/.claude/agents/agent-browser.md +80 -0
- package/.claude/agents/baldr.md +49 -0
- package/.claude/agents/forseti.md +51 -0
- package/.claude/agents/frigg.md +42 -0
- package/.claude/agents/heimdall.md +33 -0
- package/.claude/agents/hermod.md +53 -0
- package/.claude/agents/mimir.md +49 -0
- package/.claude/agents/odin.md +287 -0
- package/.claude/agents/quick.md +34 -0
- package/.claude/agents/semble-search.md +50 -0
- package/.claude/agents/thor.md +53 -0
- package/.claude/agents/tyr.md +56 -0
- package/.claude/agents/vidarr.md +54 -0
- package/.claude/agents/vor.md +53 -0
- package/.claude/commands/audit.md +25 -0
- package/.claude/commands/bizar.md +22 -0
- package/.claude/commands/explain.md +17 -0
- package/.claude/commands/init.md +29 -0
- package/.claude/commands/learn.md +44 -0
- package/.claude/commands/plan.md +35 -0
- package/.claude/commands/plow-through.md +50 -0
- package/.claude/commands/pr-review.md +49 -0
- package/.claude/commands/setup-provider.md +96 -0
- package/.claude/commands/tailscale-serve.md +100 -0
- package/.claude/commands/team.md +132 -0
- package/.claude/commands/test.md +62 -0
- package/.claude/commands/validate.md +68 -0
- package/.claude/commands/visual-plan.md +24 -0
- package/.claude/hooks/README.md +92 -0
- package/.claude/hooks/posttooluse-editwrite.mjs +91 -0
- package/.claude/hooks/pretooluse-bash.mjs +81 -0
- package/.claude/hooks/pretooluse-editwrite.mjs +139 -0
- package/.claude/hooks/sessionend-recall.mjs +74 -0
- package/.claude/hooks/sessionstart-prime.mjs +80 -0
- package/.claude/hooks/userpromptsubmit-tag.mjs +80 -0
- package/.claude/hooks/worker-suggest.mjs +110 -0
- package/.claude/settings.json +125 -0
- package/.claude/skills/9router/SKILL.md +80 -0
- package/.claude/skills/9router-chat/SKILL.md +73 -0
- package/.claude/skills/9router-embeddings/SKILL.md +69 -0
- package/.claude/skills/9router-image/SKILL.md +86 -0
- package/.claude/skills/9router-stt/SKILL.md +79 -0
- package/.claude/skills/9router-tts/SKILL.md +80 -0
- package/.claude/skills/9router-web-fetch/SKILL.md +99 -0
- package/.claude/skills/9router-web-search/SKILL.md +91 -0
- package/.claude/skills/bizar/README.md +9 -0
- package/.claude/skills/bizar/SKILL.md +450 -0
- package/.claude/skills/cpp-coding-standards/README.md +28 -0
- package/.claude/skills/cpp-coding-standards/SKILL.md +634 -0
- package/.claude/skills/cpp-coding-standards/references/concurrency.md +320 -0
- package/.claude/skills/cpp-coding-standards/references/error-handling.md +229 -0
- package/.claude/skills/cpp-coding-standards/references/memory-safety.md +216 -0
- package/.claude/skills/cpp-coding-standards/references/modern-idioms.md +282 -0
- package/.claude/skills/cpp-coding-standards/references/review-checklist.md +96 -0
- package/.claude/skills/cpp-testing/README.md +28 -0
- package/.claude/skills/cpp-testing/SKILL.md +304 -0
- package/.claude/skills/cpp-testing/references/coverage.md +370 -0
- package/.claude/skills/cpp-testing/references/framework-compare.md +175 -0
- package/.claude/skills/cpp-testing/references/host-test-for-embedded.md +499 -0
- package/.claude/skills/cpp-testing/references/mocking.md +364 -0
- package/.claude/skills/cpp-testing/references/tdd-workflow.md +308 -0
- package/.claude/skills/cubesandbox/SKILL.md +148 -0
- package/.claude/skills/embedded-esp-idf/README.md +41 -0
- package/.claude/skills/embedded-esp-idf/SKILL.md +439 -0
- package/.claude/skills/embedded-esp-idf/references/freertos-patterns.md +214 -0
- package/.claude/skills/embedded-esp-idf/references/host-tests.md +164 -0
- package/.claude/skills/embedded-esp-idf/references/idf-py-commands.md +157 -0
- package/.claude/skills/embedded-esp-idf/references/kconfig.md +159 -0
- package/.claude/skills/embedded-esp-idf/references/logging-discipline.md +118 -0
- package/.claude/skills/embedded-esp-idf/references/memory-and-iram.md +137 -0
- package/.claude/skills/embedded-esp-idf/references/nvs.md +121 -0
- package/.claude/skills/embedded-esp-idf/references/packed-structs.md +192 -0
- package/.claude/skills/embedded-esp-idf/scripts/idf_env.sh +47 -0
- package/.claude/skills/embedded-esp-idf/scripts/size_check.sh +77 -0
- package/.claude/skills/glyph/SKILL.md +163 -0
- package/.claude/skills/harness-engineering/SKILL.md +143 -0
- package/.claude/skills/lightrag/SKILL.md +81 -0
- package/.claude/skills/memory-protocol/SKILL.md +105 -0
- package/.claude/skills/obsidian/SKILL.md +306 -0
- package/.claude/skills/read-the-damn-docs/SKILL.md +113 -0
- package/.claude/skills/self-improvement/SKILL.md +64 -0
- package/README.md +87 -59
- package/bizar-dash/dist/assets/{EnvVarsSection-DXM8gRm_.js → EnvVarsSection-CW6fWKds.js} +3 -3
- package/bizar-dash/dist/assets/{EnvVarsSection-DXM8gRm_.js.map → EnvVarsSection-CW6fWKds.js.map} +1 -1
- package/bizar-dash/dist/assets/MobileChat-D7zRhU9K.js +1 -0
- package/bizar-dash/dist/assets/{MobileChat-BnKN_Ks_.js.map → MobileChat-D7zRhU9K.js.map} +1 -1
- package/bizar-dash/dist/assets/MobileSettings-K-sNsn_r.js +1 -0
- package/bizar-dash/dist/assets/{MobileSettings-DjCPxC-Q.js.map → MobileSettings-K-sNsn_r.js.map} +1 -1
- package/bizar-dash/dist/assets/{Toast-Cpl6-E63.js → Toast-WTxc3m2g.js} +1 -1
- package/bizar-dash/dist/assets/{Toast-Cpl6-E63.js.map → Toast-WTxc3m2g.js.map} +1 -1
- package/bizar-dash/dist/assets/flow-BpU0-gK2.js +1 -0
- package/bizar-dash/dist/assets/flow-BpU0-gK2.js.map +1 -0
- package/bizar-dash/dist/assets/fuzzy-l0sNRNKZ.js +1 -0
- package/bizar-dash/dist/assets/fuzzy-l0sNRNKZ.js.map +1 -0
- package/bizar-dash/dist/assets/{icons-Do5N2jmX.js → icons-B0AKnYI3.js} +98 -103
- package/bizar-dash/dist/assets/icons-B0AKnYI3.js.map +1 -0
- package/bizar-dash/dist/assets/main-C-Md3DaY.css +1 -0
- package/bizar-dash/dist/assets/main-CO8fIxQK.js +18 -0
- package/bizar-dash/dist/assets/main-CO8fIxQK.js.map +1 -0
- package/bizar-dash/dist/assets/markdown-w2bDDkAH.js +1 -0
- package/bizar-dash/dist/assets/{markdown-C6mXtQxD.js.map → markdown-w2bDDkAH.js.map} +1 -1
- package/bizar-dash/dist/assets/mobile-DXYCA0hC.js +1 -0
- package/bizar-dash/dist/assets/{mobile-CMHqtLV2.js.map → mobile-DXYCA0hC.js.map} +1 -1
- package/bizar-dash/dist/assets/mobile-layout-DSmtNPja.js +2 -0
- package/bizar-dash/dist/assets/mobile-layout-DSmtNPja.js.map +1 -0
- package/bizar-dash/dist/assets/react-vendor-BnuhLJ6X.css +1 -0
- package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js +46 -0
- package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js.map +1 -0
- package/bizar-dash/dist/assets/useSlashCommands-3DFLOfpk.js +2 -0
- package/bizar-dash/dist/assets/useSlashCommands-3DFLOfpk.js.map +1 -0
- package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js +45 -0
- package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js.map +1 -0
- package/bizar-dash/dist/index.html +12 -10
- package/bizar-dash/dist/mobile.html +7 -4
- package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/bizar-dash/src/server/api.mjs +15 -4
- package/bizar-dash/src/server/background-store.mjs +15 -16
- package/bizar-dash/src/server/bg-poller.mjs +25 -35
- package/bizar-dash/src/server/bg-retry.mjs +66 -194
- package/bizar-dash/src/server/claude-bg-spawner.mjs +390 -0
- package/bizar-dash/src/server/claude-info.mjs +412 -0
- package/bizar-dash/src/server/{cline-runner.mjs → claude-runner.mjs} +53 -48
- package/bizar-dash/src/server/claude-sdk.mjs +191 -0
- package/bizar-dash/src/server/memory-consolidator.mjs +218 -0
- package/bizar-dash/src/server/memory-distillation.mjs +296 -0
- package/bizar-dash/src/server/providers-store.mjs +22 -49
- package/bizar-dash/src/server/routes/background.mjs +24 -20
- package/bizar-dash/src/server/routes/chat.mjs +223 -326
- package/bizar-dash/src/server/routes/claude-session-detail.mjs +329 -0
- package/bizar-dash/src/server/routes/claude-sessions.mjs +259 -0
- package/bizar-dash/src/server/routes/distill.mjs +129 -0
- package/bizar-dash/src/server/routes/goal-planner.mjs +236 -0
- package/bizar-dash/src/server/routes/tasks.mjs +20 -28
- package/bizar-dash/src/server/task-delegator.mjs +76 -77
- package/bizar-dash/src/web/App.tsx +4 -0
- package/bizar-dash/src/web/components/Tag.tsx +18 -5
- package/bizar-dash/src/web/components/Topbar.tsx +3 -0
- package/bizar-dash/src/web/components/agents/CommunicationLog.tsx +126 -0
- package/bizar-dash/src/web/components/agents/DependencyGraph.tsx +163 -0
- package/bizar-dash/src/web/components/agents/QualityGates.tsx +123 -0
- package/bizar-dash/src/web/components/agents/RealTimeEventLog.tsx +127 -0
- package/bizar-dash/src/web/components/agents/RoutingDecisions.test.tsx +148 -0
- package/bizar-dash/src/web/components/agents/RoutingDecisions.tsx +160 -0
- package/bizar-dash/src/web/components/chat/useChat.ts +23 -27
- package/bizar-dash/src/web/components/goals/GoalInput.tsx +86 -0
- package/bizar-dash/src/web/components/goals/PlanVisualization.tsx +111 -0
- package/bizar-dash/src/web/lib/goapPlanner.ts +614 -0
- package/bizar-dash/src/web/lib/types.ts +25 -0
- package/bizar-dash/src/web/styles/agents-redesign.css +311 -0
- package/bizar-dash/src/web/styles/main.css +251 -0
- package/bizar-dash/src/web/styles/tasks-redesign.css +231 -0
- package/bizar-dash/src/web/ui/controls/Button.tsx +77 -0
- package/bizar-dash/src/web/ui/controls/Checkbox.tsx +95 -0
- package/bizar-dash/src/web/ui/controls/IconButton.tsx +66 -0
- package/bizar-dash/src/web/ui/controls/Kbd.tsx +24 -0
- package/bizar-dash/src/web/ui/controls/NumberInput.tsx +77 -0
- package/bizar-dash/src/web/ui/controls/RadioGroup.tsx +109 -0
- package/bizar-dash/src/web/ui/controls/SearchInput.tsx +91 -0
- package/bizar-dash/src/web/ui/controls/Select.tsx +92 -0
- package/bizar-dash/src/web/ui/controls/Slider.tsx +70 -0
- package/bizar-dash/src/web/ui/controls/TextInput.tsx +67 -0
- package/bizar-dash/src/web/ui/controls/Toggle.tsx +79 -0
- package/bizar-dash/src/web/ui/controls/controls.css +454 -0
- package/bizar-dash/src/web/ui/controls/index.ts +33 -0
- package/bizar-dash/src/web/ui/data/BarChart.tsx +60 -0
- package/bizar-dash/src/web/ui/data/DataTable.tsx +216 -0
- package/bizar-dash/src/web/ui/data/EmptyState.tsx +60 -0
- package/bizar-dash/src/web/ui/data/ErrorState.tsx +56 -0
- package/bizar-dash/src/web/ui/data/KeyValueList.tsx +106 -0
- package/bizar-dash/src/web/ui/data/LoadingState.tsx +65 -0
- package/bizar-dash/src/web/ui/data/Sparkline.tsx +87 -0
- package/bizar-dash/src/web/ui/data/StatTile.tsx +87 -0
- package/bizar-dash/src/web/ui/data/data.css +535 -0
- package/bizar-dash/src/web/ui/data/index.ts +31 -0
- package/bizar-dash/src/web/ui/feedback/Badge.tsx +49 -0
- package/bizar-dash/src/web/ui/feedback/Dialog.tsx +209 -0
- package/bizar-dash/src/web/ui/feedback/ProgressBar.tsx +69 -0
- package/bizar-dash/src/web/ui/feedback/StatusDot.tsx +50 -0
- package/bizar-dash/src/web/ui/feedback/Toast.tsx +200 -0
- package/bizar-dash/src/web/ui/feedback/Tooltip.tsx +126 -0
- package/bizar-dash/src/web/ui/feedback/feedback.css +314 -0
- package/bizar-dash/src/web/ui/feedback/index.ts +43 -0
- package/bizar-dash/src/web/ui/index.ts +56 -0
- package/bizar-dash/src/web/ui/layout/AppShell.tsx +51 -0
- package/bizar-dash/src/web/ui/layout/Breadcrumbs.tsx +97 -0
- package/bizar-dash/src/web/ui/layout/Panel.tsx +73 -0
- package/bizar-dash/src/web/ui/layout/PanelHeader.tsx +39 -0
- package/bizar-dash/src/web/ui/layout/Sidebar.tsx +131 -0
- package/bizar-dash/src/web/ui/layout/Tabs.tsx +133 -0
- package/bizar-dash/src/web/ui/layout/Topbar.tsx +42 -0
- package/bizar-dash/src/web/ui/layout/index.ts +33 -0
- package/bizar-dash/src/web/ui/layout/layout.css +413 -0
- package/bizar-dash/src/web/ui/navigation/CommandPalette.tsx +199 -0
- package/bizar-dash/src/web/ui/navigation/NavGroup.tsx +95 -0
- package/bizar-dash/src/web/ui/navigation/NavLink.tsx +93 -0
- package/bizar-dash/src/web/ui/navigation/index.ts +17 -0
- package/bizar-dash/src/web/ui/navigation/navigation.css +343 -0
- package/bizar-dash/src/web/ui/primitives/Box.tsx +91 -0
- package/bizar-dash/src/web/ui/primitives/Grid.tsx +49 -0
- package/bizar-dash/src/web/ui/primitives/Inline.tsx +55 -0
- package/bizar-dash/src/web/ui/primitives/Separator.tsx +46 -0
- package/bizar-dash/src/web/ui/primitives/Stack.tsx +54 -0
- package/bizar-dash/src/web/ui/primitives/VisuallyHidden.tsx +26 -0
- package/bizar-dash/src/web/ui/primitives/index.ts +23 -0
- package/bizar-dash/src/web/ui/primitives/primitives.css +183 -0
- package/bizar-dash/src/web/ui/styles/globals.css +86 -0
- package/bizar-dash/src/web/ui/styles/reset.css +125 -0
- package/bizar-dash/src/web/ui/styles/tokens.css +195 -0
- package/bizar-dash/src/web/ui/theme/ThemeProvider.tsx +142 -0
- package/bizar-dash/src/web/ui/theme/tokens.ts +131 -0
- package/bizar-dash/src/web/ui/theme/useTheme.ts +22 -0
- package/bizar-dash/src/web/ui/utils/cx.ts +42 -0
- package/bizar-dash/src/web/views/Agents.tsx +506 -266
- package/bizar-dash/src/web/views/Chat.tsx +8 -8
- package/bizar-dash/src/web/views/GoalPlanner.tsx +158 -0
- package/bizar-dash/src/web/views/Overview.tsx +996 -448
- package/bizar-dash/src/web/views/Tasks.tsx +277 -273
- package/bizar-dash/tests/communication-log.test.tsx +67 -0
- package/bizar-dash/tests/dep-graph.test.tsx +40 -0
- package/bizar-dash/tests/event-log.test.tsx +61 -0
- package/bizar-dash/tests/goal-input.test.tsx +72 -0
- package/bizar-dash/tests/goap-planner.test.ts +68 -0
- package/bizar-dash/tests/quality-gates.test.tsx +59 -0
- package/bizar-dash/tests/setup.ts +31 -1
- package/bizar-dash/tests/ui/controls/Button.test.tsx +50 -0
- package/bizar-dash/tests/ui/controls/Checkbox.test.tsx +44 -0
- package/bizar-dash/tests/ui/controls/IconButton.test.tsx +37 -0
- package/bizar-dash/tests/ui/controls/Kbd.test.tsx +39 -0
- package/bizar-dash/tests/ui/controls/NumberInput.test.tsx +56 -0
- package/bizar-dash/tests/ui/controls/RadioGroup.test.tsx +58 -0
- package/bizar-dash/tests/ui/controls/SearchInput.test.tsx +52 -0
- package/bizar-dash/tests/ui/controls/Select.test.tsx +66 -0
- package/bizar-dash/tests/ui/controls/Slider.test.tsx +41 -0
- package/bizar-dash/tests/ui/controls/TextInput.test.tsx +44 -0
- package/bizar-dash/tests/ui/controls/Toggle.test.tsx +39 -0
- package/bizar-dash/tests/ui/data/BarChart.test.tsx +55 -0
- package/bizar-dash/tests/ui/data/DataTable.test.tsx +112 -0
- package/bizar-dash/tests/ui/data/EmptyState.test.tsx +57 -0
- package/bizar-dash/tests/ui/data/ErrorState.test.tsx +47 -0
- package/bizar-dash/tests/ui/data/KeyValueList.test.tsx +59 -0
- package/bizar-dash/tests/ui/data/LoadingState.test.tsx +37 -0
- package/bizar-dash/tests/ui/data/Sparkline.test.tsx +56 -0
- package/bizar-dash/tests/ui/data/StatTile.test.tsx +69 -0
- package/bizar-dash/tests/ui/feedback/Badge.test.tsx +41 -0
- package/bizar-dash/tests/ui/feedback/Dialog.test.tsx +99 -0
- package/bizar-dash/tests/ui/feedback/ProgressBar.test.tsx +66 -0
- package/bizar-dash/tests/ui/feedback/StatusDot.test.tsx +41 -0
- package/bizar-dash/tests/ui/feedback/Toast.test.tsx +124 -0
- package/bizar-dash/tests/ui/feedback/Tooltip.test.tsx +102 -0
- package/bizar-dash/tests/ui/layout/AppShell.test.tsx +50 -0
- package/bizar-dash/tests/ui/layout/Breadcrumbs.test.tsx +85 -0
- package/bizar-dash/tests/ui/layout/Panel.test.tsx +52 -0
- package/bizar-dash/tests/ui/layout/PanelHeader.test.tsx +32 -0
- package/bizar-dash/tests/ui/layout/Sidebar.test.tsx +95 -0
- package/bizar-dash/tests/ui/layout/Tabs.test.tsx +87 -0
- package/bizar-dash/tests/ui/layout/Topbar.test.tsx +41 -0
- package/bizar-dash/tests/ui/navigation/CommandPalette.test.tsx +105 -0
- package/bizar-dash/tests/ui/navigation/NavGroup.test.tsx +55 -0
- package/bizar-dash/tests/ui/navigation/NavLink.test.tsx +61 -0
- package/bizar-dash/tests/ui/primitives/Box.test.tsx +49 -0
- package/bizar-dash/tests/ui/primitives/Grid.test.tsx +37 -0
- package/bizar-dash/tests/ui/primitives/Inline.test.tsx +33 -0
- package/bizar-dash/tests/ui/primitives/Separator.test.tsx +34 -0
- package/bizar-dash/tests/ui/primitives/Stack.test.tsx +45 -0
- package/bizar-dash/tests/ui/primitives/VisuallyHidden.test.tsx +27 -0
- package/bizar-dash/tests/views/Agents.test.tsx +359 -0
- package/bizar-dash/tests/views/Overview.test.tsx +413 -0
- package/bizar-dash/tests/views/Tasks.test.tsx +250 -0
- package/bizar-dash/vitest.config.ts +3 -1
- package/cli/__tests__/cost-gate.test.mjs +301 -0
- package/cli/__tests__/feature-list-bridge.test.mjs +371 -0
- package/cli/bin.mjs +55 -18
- package/cli/commands/claim.mjs +282 -0
- package/cli/commands/claude-cmd.mjs +348 -0
- package/cli/commands/cost.mjs +279 -0
- package/cli/commands/install.mjs +12 -11
- package/cli/commands/validate.mjs +252 -325
- package/cli/cost-gate.mjs +501 -0
- package/cli/dev-link.test.mjs +11 -11
- package/cli/doctor.mjs +131 -213
- package/cli/feature-list-bridge.mjs +400 -0
- package/cli/install.mjs +33 -37
- package/cli/install.test.mjs +9 -9
- package/cli/memory-constants.mjs +17 -0
- package/cli/provision-claude.mjs +893 -0
- package/cli/provision.mjs +224 -227
- package/cli/utils.mjs +72 -58
- package/cli/worker-dispatcher.mjs +259 -0
- package/cli/worker-dispatcher.test.mjs +204 -0
- package/config/AGENTS.md +28 -28
- package/config/trigger-patterns.json +186 -0
- package/install.sh +66 -44
- package/package.json +19 -11
- package/packages/sdk/ARCHITECTURE.md +40 -36
- package/packages/sdk/package.json +15 -5
- package/packages/sdk/src/agent-registry.ts +304 -0
- package/packages/sdk/src/consensus/byzantine.ts +587 -0
- package/packages/sdk/src/consensus/index.ts +147 -0
- package/packages/sdk/src/consensus/queen.ts +173 -0
- package/packages/sdk/src/consensus/types.ts +199 -0
- package/{plugins/bizar → packages/sdk}/src/dangerous-patterns.ts +6 -28
- package/packages/sdk/src/federation/audit.ts +156 -0
- package/packages/sdk/src/federation/budget.ts +269 -0
- package/packages/sdk/src/federation/envelope.ts +144 -0
- package/packages/sdk/src/federation/hmac.ts +160 -0
- package/packages/sdk/src/federation/index.ts +443 -0
- package/packages/sdk/src/federation/pii.ts +241 -0
- package/packages/sdk/src/federation/policy.ts +112 -0
- package/packages/sdk/src/federation/trust.ts +146 -0
- package/packages/sdk/src/fingerprint.ts +0 -0
- package/packages/sdk/src/index.ts +101 -69
- package/packages/sdk/src/mcp/bin.ts +51 -0
- package/packages/sdk/src/mcp/server.ts +848 -0
- package/{plugins/bizar/src/memory-vault.ts → packages/sdk/src/memory/index.ts} +18 -21
- package/packages/sdk/src/router/codemod-intent.ts +125 -0
- package/packages/sdk/src/router/index.ts +247 -0
- package/packages/sdk/src/router/memory-distillation-shim.mjs +18 -0
- package/packages/sdk/src/router/memory-distillation.ts +249 -0
- package/packages/sdk/src/router/model-router.ts +235 -0
- package/packages/sdk/src/router/q-learning-router.ts +304 -0
- package/packages/sdk/src/swarm-topology.ts +279 -0
- package/packages/sdk/tests/agent-registry.test.ts +285 -0
- package/packages/sdk/tests/consensus.test.ts +376 -0
- package/packages/sdk/tests/federation/audit.test.ts +164 -0
- package/packages/sdk/tests/federation/budget.test.ts +189 -0
- package/packages/sdk/tests/federation/envelope.test.ts +120 -0
- package/packages/sdk/tests/federation/hmac.test.ts +194 -0
- package/packages/sdk/tests/federation/orchestrator.test.ts +176 -0
- package/packages/sdk/tests/federation/pii.test.ts +106 -0
- package/packages/sdk/tests/federation/policy.test.ts +140 -0
- package/packages/sdk/tests/federation/trust.test.ts +124 -0
- package/packages/sdk/tests/mcp-tools.test.ts +204 -0
- package/packages/sdk/tests/memory-distillation.test.mjs +186 -0
- package/packages/sdk/tests/model-router.test.mjs +108 -0
- package/packages/sdk/tests/q-learning-router.test.mjs +100 -0
- package/packages/sdk/tests/router-orchestrator.test.mjs +172 -0
- package/packages/sdk/tests/router.test.mjs +91 -0
- package/packages/sdk/tests/sdk.test.mjs +172 -0
- package/packages/sdk/tests/swarm-topology.test.ts +258 -0
- package/packages/sdk/vitest.config.ts +1 -1
- package/scripts/bh-full-e2e.mjs +167 -368
- package/scripts/check-deps.mjs +28 -46
- package/scripts/mirror-claude-md.sh +78 -0
- package/scripts/test-in-container.sh +31 -9
- package/templates/clean-state-checklist.md +3 -3
- package/templates/sprint-contract.md +2 -2
- package/bizar-dash/.bizar/activity.log +0 -3
- package/bizar-dash/.omx/logs/omx-2026-07-07.jsonl +0 -1
- package/bizar-dash/.omx/state/session.json +0 -10
- package/bizar-dash/bizar-design/EXPLANATION.md +0 -307
- package/bizar-dash/bizar-design/INSTRUCTIONS.md +0 -149
- package/bizar-dash/bizar-design/canvas.html +0 -281
- package/bizar-dash/bizar-design/canvas.html.artifact.json +0 -19
- package/bizar-dash/bizar-design/components.css +0 -1665
- package/bizar-dash/bizar-design/components.html +0 -557
- package/bizar-dash/bizar-design/components.html.artifact.json +0 -19
- package/bizar-dash/bizar-design/desktop/agents.html +0 -74
- package/bizar-dash/bizar-design/desktop/agents.html.artifact.json +0 -19
- package/bizar-dash/bizar-design/desktop/memory.html +0 -75
- package/bizar-dash/bizar-design/desktop/memory.html.artifact.json +0 -19
- package/bizar-dash/bizar-design/desktop/metrics.html +0 -63
- package/bizar-dash/bizar-design/desktop/metrics.html.artifact.json +0 -19
- package/bizar-dash/bizar-design/desktop/overview.html +0 -95
- package/bizar-dash/bizar-design/desktop/overview.html.artifact.json +0 -19
- package/bizar-dash/bizar-design/desktop/settings.html +0 -70
- package/bizar-dash/bizar-design/desktop/settings.html.artifact.json +0 -19
- package/bizar-dash/bizar-design/icons.svg +0 -44
- package/bizar-dash/bizar-design/index.html +0 -192
- package/bizar-dash/bizar-design/index.html.artifact.json +0 -19
- package/bizar-dash/bizar-design/mobile/agents.html +0 -29
- package/bizar-dash/bizar-design/mobile/agents.html.artifact.json +0 -19
- package/bizar-dash/bizar-design/mobile/memory.html +0 -28
- package/bizar-dash/bizar-design/mobile/memory.html.artifact.json +0 -19
- package/bizar-dash/bizar-design/mobile/metrics.html +0 -29
- package/bizar-dash/bizar-design/mobile/metrics.html.artifact.json +0 -19
- package/bizar-dash/bizar-design/mobile/overview.html +0 -33
- package/bizar-dash/bizar-design/mobile/overview.html.artifact.json +0 -19
- package/bizar-dash/bizar-design/mobile/settings.html +0 -28
- package/bizar-dash/bizar-design/mobile/settings.html.artifact.json +0 -19
- package/bizar-dash/bizar-design/prototype.js +0 -114
- package/bizar-dash/bizar-design/tokens.css +0 -106
- package/bizar-dash/dist/assets/MobileChat-BnKN_Ks_.js +0 -1
- package/bizar-dash/dist/assets/MobileSettings-DjCPxC-Q.js +0 -1
- package/bizar-dash/dist/assets/icons-Do5N2jmX.js.map +0 -1
- package/bizar-dash/dist/assets/main-DYiZqMrn.js +0 -18
- package/bizar-dash/dist/assets/main-DYiZqMrn.js.map +0 -1
- package/bizar-dash/dist/assets/main-ietCEg_R.css +0 -1
- package/bizar-dash/dist/assets/markdown-C6mXtQxD.js +0 -1
- package/bizar-dash/dist/assets/mobile-CMHqtLV2.js +0 -1
- package/bizar-dash/dist/assets/mobile-layout-3jIhHX_p.js +0 -2
- package/bizar-dash/dist/assets/mobile-layout-3jIhHX_p.js.map +0 -1
- package/bizar-dash/dist/assets/react-vendor-Dn4wqh4Z.js +0 -40
- package/bizar-dash/dist/assets/react-vendor-Dn4wqh4Z.js.map +0 -1
- package/bizar-dash/dist/assets/useSlashCommands-DtITw8Xv.js +0 -2
- package/bizar-dash/dist/assets/useSlashCommands-DtITw8Xv.js.map +0 -1
- package/bizar-dash/dist/assets/vendor-CeHGtduv.js +0 -29
- package/bizar-dash/dist/assets/vendor-CeHGtduv.js.map +0 -1
- package/bizar-dash/node_modules/.package-lock.json +0 -6
- package/bizar-dash/package-lock.json +0 -6
- package/bizar-dash/src/server/cline-sdk.mjs +0 -132
- package/bizar-dash/src/server/routes/cline-session-detail.mjs +0 -559
- package/bizar-dash/src/server/routes/cline-sessions.mjs +0 -291
- package/cli/commands/cline-cmd.mjs +0 -289
- package/cli/commands/validate.test.mjs +0 -379
- package/cli/doctor.test.mjs +0 -350
- package/config/cline.json.template +0 -342
- package/packages/sdk/src/client.ts +0 -188
- package/packages/sdk/src/cline-events.ts +0 -134
- package/packages/sdk/src/cline-types.ts +0 -66
- package/packages/sdk/src/cline.ts +0 -339
- package/packages/sdk/src/errors.ts +0 -129
- package/packages/sdk/src/events.ts +0 -153
- package/packages/sdk/src/types.ts +0 -176
- package/packages/sdk/tests/client.test.ts +0 -217
- package/packages/sdk/tests/errors.test.ts +0 -108
- package/packages/sdk/tests/events.test.ts +0 -139
- package/packages/sdk/tests/fixtures/fetch-mock.ts +0 -152
- package/packages/sdk/tests/fixtures/sse-mock.ts +0 -30
- package/plugins/bizar/ARCHITECTURE.md +0 -142
- package/plugins/bizar/CONSTRAINTS.md +0 -67
- package/plugins/bizar/LICENSE +0 -21
- package/plugins/bizar/README.md +0 -448
- package/plugins/bizar/index.ts +0 -925
- package/plugins/bizar/package.json +0 -39
- package/plugins/bizar/scripts/check-forbidden-imports.sh +0 -33
- package/plugins/bizar/src/background-state.ts +0 -641
- package/plugins/bizar/src/background.ts +0 -1806
- package/plugins/bizar/src/cline-runner.ts +0 -203
- package/plugins/bizar/src/clineruntime.ts +0 -227
- package/plugins/bizar/src/commands-impl.ts +0 -151
- package/plugins/bizar/src/commands.ts +0 -1799
- package/plugins/bizar/src/compaction.d.mts +0 -48
- package/plugins/bizar/src/compaction.mjs +0 -192
- package/plugins/bizar/src/dashboard-client.ts +0 -233
- package/plugins/bizar/src/event-stream.ts +0 -606
- package/plugins/bizar/src/fingerprint.ts +0 -120
- package/plugins/bizar/src/handoff.ts +0 -79
- package/plugins/bizar/src/hooks/memory-flush-on-compact.ts +0 -123
- package/plugins/bizar/src/hooks/memory-inject.ts +0 -247
- package/plugins/bizar/src/hooks/memory-write-on-end.ts +0 -188
- package/plugins/bizar/src/hooks/skill-curator.ts +0 -180
- package/plugins/bizar/src/http-client.ts +0 -467
- package/plugins/bizar/src/key-rotation.ts +0 -218
- package/plugins/bizar/src/logger.ts +0 -144
- package/plugins/bizar/src/loop-engineering.ts +0 -241
- package/plugins/bizar/src/loop.ts +0 -176
- package/plugins/bizar/src/mistake-recovery.ts +0 -98
- package/plugins/bizar/src/odin.ts +0 -227
- package/plugins/bizar/src/options.ts +0 -470
- package/plugins/bizar/src/plan-fs.ts +0 -323
- package/plugins/bizar/src/reasoning-clean.ts +0 -454
- package/plugins/bizar/src/report.ts +0 -178
- package/plugins/bizar/src/research-prompt.ts +0 -35
- package/plugins/bizar/src/serve-info.ts +0 -228
- package/plugins/bizar/src/serve.ts +0 -496
- package/plugins/bizar/src/settings.ts +0 -349
- package/plugins/bizar/src/state.ts +0 -298
- package/plugins/bizar/src/tool-discipline.ts +0 -105
- package/plugins/bizar/src/tools/agent-browser.ts +0 -315
- package/plugins/bizar/src/tools/bg-collect.ts +0 -131
- package/plugins/bizar/src/tools/bg-get-comments.ts +0 -266
- package/plugins/bizar/src/tools/bg-kill.ts +0 -116
- package/plugins/bizar/src/tools/bg-pause.ts +0 -99
- package/plugins/bizar/src/tools/bg-report-progress.ts +0 -115
- package/plugins/bizar/src/tools/bg-resume.ts +0 -94
- package/plugins/bizar/src/tools/bg-send-message.ts +0 -223
- package/plugins/bizar/src/tools/bg-spawn.ts +0 -502
- package/plugins/bizar/src/tools/bg-status.ts +0 -130
- package/plugins/bizar/src/tools/graph-query.ts +0 -278
- package/plugins/bizar/src/tools/loop-engineering.ts +0 -193
- package/plugins/bizar/src/tools/memory-list.ts +0 -43
- package/plugins/bizar/src/tools/memory-read.ts +0 -69
- package/plugins/bizar/src/tools/memory-search.ts +0 -47
- package/plugins/bizar/src/tools/memory-write.ts +0 -54
- package/plugins/bizar/src/tools/open-kb.ts +0 -198
- package/plugins/bizar/src/tools/plan-action.ts +0 -785
- package/plugins/bizar/src/tools/read-glyph-feedback.ts +0 -191
- package/plugins/bizar/src/tools/sandbox.ts +0 -232
- package/plugins/bizar/src/tools/team-spawn.ts +0 -73
- package/plugins/bizar/src/tools/team-status.ts +0 -76
- package/plugins/bizar/src/tools/wait-for-feedback.ts +0 -415
- package/plugins/bizar/src/trajectory.ts +0 -104
- package/plugins/bizar/tests/README.md +0 -99
- package/plugins/bizar/tests/attach-handler-bug.test.ts +0 -169
- package/plugins/bizar/tests/background-state.test.ts +0 -277
- package/plugins/bizar/tests/background.test.ts +0 -402
- package/plugins/bizar/tests/block.test.ts +0 -195
- package/plugins/bizar/tests/canonical-key-order.test.ts +0 -75
- package/plugins/bizar/tests/clineruntime-config.test.ts +0 -283
- package/plugins/bizar/tests/commands-impl.test.ts +0 -316
- package/plugins/bizar/tests/commands.test.ts +0 -584
- package/plugins/bizar/tests/compaction.test.ts +0 -264
- package/plugins/bizar/tests/config.test.ts +0 -128
- package/plugins/bizar/tests/dashboard-client.test.ts +0 -159
- package/plugins/bizar/tests/dispose.test.ts +0 -336
- package/plugins/bizar/tests/event-stream.test.ts +0 -409
- package/plugins/bizar/tests/event.test.ts +0 -262
- package/plugins/bizar/tests/fingerprint.test.ts +0 -190
- package/plugins/bizar/tests/http-client.test.ts +0 -404
- package/plugins/bizar/tests/init-helpers.test.ts +0 -203
- package/plugins/bizar/tests/integration/slash-command.test.ts +0 -349
- package/plugins/bizar/tests/integration/tool-routing.test.ts +0 -98
- package/plugins/bizar/tests/key-rotation.test.ts +0 -396
- package/plugins/bizar/tests/loop-engineering.test.ts +0 -168
- package/plugins/bizar/tests/loop.test.ts +0 -397
- package/plugins/bizar/tests/memory-write-on-end.test.ts +0 -92
- package/plugins/bizar/tests/mistake-recovery.test.ts +0 -116
- package/plugins/bizar/tests/odin.test.ts +0 -125
- package/plugins/bizar/tests/options.test.ts +0 -329
- package/plugins/bizar/tests/reasoning-clean.test.ts +0 -422
- package/plugins/bizar/tests/safety.test.ts +0 -281
- package/plugins/bizar/tests/serve.test.ts +0 -339
- package/plugins/bizar/tests/settings.test.ts +0 -351
- package/plugins/bizar/tests/stall-think.test.ts +0 -750
- package/plugins/bizar/tests/state.test.ts +0 -276
- package/plugins/bizar/tests/tool-discipline.test.ts +0 -77
- package/plugins/bizar/tests/tools/agent-browser.test.ts +0 -98
- package/plugins/bizar/tests/tools/bg-collect.test.ts +0 -337
- package/plugins/bizar/tests/tools/bg-get-comments.test.ts +0 -485
- package/plugins/bizar/tests/tools/bg-kill.test.ts +0 -235
- package/plugins/bizar/tests/tools/bg-pause.test.ts +0 -61
- package/plugins/bizar/tests/tools/bg-report-progress.test.ts +0 -79
- package/plugins/bizar/tests/tools/bg-resume.test.ts +0 -40
- package/plugins/bizar/tests/tools/bg-send-message.test.ts +0 -116
- package/plugins/bizar/tests/tools/bg-spawn-delegation.test.ts +0 -147
- package/plugins/bizar/tests/tools/bg-spawn-http.test.ts +0 -233
- package/plugins/bizar/tests/tools/bg-spawn.test.ts +0 -311
- package/plugins/bizar/tests/tools/bg-status.test.ts +0 -217
- package/plugins/bizar/tests/tools/cline-runner.test.ts +0 -115
- package/plugins/bizar/tests/tools/plan-action.test.ts +0 -599
- package/plugins/bizar/tests/tools/read-glyph-feedback.test.ts +0 -253
- package/plugins/bizar/tests/tools/sandbox.test.ts +0 -117
- package/plugins/bizar/tests/tools/wait-for-feedback.test.ts +0 -390
- package/plugins/bizar/tests/update-deadlock.test.ts +0 -151
- package/plugins/bizar/tsconfig.json +0 -29
- 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
|
+
});
|