@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,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* federation/audit.ts — AuditService: NDJSON log of every
|
|
3
|
+
* federation decision.
|
|
4
|
+
*
|
|
5
|
+
* F-038 — ported from ruflo
|
|
6
|
+
* `v3/@claude-flow/plugin-agent-federation/src/domain/services/audit-service.ts`
|
|
7
|
+
* stripped to the parts the skeleton needs:
|
|
8
|
+
*
|
|
9
|
+
* - record(envelope, decision) appends ONE NDJSON line to
|
|
10
|
+
* `.harness/federation-audit.log`.
|
|
11
|
+
* - When the file exceeds 10 MB, it rotates to
|
|
12
|
+
* `.harness/federation-audit.log.1` (single generation).
|
|
13
|
+
* - tail() reads the last N lines for the `federation_status` MCP tool.
|
|
14
|
+
* - size() returns the on-disk file size for the status payload.
|
|
15
|
+
*
|
|
16
|
+
* Writes are synchronous (fs.appendFileSync) — the skeleton is
|
|
17
|
+
* in-process and not throughput-bound; an async path would only
|
|
18
|
+
* complicate testing.
|
|
19
|
+
*
|
|
20
|
+
* The `.harness/` directory is created on first write. The audit
|
|
21
|
+
* log file is added to `.gitignore` by the Makefile/install step.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { appendFileSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, renameSync, statSync } from "node:fs";
|
|
25
|
+
import { dirname } from "node:path";
|
|
26
|
+
|
|
27
|
+
/** Default audit log path — colocated with the rest of `.harness/`. */
|
|
28
|
+
export const DEFAULT_AUDIT_PATH = ".harness/federation-audit.log";
|
|
29
|
+
|
|
30
|
+
/** Rotate when the file exceeds this size. */
|
|
31
|
+
const ROTATION_THRESHOLD_BYTES = 10 * 1024 * 1024; // 10 MB
|
|
32
|
+
|
|
33
|
+
/** Tail size cap — protects against reading gigabytes into memory. */
|
|
34
|
+
const TAIL_MAX_BYTES = 1 * 1024 * 1024; // 1 MB
|
|
35
|
+
|
|
36
|
+
/** Decisions recorded by the orchestrator — matches the schema the
|
|
37
|
+
* `federation_status` MCP tool reads back. */
|
|
38
|
+
export type AuditDecision =
|
|
39
|
+
| { allowed: true; reason: string }
|
|
40
|
+
| { allowed: false; reason: string };
|
|
41
|
+
|
|
42
|
+
export interface AuditEntry {
|
|
43
|
+
readonly ts: string;
|
|
44
|
+
readonly envelopeId: string;
|
|
45
|
+
readonly sourceNodeId: string;
|
|
46
|
+
readonly targetNodeId: string;
|
|
47
|
+
readonly messageType: string;
|
|
48
|
+
readonly nonce: string;
|
|
49
|
+
readonly allowed: boolean;
|
|
50
|
+
readonly reason: string;
|
|
51
|
+
readonly layer: "trust" | "policy" | "hmac" | "budget" | "received" | "sent";
|
|
52
|
+
readonly nodeId: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface AuditServiceOpts {
|
|
56
|
+
/** Override the audit file path (defaults to .harness/federation-audit.log). */
|
|
57
|
+
readonly path?: string;
|
|
58
|
+
/** Local nodeId stamped into every entry. */
|
|
59
|
+
readonly nodeId: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export class AuditService {
|
|
63
|
+
private readonly path: string;
|
|
64
|
+
private readonly nodeId: string;
|
|
65
|
+
private rotated = false;
|
|
66
|
+
|
|
67
|
+
constructor(opts: AuditServiceOpts) {
|
|
68
|
+
if (!opts || typeof opts.nodeId !== "string" || opts.nodeId.length === 0) {
|
|
69
|
+
throw new Error("AuditService: nodeId is required");
|
|
70
|
+
}
|
|
71
|
+
this.path = opts.path ?? DEFAULT_AUDIT_PATH;
|
|
72
|
+
this.nodeId = opts.nodeId;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
getPath(): string {
|
|
76
|
+
return this.path;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Append an NDJSON line. Triggers rotation if the file would
|
|
80
|
+
* exceed the threshold after the write. */
|
|
81
|
+
record(entry: Omit<AuditEntry, "nodeId">): void {
|
|
82
|
+
const full: AuditEntry = { ...entry, nodeId: this.nodeId };
|
|
83
|
+
const dir = dirname(this.path);
|
|
84
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
85
|
+
|
|
86
|
+
// Pre-write rotation check: if appending would exceed the
|
|
87
|
+
// threshold AND we haven't already rotated this session, rotate
|
|
88
|
+
// first. We keep a single generation (`.log.1`); older logs
|
|
89
|
+
// are the operator's responsibility (or a separate cron).
|
|
90
|
+
if (!this.rotated && existsSync(this.path)) {
|
|
91
|
+
try {
|
|
92
|
+
const size = statSync(this.path).size;
|
|
93
|
+
if (size + 256 > ROTATION_THRESHOLD_BYTES) {
|
|
94
|
+
this.rotate();
|
|
95
|
+
}
|
|
96
|
+
} catch {
|
|
97
|
+
// stat failed (file gone between check and write) — fall through.
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
appendFileSync(this.path, JSON.stringify(full) + "\n", "utf-8");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Rotate the current log to `.log.1`. Subsequent writes go to a
|
|
105
|
+
* fresh `.log` file. Idempotent within a single service lifetime. */
|
|
106
|
+
rotate(): void {
|
|
107
|
+
const target = `${this.path}.1`;
|
|
108
|
+
if (existsSync(this.path)) {
|
|
109
|
+
try { renameSync(this.path, target); }
|
|
110
|
+
catch { /* ignore — best-effort */ }
|
|
111
|
+
}
|
|
112
|
+
this.rotated = true;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** On-disk size in bytes. 0 if file missing. */
|
|
116
|
+
size(): number {
|
|
117
|
+
try {
|
|
118
|
+
return existsSync(this.path) ? statSync(this.path).size : 0;
|
|
119
|
+
} catch {
|
|
120
|
+
return 0;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Read the last `n` entries from the log. Bounded by TAIL_MAX_BYTES. */
|
|
125
|
+
tail(n: number = 50): AuditEntry[] {
|
|
126
|
+
if (!existsSync(this.path)) return [];
|
|
127
|
+
let raw: string;
|
|
128
|
+
try {
|
|
129
|
+
const stats = statSync(this.path);
|
|
130
|
+
const start = Math.max(0, stats.size - TAIL_MAX_BYTES);
|
|
131
|
+
const buffer = Buffer.alloc(stats.size - start);
|
|
132
|
+
// Read the last TAIL_MAX_BYTES via a small open/read/close dance.
|
|
133
|
+
const handle = openSync(this.path, "r");
|
|
134
|
+
try { readSync(handle, buffer, 0, buffer.length, start); }
|
|
135
|
+
finally { closeSync(handle); }
|
|
136
|
+
raw = buffer.toString("utf-8");
|
|
137
|
+
} catch {
|
|
138
|
+
raw = readFileSync(this.path, "utf-8");
|
|
139
|
+
}
|
|
140
|
+
const lines = raw.split("\n").filter((l) => l.length > 0);
|
|
141
|
+
const slice = lines.slice(-n);
|
|
142
|
+
const out: AuditEntry[] = [];
|
|
143
|
+
for (const line of slice) {
|
|
144
|
+
try { out.push(JSON.parse(line) as AuditEntry); }
|
|
145
|
+
catch { /* malformed line — skip */ }
|
|
146
|
+
}
|
|
147
|
+
return out;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Wipe the audit log. Test-only helper. */
|
|
151
|
+
reset(): void {
|
|
152
|
+
this.rotated = false;
|
|
153
|
+
try { if (existsSync(this.path)) renameSync(this.path, `${this.path}.deleted`); }
|
|
154
|
+
catch { /* ignore */ }
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* federation/budget.ts — FederationBudget: per-peer reserved →
|
|
3
|
+
* committed → released state machine for token / USD spend.
|
|
4
|
+
*
|
|
5
|
+
* F-038 — ported from ruflo
|
|
6
|
+
* `v3/@claude-flow/plugin-agent-federation/src/domain/value-objects/federation-budget.ts`
|
|
7
|
+
* (validateBudget + enforceBudget shape) and folded into a class
|
|
8
|
+
* that tracks actual spend per peer across calls.
|
|
9
|
+
*
|
|
10
|
+
* State machine (per peer):
|
|
11
|
+
* reserved: amount is held back (the peer is going to spend ≤ N).
|
|
12
|
+
* committed: amount was actually spent (the call completed and we
|
|
13
|
+
* know the real cost).
|
|
14
|
+
* released: amount returned to the peer (call failed before
|
|
15
|
+
* commit; we give the hold back).
|
|
16
|
+
*
|
|
17
|
+
* Persistence: optional JSON snapshot at
|
|
18
|
+
* `.harness/federation-budget.json` so the budget survives a
|
|
19
|
+
* Claude Code session restart. The file is gitignored.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync } from "node:fs";
|
|
23
|
+
import { dirname } from "node:path";
|
|
24
|
+
|
|
25
|
+
/** Default budget JSON path. */
|
|
26
|
+
export const DEFAULT_BUDGET_PATH = ".harness/federation-budget.json";
|
|
27
|
+
|
|
28
|
+
/** Hard ceilings — matches ruflo's MAX_*_CEILING values, kept here
|
|
29
|
+
* so callers can't ask for absurdly large budgets. */
|
|
30
|
+
export const MAX_USD_CEILING = 1_000_000;
|
|
31
|
+
export const MAX_TOKENS_CEILING = 1_000_000_000;
|
|
32
|
+
|
|
33
|
+
export type ReserveResult =
|
|
34
|
+
| { ok: true; reservationId: string }
|
|
35
|
+
| { ok: false; reason: "peer_over_budget" | "invalid_amount" };
|
|
36
|
+
|
|
37
|
+
export interface PerPeerBudget {
|
|
38
|
+
readonly nodeId: string;
|
|
39
|
+
readonly maxTokens: number;
|
|
40
|
+
readonly maxUsd: number;
|
|
41
|
+
readonly maxHops: number;
|
|
42
|
+
readonly spentTokens: number;
|
|
43
|
+
readonly spentUsd: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface PersistedBudget {
|
|
47
|
+
readonly version: 1;
|
|
48
|
+
readonly updatedAt: string;
|
|
49
|
+
readonly perPeer: PerPeerBudget[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface Reservation {
|
|
53
|
+
readonly id: string;
|
|
54
|
+
readonly nodeId: string;
|
|
55
|
+
readonly tokens: number;
|
|
56
|
+
readonly usd: number;
|
|
57
|
+
readonly atMs: number;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let reservationCounter = 0;
|
|
61
|
+
|
|
62
|
+
function nextReservationId(): string {
|
|
63
|
+
reservationCounter++;
|
|
64
|
+
// 128-bit random suffix avoids collision across processes.
|
|
65
|
+
return `res-${Date.now().toString(36)}-${reservationCounter.toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Pure validator — borrowed from ruflo's `validateBudget` and
|
|
69
|
+
* simplified. Returns a discriminated result. */
|
|
70
|
+
export function validateBudgetInput(raw: unknown): { ok: true; maxTokens: number; maxUsd: number; maxHops: number } | { ok: false; error: string } {
|
|
71
|
+
if (raw == null) {
|
|
72
|
+
return {
|
|
73
|
+
ok: true,
|
|
74
|
+
maxTokens: MAX_TOKENS_CEILING,
|
|
75
|
+
maxUsd: MAX_USD_CEILING,
|
|
76
|
+
maxHops: 8,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
if (typeof raw !== "object" || Array.isArray(raw)) {
|
|
80
|
+
return { ok: false, error: "budget must be an object" };
|
|
81
|
+
}
|
|
82
|
+
const obj = raw as Record<string, unknown>;
|
|
83
|
+
const hops = typeof obj.maxHops === "number" && Number.isFinite(obj.maxHops) ? obj.maxHops : 8;
|
|
84
|
+
const tokens = typeof obj.maxTokens === "number" && Number.isFinite(obj.maxTokens) ? obj.maxTokens : MAX_TOKENS_CEILING;
|
|
85
|
+
const usd = typeof obj.maxUsd === "number" && Number.isFinite(obj.maxUsd) ? obj.maxUsd : MAX_USD_CEILING;
|
|
86
|
+
if (hops < 0 || hops > 64) return { ok: false, error: "maxHops out of range" };
|
|
87
|
+
if (tokens < 0 || tokens > MAX_TOKENS_CEILING) return { ok: false, error: "maxTokens out of range" };
|
|
88
|
+
if (usd < 0 || usd > MAX_USD_CEILING) return { ok: false, error: "maxUsd out of range" };
|
|
89
|
+
return { ok: true, maxTokens: tokens, maxUsd: usd, maxHops: hops };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface FederationBudgetOpts {
|
|
93
|
+
/** Override the JSON persistence path. */
|
|
94
|
+
readonly path?: string;
|
|
95
|
+
/** Default per-peer limits applied when a peer is first seen. */
|
|
96
|
+
readonly defaultMaxTokens?: number;
|
|
97
|
+
readonly defaultMaxUsd?: number;
|
|
98
|
+
readonly defaultMaxHops?: number;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export class FederationBudget {
|
|
102
|
+
private readonly path: string;
|
|
103
|
+
private readonly perPeer = new Map<string, PerPeerBudget>();
|
|
104
|
+
private readonly reservations = new Map<string, Reservation>();
|
|
105
|
+
private readonly defaults: { maxTokens: number; maxUsd: number; maxHops: number };
|
|
106
|
+
|
|
107
|
+
constructor(opts: FederationBudgetOpts = {}) {
|
|
108
|
+
this.path = opts.path ?? DEFAULT_BUDGET_PATH;
|
|
109
|
+
this.defaults = {
|
|
110
|
+
maxTokens: opts.defaultMaxTokens ?? MAX_TOKENS_CEILING,
|
|
111
|
+
maxUsd: opts.defaultMaxUsd ?? MAX_USD_CEILING,
|
|
112
|
+
maxHops: opts.defaultMaxHops ?? 8,
|
|
113
|
+
};
|
|
114
|
+
this.loadFromDisk();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Get or lazily-create the per-peer budget record. */
|
|
118
|
+
getOrCreate(nodeId: string): PerPeerBudget {
|
|
119
|
+
let existing = this.perPeer.get(nodeId);
|
|
120
|
+
if (!existing) {
|
|
121
|
+
existing = {
|
|
122
|
+
nodeId,
|
|
123
|
+
maxTokens: this.defaults.maxTokens,
|
|
124
|
+
maxUsd: this.defaults.maxUsd,
|
|
125
|
+
maxHops: this.defaults.maxHops,
|
|
126
|
+
spentTokens: 0,
|
|
127
|
+
spentUsd: 0,
|
|
128
|
+
};
|
|
129
|
+
this.perPeer.set(nodeId, existing);
|
|
130
|
+
}
|
|
131
|
+
return existing;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Override the limits for a specific peer. */
|
|
135
|
+
setLimits(nodeId: string, limits: { maxTokens?: number; maxUsd?: number; maxHops?: number }): void {
|
|
136
|
+
const peer = this.getOrCreate(nodeId);
|
|
137
|
+
this.perPeer.set(nodeId, {
|
|
138
|
+
...peer,
|
|
139
|
+
maxTokens: limits.maxTokens ?? peer.maxTokens,
|
|
140
|
+
maxUsd: limits.maxUsd ?? peer.maxUsd,
|
|
141
|
+
maxHops: limits.maxHops ?? peer.maxHops,
|
|
142
|
+
});
|
|
143
|
+
this.saveToDisk();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Reserve `tokens` + `usd` against a peer's budget. The caller
|
|
147
|
+
* MUST eventually `commit()` or `release()` — un-released
|
|
148
|
+
* reservations are surfaced via `outstanding()`. */
|
|
149
|
+
reserve(nodeId: string, tokens: number, usd: number): ReserveResult {
|
|
150
|
+
if (!Number.isFinite(tokens) || tokens < 0 || !Number.isFinite(usd) || usd < 0) {
|
|
151
|
+
return { ok: false, reason: "invalid_amount" };
|
|
152
|
+
}
|
|
153
|
+
const peer = this.getOrCreate(nodeId);
|
|
154
|
+
const remainingTokens = peer.maxTokens - peer.spentTokens;
|
|
155
|
+
const remainingUsd = peer.maxUsd - peer.spentUsd;
|
|
156
|
+
if (tokens > remainingTokens || usd > remainingUsd) {
|
|
157
|
+
return { ok: false, reason: "peer_over_budget" };
|
|
158
|
+
}
|
|
159
|
+
const id = nextReservationId();
|
|
160
|
+
this.reservations.set(id, {
|
|
161
|
+
id,
|
|
162
|
+
nodeId,
|
|
163
|
+
tokens,
|
|
164
|
+
usd,
|
|
165
|
+
atMs: Date.now(),
|
|
166
|
+
});
|
|
167
|
+
// Optimistically charge the spend against the budget so a
|
|
168
|
+
// concurrent reserve from the same peer cannot double-spend.
|
|
169
|
+
// If commit() reports less, the diff is refunded. If the call
|
|
170
|
+
// fails and release() is called, the full reservation is
|
|
171
|
+
// refunded.
|
|
172
|
+
this.perPeer.set(nodeId, {
|
|
173
|
+
...peer,
|
|
174
|
+
spentTokens: peer.spentTokens + tokens,
|
|
175
|
+
spentUsd: peer.spentUsd + usd,
|
|
176
|
+
});
|
|
177
|
+
return { ok: true, reservationId: id };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** Commit a reservation — caller reports the ACTUAL spend which
|
|
181
|
+
* may be less than the reserved amount. Difference is refunded. */
|
|
182
|
+
commit(reservationId: string, actual: { tokens?: number; usd?: number } = {}): boolean {
|
|
183
|
+
const r = this.reservations.get(reservationId);
|
|
184
|
+
if (!r) return false;
|
|
185
|
+
const actualTokens = Math.max(0, actual.tokens ?? r.tokens);
|
|
186
|
+
const actualUsd = Math.max(0, actual.usd ?? r.usd);
|
|
187
|
+
const peer = this.perPeer.get(r.nodeId);
|
|
188
|
+
if (!peer) {
|
|
189
|
+
this.reservations.delete(reservationId);
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
// Refund the difference between reserved and actual.
|
|
193
|
+
const tokenRefund = r.tokens - actualTokens;
|
|
194
|
+
const usdRefund = r.usd - actualUsd;
|
|
195
|
+
this.perPeer.set(r.nodeId, {
|
|
196
|
+
...peer,
|
|
197
|
+
spentTokens: Math.max(0, peer.spentTokens - tokenRefund),
|
|
198
|
+
spentUsd: Math.max(0, peer.spentUsd - usdRefund),
|
|
199
|
+
});
|
|
200
|
+
this.reservations.delete(reservationId);
|
|
201
|
+
this.saveToDisk();
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** Release a reservation — full refund. */
|
|
206
|
+
release(reservationId: string): boolean {
|
|
207
|
+
const r = this.reservations.get(reservationId);
|
|
208
|
+
if (!r) return false;
|
|
209
|
+
const peer = this.perPeer.get(r.nodeId);
|
|
210
|
+
if (peer) {
|
|
211
|
+
this.perPeer.set(r.nodeId, {
|
|
212
|
+
...peer,
|
|
213
|
+
spentTokens: Math.max(0, peer.spentTokens - r.tokens),
|
|
214
|
+
spentUsd: Math.max(0, peer.spentUsd - r.usd),
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
this.reservations.delete(reservationId);
|
|
218
|
+
this.saveToDisk();
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** List outstanding reservations. */
|
|
223
|
+
outstanding(): Reservation[] {
|
|
224
|
+
return [...this.reservations.values()];
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** Snapshot every per-peer budget — for the status MCP tool. */
|
|
228
|
+
snapshot(): PerPeerBudget[] {
|
|
229
|
+
return [...this.perPeer.values()];
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** Serialize to the persisted JSON shape. */
|
|
233
|
+
toJSON(): PersistedBudget {
|
|
234
|
+
return {
|
|
235
|
+
version: 1,
|
|
236
|
+
updatedAt: new Date().toISOString(),
|
|
237
|
+
perPeer: this.snapshot(),
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** Write to disk (atomic tmp + rename). */
|
|
242
|
+
saveToDisk(): void {
|
|
243
|
+
const dir = dirname(this.path);
|
|
244
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
245
|
+
const tmp = `${this.path}.tmp`;
|
|
246
|
+
try {
|
|
247
|
+
writeFileSync(tmp, JSON.stringify(this.toJSON(), null, 2), "utf-8");
|
|
248
|
+
renameSync(tmp, this.path);
|
|
249
|
+
} catch {
|
|
250
|
+
/* best-effort */
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** Re-read from disk, replacing in-memory state. */
|
|
255
|
+
loadFromDisk(): void {
|
|
256
|
+
if (!existsSync(this.path)) return;
|
|
257
|
+
try {
|
|
258
|
+
const raw = readFileSync(this.path, "utf-8");
|
|
259
|
+
const parsed = JSON.parse(raw) as PersistedBudget;
|
|
260
|
+
if (parsed.version !== 1 || !Array.isArray(parsed.perPeer)) return;
|
|
261
|
+
this.perPeer.clear();
|
|
262
|
+
for (const p of parsed.perPeer) {
|
|
263
|
+
if (typeof p.nodeId === "string") this.perPeer.set(p.nodeId, p);
|
|
264
|
+
}
|
|
265
|
+
} catch {
|
|
266
|
+
/* malformed — keep defaults */
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* federation/envelope.ts — Federation envelope type and message kinds.
|
|
3
|
+
*
|
|
4
|
+
* F-038 — ported from ruflo
|
|
5
|
+
* `v3/@claude-flow/plugin-agent-federation/src/domain/entities/federation-envelope.ts:1-20`
|
|
6
|
+
* (the 16-kind `FederationMessageType` union + `FederationEnvelope` shape).
|
|
7
|
+
*
|
|
8
|
+
* This module is intentionally minimal: it exports ONLY types + a
|
|
9
|
+
* tiny `emptyScanResult()` helper. The runtime signing/verification
|
|
10
|
+
* lives in `./hmac.ts`, PII in `./pii.ts`, audit in `./audit.ts`,
|
|
11
|
+
* budget in `./budget.ts`. Keeping the type isolated here avoids a
|
|
12
|
+
* cycle with the signing/policy modules that import from this file.
|
|
13
|
+
*
|
|
14
|
+
* Backward-compat note: Bizar's v6.4.0 release (current `master`)
|
|
15
|
+
* shipped zero federation code, so anything we add is purely
|
|
16
|
+
* additive — no consumers can break. The 16 message kinds are
|
|
17
|
+
* defined as a `readonly` array AND a union type so the validator
|
|
18
|
+
* surface (e.g. PolicyEngine) can iterate without re-listing them.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Wire-level message kinds carried inside a federation envelope.
|
|
23
|
+
* 16 total — kept identical to the ruflo source so cross-installation
|
|
24
|
+
* pairs can interoperate if both run a Bizar build ≥ v6.5.0.
|
|
25
|
+
*/
|
|
26
|
+
export const FEDERATION_MESSAGE_TYPES = [
|
|
27
|
+
"task-assignment",
|
|
28
|
+
"memory-query",
|
|
29
|
+
"memory-response",
|
|
30
|
+
"context-share",
|
|
31
|
+
"status-broadcast",
|
|
32
|
+
"trust-change",
|
|
33
|
+
"topology-change",
|
|
34
|
+
"agent-spawn",
|
|
35
|
+
"heartbeat",
|
|
36
|
+
"challenge",
|
|
37
|
+
"challenge-response",
|
|
38
|
+
"handshake-init",
|
|
39
|
+
"handshake-accept",
|
|
40
|
+
"handshake-reject",
|
|
41
|
+
"session-terminate",
|
|
42
|
+
"claim-event",
|
|
43
|
+
"agent-handoff",
|
|
44
|
+
] as const;
|
|
45
|
+
|
|
46
|
+
export type FederationMessageType = (typeof FEDERATION_MESSAGE_TYPES)[number];
|
|
47
|
+
|
|
48
|
+
/** Is `t` a recognised federation message kind? Useful for validation. */
|
|
49
|
+
export function isFederationMessageType(t: unknown): t is FederationMessageType {
|
|
50
|
+
return typeof t === "string" && (FEDERATION_MESSAGE_TYPES as readonly string[]).includes(t);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Subset of message kinds that mutate trust / topology state — those
|
|
54
|
+
* that should require extra policy scrutiny (mirrors ruflo's
|
|
55
|
+
* CONSENSUS_REQUIRED_TYPES set, minus the consensus-vote-specific
|
|
56
|
+
* types that don't apply to the skeleton). */
|
|
57
|
+
export const TRUST_AFFECTING_MESSAGE_TYPES: ReadonlySet<FederationMessageType> = new Set<FederationMessageType>([
|
|
58
|
+
"trust-change",
|
|
59
|
+
"topology-change",
|
|
60
|
+
"agent-spawn",
|
|
61
|
+
"agent-handoff",
|
|
62
|
+
]);
|
|
63
|
+
|
|
64
|
+
/** Outcome of a PII scan over the envelope payload. Mirrors ruflo's
|
|
65
|
+
* PIIScanResult so audit consumers can read either implementation
|
|
66
|
+
* interchangeably. */
|
|
67
|
+
export type PiiScanAction = "pass" | "redact" | "hash" | "block";
|
|
68
|
+
|
|
69
|
+
export interface PiiScanDetection {
|
|
70
|
+
readonly type: string;
|
|
71
|
+
readonly action: PiiScanAction;
|
|
72
|
+
readonly confidence: number;
|
|
73
|
+
readonly count: number;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface PiiScanResult {
|
|
77
|
+
readonly scanned: boolean;
|
|
78
|
+
readonly piiFound: boolean;
|
|
79
|
+
readonly detections: readonly PiiScanDetection[];
|
|
80
|
+
readonly actionsApplied: readonly PiiScanAction[];
|
|
81
|
+
readonly scanDurationMs: number;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Empty / no-op scan result — used by senders that opt out of PII
|
|
85
|
+
* scanning for the given message kind. */
|
|
86
|
+
export function emptyScanResult(): PiiScanResult {
|
|
87
|
+
return {
|
|
88
|
+
scanned: false,
|
|
89
|
+
piiFound: false,
|
|
90
|
+
detections: [],
|
|
91
|
+
actionsApplied: [],
|
|
92
|
+
scanDurationMs: 0,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Per-peer budget attached to every envelope. `maxTokens` and
|
|
97
|
+
* `maxUsd` are the sender's original limits for the call; the
|
|
98
|
+
* remote decrements them as the message fans out. `maxHops` is
|
|
99
|
+
* the absolute hop ceiling (sender's value). */
|
|
100
|
+
export interface EnvelopeBudget {
|
|
101
|
+
readonly maxTokens: number;
|
|
102
|
+
readonly maxUsd: number;
|
|
103
|
+
readonly maxHops: number;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface FederationEnvelope<T = unknown> {
|
|
107
|
+
readonly envelopeId: string;
|
|
108
|
+
readonly sourceNodeId: string;
|
|
109
|
+
readonly targetNodeId: string;
|
|
110
|
+
readonly sessionId: string;
|
|
111
|
+
readonly messageType: FederationMessageType;
|
|
112
|
+
readonly payload: T;
|
|
113
|
+
readonly timestamp: string; // ISO-8601, parsed back via new Date()
|
|
114
|
+
readonly nonce: string; // 128-bit random via crypto.randomUUID()
|
|
115
|
+
readonly hmacSignature: string; // hex SHA-256 HMAC over canonical payload
|
|
116
|
+
readonly piiScanResult: PiiScanResult;
|
|
117
|
+
readonly budget?: EnvelopeBudget;
|
|
118
|
+
readonly hopCount?: number; // present on re-emit, defaults to 0 on origin
|
|
119
|
+
readonly hopsRemaining?: number;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Canonical JSON form signed by HMAC. Field order MUST stay stable
|
|
123
|
+
* across sender + receiver or signatures will mismatch. */
|
|
124
|
+
export function canonicalSignablePayload<T>(e: FederationEnvelope<T>): string {
|
|
125
|
+
const obj = {
|
|
126
|
+
envelopeId: e.envelopeId,
|
|
127
|
+
sourceNodeId: e.sourceNodeId,
|
|
128
|
+
targetNodeId: e.targetNodeId,
|
|
129
|
+
sessionId: e.sessionId,
|
|
130
|
+
messageType: e.messageType,
|
|
131
|
+
payload: e.payload,
|
|
132
|
+
timestamp: e.timestamp,
|
|
133
|
+
nonce: e.nonce,
|
|
134
|
+
hopCount: e.hopCount ?? 0,
|
|
135
|
+
};
|
|
136
|
+
return JSON.stringify(obj);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** `Date.now()`-based envelope age in milliseconds. */
|
|
140
|
+
export function envelopeAgeMs(e: FederationEnvelope, now: number = Date.now()): number {
|
|
141
|
+
const ts = new Date(e.timestamp).getTime();
|
|
142
|
+
if (!Number.isFinite(ts)) return Number.POSITIVE_INFINITY;
|
|
143
|
+
return Math.max(0, now - ts);
|
|
144
|
+
}
|