@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,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* federation/hmac.ts — HMAC-SHA-256 signing + verification for
|
|
3
|
+
* federation envelopes.
|
|
4
|
+
*
|
|
5
|
+
* F-038 — security-critical. Pinned by AGENTS.md hard constraint:
|
|
6
|
+
* "MUST use crypto.timingSafeEqual for HMAC comparison
|
|
7
|
+
* (timing-attack resistant)."
|
|
8
|
+
* "MUST use crypto.randomUUID() for nonces (128-bit random)."
|
|
9
|
+
*
|
|
10
|
+
* Nonce strategy:
|
|
11
|
+
* - Sender generates a fresh `crypto.randomUUID()` per envelope.
|
|
12
|
+
* - Receiver tracks every nonce it has seen in a TTL-bounded Set.
|
|
13
|
+
* - An envelope whose nonce appears twice is a replay → reject.
|
|
14
|
+
* - An envelope whose timestamp is older than `nonceWindowMs`
|
|
15
|
+
* (default 5 min) is also a replay → reject.
|
|
16
|
+
*
|
|
17
|
+
* No native deps. Pure Node `node:crypto`.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { createHmac, randomUUID, timingSafeEqual } from "node:crypto";
|
|
21
|
+
import {
|
|
22
|
+
canonicalSignablePayload,
|
|
23
|
+
type FederationEnvelope,
|
|
24
|
+
} from "./envelope.js";
|
|
25
|
+
|
|
26
|
+
/** Default freshness window for HMAC verification (5 minutes). */
|
|
27
|
+
export const DEFAULT_NONCE_WINDOW_MS = 5 * 60 * 1000;
|
|
28
|
+
|
|
29
|
+
/** Cap on the receiver's seen-nonce set so it can't grow unbounded. */
|
|
30
|
+
const SEEN_NONCE_CAP = 10_000;
|
|
31
|
+
|
|
32
|
+
/** Compute a deterministic 64-char hex HMAC-SHA-256 over the envelope's
|
|
33
|
+
* canonical signable payload. */
|
|
34
|
+
export function signEnvelope<T>(
|
|
35
|
+
envelope: FederationEnvelope<T>,
|
|
36
|
+
secret: string,
|
|
37
|
+
): string {
|
|
38
|
+
if (typeof secret !== "string" || secret.length === 0) {
|
|
39
|
+
throw new Error("signEnvelope: secret must be a non-empty string");
|
|
40
|
+
}
|
|
41
|
+
const payload = canonicalSignablePayload(envelope);
|
|
42
|
+
return createHmac("sha256", secret).update(payload).digest("hex");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface VerifyOptions {
|
|
46
|
+
/** Freshness window. Envelopes older than this are rejected.
|
|
47
|
+
* Defaults to 5 minutes. */
|
|
48
|
+
readonly nonceWindowMs?: number;
|
|
49
|
+
/** Optional clock-skew offset (ms) to *add* to the freshness
|
|
50
|
+
* window (e.g. for poorly-synced peers). Defaults to 0. */
|
|
51
|
+
readonly clockSkewMs?: number;
|
|
52
|
+
/** Optional explicit timestamp for deterministic tests. */
|
|
53
|
+
readonly now?: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type VerifyOutcome =
|
|
57
|
+
| { ok: true }
|
|
58
|
+
| {
|
|
59
|
+
ok: false;
|
|
60
|
+
reason:
|
|
61
|
+
| "signature_mismatch"
|
|
62
|
+
| "nonce_replay"
|
|
63
|
+
| "envelope_expired"
|
|
64
|
+
| "envelope_from_future"
|
|
65
|
+
| "invalid_signature_format";
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/** Pure verifier — no shared state. The caller owns the seen-nonce
|
|
69
|
+
* cache (see `NonceCache` below) and passes it in via `seen`. */
|
|
70
|
+
export function verifySignature<T>(
|
|
71
|
+
envelope: FederationEnvelope<T>,
|
|
72
|
+
secret: string,
|
|
73
|
+
seen: ReadonlySet<string>,
|
|
74
|
+
opts: VerifyOptions = {},
|
|
75
|
+
): VerifyOutcome {
|
|
76
|
+
if (typeof secret !== "string" || secret.length === 0) {
|
|
77
|
+
return { ok: false, reason: "invalid_signature_format" };
|
|
78
|
+
}
|
|
79
|
+
if (typeof envelope.hmacSignature !== "string" || envelope.hmacSignature.length === 0) {
|
|
80
|
+
return { ok: false, reason: "invalid_signature_format" };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Freshness — reject before we touch the HMAC code path so we
|
|
84
|
+
// don't burn cycles on ancient replays.
|
|
85
|
+
const now = opts.now ?? Date.now();
|
|
86
|
+
const ts = new Date(envelope.timestamp).getTime();
|
|
87
|
+
if (!Number.isFinite(ts)) return { ok: false, reason: "envelope_expired" };
|
|
88
|
+
const ageMs = now - ts;
|
|
89
|
+
const window = (opts.nonceWindowMs ?? DEFAULT_NONCE_WINDOW_MS) + (opts.clockSkewMs ?? 0);
|
|
90
|
+
if (ageMs > window) return { ok: false, reason: "envelope_expired" };
|
|
91
|
+
if (ageMs < -window) return { ok: false, reason: "envelope_from_future" };
|
|
92
|
+
|
|
93
|
+
// Nonce replay — checked AFTER freshness so a delayed-but-unique
|
|
94
|
+
// envelope doesn't get falsely fingerprinted as a replay.
|
|
95
|
+
if (seen.has(envelope.nonce)) return { ok: false, reason: "nonce_replay" };
|
|
96
|
+
|
|
97
|
+
// Constant-time signature compare. Both buffers must be the same
|
|
98
|
+
// length for timingSafeEqual; if lengths differ, signature_mismatch.
|
|
99
|
+
const expected = signEnvelope(envelope, secret);
|
|
100
|
+
const a = Buffer.from(expected, "hex");
|
|
101
|
+
let b: Buffer;
|
|
102
|
+
try {
|
|
103
|
+
b = Buffer.from(envelope.hmacSignature, "hex");
|
|
104
|
+
} catch {
|
|
105
|
+
return { ok: false, reason: "invalid_signature_format" };
|
|
106
|
+
}
|
|
107
|
+
if (a.length !== b.length) return { ok: false, reason: "signature_mismatch" };
|
|
108
|
+
if (!timingSafeEqual(a, b)) return { ok: false, reason: "signature_mismatch" };
|
|
109
|
+
|
|
110
|
+
return { ok: true };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Mutable nonce cache with TTL eviction + hard cap. The receiver
|
|
114
|
+
* keeps one per remote node (or a single global one for the skeleton). */
|
|
115
|
+
export class NonceCache {
|
|
116
|
+
private readonly seen = new Map<string, number>(); // nonce → expiryMs
|
|
117
|
+
private readonly windowMs: number;
|
|
118
|
+
|
|
119
|
+
constructor(windowMs: number = DEFAULT_NONCE_WINDOW_MS) {
|
|
120
|
+
this.windowMs = windowMs;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** True if the nonce is fresh (not seen in windowMs). Adds it
|
|
124
|
+
* to the cache as a side effect. */
|
|
125
|
+
check(nonce: string, now: number = Date.now()): boolean {
|
|
126
|
+
this.evict(now);
|
|
127
|
+
if (this.seen.has(nonce)) return false;
|
|
128
|
+
if (this.seen.size >= SEEN_NONCE_CAP) {
|
|
129
|
+
// Hard cap — evict the oldest entry to make room.
|
|
130
|
+
const oldest = this.seen.keys().next().value;
|
|
131
|
+
if (oldest !== undefined) this.seen.delete(oldest);
|
|
132
|
+
}
|
|
133
|
+
this.seen.set(nonce, now + this.windowMs);
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** True if the nonce is in the cache (read-only). */
|
|
138
|
+
has(nonce: string): boolean {
|
|
139
|
+
return this.seen.has(nonce);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
size(): number {
|
|
143
|
+
return this.seen.size;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
clear(): void {
|
|
147
|
+
this.seen.clear();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private evict(now: number): void {
|
|
151
|
+
for (const [nonce, expiry] of this.seen) {
|
|
152
|
+
if (expiry <= now) this.seen.delete(nonce);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Helper — produce a fresh nonce via `crypto.randomUUID()`. */
|
|
158
|
+
export function freshNonce(): string {
|
|
159
|
+
return randomUUID();
|
|
160
|
+
}
|
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* federation/index.ts — top-level orchestrator + public exports.
|
|
3
|
+
*
|
|
4
|
+
* F-038 — composes the eight federation primitives into a single
|
|
5
|
+
* `createFederation(opts)` factory. The orchestrator wires:
|
|
6
|
+
*
|
|
7
|
+
* sign — produce a signed FederationEnvelope (with fresh nonce +
|
|
8
|
+
* PII redaction + audit log entry).
|
|
9
|
+
* verify — verify an inbound envelope (HMAC + nonce window +
|
|
10
|
+
* trust + policy + audit log entry).
|
|
11
|
+
* send — high-level helper: sign + persist outgoing audit.
|
|
12
|
+
* receive — high-level helper: verify + accept-or-reject + persist
|
|
13
|
+
* inbound audit.
|
|
14
|
+
* status — snapshot for the `federation_status` MCP tool.
|
|
15
|
+
*
|
|
16
|
+
* No native deps. Pure TS over Node `crypto` + `fs`.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { randomUUID } from "node:crypto";
|
|
20
|
+
|
|
21
|
+
import {
|
|
22
|
+
canonicalSignablePayload,
|
|
23
|
+
emptyScanResult,
|
|
24
|
+
type EnvelopeBudget,
|
|
25
|
+
type FederationEnvelope,
|
|
26
|
+
type FederationMessageType,
|
|
27
|
+
} from "./envelope.js";
|
|
28
|
+
import { freshNonce, NonceCache, signEnvelope, verifySignature } from "./hmac.js";
|
|
29
|
+
import { apply, isPiiMode, type PiiMode, type PiiResult } from "./pii.js";
|
|
30
|
+
import { TrustEvaluator, type PeerState } from "./trust.js";
|
|
31
|
+
import { PolicyEngine, type PolicyDecision } from "./policy.js";
|
|
32
|
+
import { AuditService } from "./audit.js";
|
|
33
|
+
import { FederationBudget, validateBudgetInput, type PerPeerBudget } from "./budget.js";
|
|
34
|
+
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// Public re-exports
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
export {
|
|
40
|
+
FEDERATION_MESSAGE_TYPES,
|
|
41
|
+
TRUST_AFFECTING_MESSAGE_TYPES,
|
|
42
|
+
isFederationMessageType,
|
|
43
|
+
canonicalSignablePayload,
|
|
44
|
+
envelopeAgeMs,
|
|
45
|
+
emptyScanResult,
|
|
46
|
+
type EnvelopeBudget,
|
|
47
|
+
type FederationEnvelope,
|
|
48
|
+
type FederationMessageType,
|
|
49
|
+
type PiiScanAction,
|
|
50
|
+
type PiiScanDetection,
|
|
51
|
+
type PiiScanResult,
|
|
52
|
+
} from "./envelope.js";
|
|
53
|
+
|
|
54
|
+
export {
|
|
55
|
+
DEFAULT_NONCE_WINDOW_MS,
|
|
56
|
+
NonceCache,
|
|
57
|
+
freshNonce,
|
|
58
|
+
signEnvelope,
|
|
59
|
+
verifySignature,
|
|
60
|
+
type VerifyOptions,
|
|
61
|
+
type VerifyOutcome,
|
|
62
|
+
} from "./hmac.js";
|
|
63
|
+
|
|
64
|
+
export {
|
|
65
|
+
PII_MODES,
|
|
66
|
+
isPiiMode,
|
|
67
|
+
PiiPipeline,
|
|
68
|
+
apply,
|
|
69
|
+
type ApplyOptions,
|
|
70
|
+
type PiiAction,
|
|
71
|
+
type PiiCategory,
|
|
72
|
+
type PiiDetection,
|
|
73
|
+
type PiiMode,
|
|
74
|
+
type PiiResult,
|
|
75
|
+
} from "./pii.js";
|
|
76
|
+
|
|
77
|
+
export {
|
|
78
|
+
MAX_TRUST_AGE_MS,
|
|
79
|
+
MIN_TRUST_SCORE,
|
|
80
|
+
TrustEvaluator,
|
|
81
|
+
type PeerState,
|
|
82
|
+
type TrustDecision,
|
|
83
|
+
} from "./trust.js";
|
|
84
|
+
|
|
85
|
+
export {
|
|
86
|
+
DEFAULT_MAX_HOPS,
|
|
87
|
+
PolicyEngine,
|
|
88
|
+
type PolicyConfig,
|
|
89
|
+
type PolicyDecision,
|
|
90
|
+
} from "./policy.js";
|
|
91
|
+
|
|
92
|
+
export {
|
|
93
|
+
DEFAULT_AUDIT_PATH,
|
|
94
|
+
AuditService,
|
|
95
|
+
type AuditDecision,
|
|
96
|
+
type AuditEntry,
|
|
97
|
+
type AuditServiceOpts,
|
|
98
|
+
} from "./audit.js";
|
|
99
|
+
|
|
100
|
+
export {
|
|
101
|
+
DEFAULT_BUDGET_PATH,
|
|
102
|
+
MAX_TOKENS_CEILING,
|
|
103
|
+
MAX_USD_CEILING,
|
|
104
|
+
FederationBudget,
|
|
105
|
+
validateBudgetInput,
|
|
106
|
+
type FederationBudgetOpts,
|
|
107
|
+
type PerPeerBudget,
|
|
108
|
+
type ReserveResult,
|
|
109
|
+
} from "./budget.js";
|
|
110
|
+
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// Orchestrator
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
|
|
115
|
+
export interface FederationStatusSnapshot {
|
|
116
|
+
readonly nodeId: string;
|
|
117
|
+
readonly nonceCacheSize: number;
|
|
118
|
+
readonly auditSizeBytes: number;
|
|
119
|
+
readonly auditPath: string;
|
|
120
|
+
readonly budget: {
|
|
121
|
+
readonly path: string;
|
|
122
|
+
readonly perPeer: readonly PerPeerBudget[];
|
|
123
|
+
readonly outstandingReservations: number;
|
|
124
|
+
};
|
|
125
|
+
readonly peers: readonly PeerState[];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface CreateFederationOpts {
|
|
129
|
+
/** Local node id stamped into audit + status payloads. */
|
|
130
|
+
readonly nodeId: string;
|
|
131
|
+
/** Shared HMAC secret. Sender + receiver MUST match. */
|
|
132
|
+
readonly secret: string;
|
|
133
|
+
/** PII redaction mode applied on `sign()`. */
|
|
134
|
+
readonly piiMode?: PiiMode;
|
|
135
|
+
/** Override audit log path. */
|
|
136
|
+
readonly auditPath?: string;
|
|
137
|
+
/** Override budget JSON path. */
|
|
138
|
+
readonly budgetPath?: string;
|
|
139
|
+
/** Optional explicit PolicyEngine (for custom allowlists/blocklists). */
|
|
140
|
+
readonly policy?: PolicyEngine;
|
|
141
|
+
/** Optional explicit TrustEvaluator (custom allowlist). */
|
|
142
|
+
readonly trust?: TrustEvaluator;
|
|
143
|
+
/** Optional explicit AuditService (test-only override). */
|
|
144
|
+
readonly audit?: AuditService;
|
|
145
|
+
/** Optional explicit FederationBudget (test-only override). */
|
|
146
|
+
readonly budget?: FederationBudget;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface FederationHandle {
|
|
150
|
+
readonly nodeId: string;
|
|
151
|
+
/** PII redaction mode currently in effect. */
|
|
152
|
+
getPiiMode(): PiiMode;
|
|
153
|
+
setPiiMode(mode: PiiMode): void;
|
|
154
|
+
/** Sign + redact + audit. The resulting envelope is ready to
|
|
155
|
+
* transmit to the peer. */
|
|
156
|
+
sign<T>(input: SignInput<T>): FederationEnvelope<T>;
|
|
157
|
+
/** Verify + admit-or-reject + audit. The caller decides what
|
|
158
|
+
* to do with the result. */
|
|
159
|
+
receive<T>(envelope: FederationEnvelope<T>): ReceiveResult;
|
|
160
|
+
/** Snapshot for the `federation_status` MCP tool. */
|
|
161
|
+
status(): FederationStatusSnapshot;
|
|
162
|
+
/** Direct access to the underlying services — for advanced
|
|
163
|
+
* consumers + tests. */
|
|
164
|
+
readonly services: {
|
|
165
|
+
readonly trust: TrustEvaluator;
|
|
166
|
+
readonly policy: PolicyEngine;
|
|
167
|
+
readonly audit: AuditService;
|
|
168
|
+
readonly budget: FederationBudget;
|
|
169
|
+
readonly nonces: NonceCache;
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export interface SignInput<T> {
|
|
174
|
+
readonly targetNodeId: string;
|
|
175
|
+
readonly sessionId: string;
|
|
176
|
+
readonly messageType: FederationMessageType;
|
|
177
|
+
readonly payload: T;
|
|
178
|
+
readonly budget?: EnvelopeBudget;
|
|
179
|
+
/** If true, skip PII redaction. Defaults to false. */
|
|
180
|
+
readonly skipPii?: boolean;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export interface ReceiveResult {
|
|
184
|
+
readonly trusted: boolean;
|
|
185
|
+
readonly policyAllowed: boolean;
|
|
186
|
+
readonly hmacValid: boolean;
|
|
187
|
+
readonly pii: PiiResult;
|
|
188
|
+
readonly envelope: FederationEnvelope;
|
|
189
|
+
readonly rejectionReason?: string;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Build the orchestrator handle. */
|
|
193
|
+
export function createFederation(opts: CreateFederationOpts): FederationHandle {
|
|
194
|
+
if (!opts || typeof opts.nodeId !== "string" || opts.nodeId.length === 0) {
|
|
195
|
+
throw new Error("createFederation: nodeId is required");
|
|
196
|
+
}
|
|
197
|
+
if (typeof opts.secret !== "string" || opts.secret.length === 0) {
|
|
198
|
+
throw new Error("createFederation: secret is required");
|
|
199
|
+
}
|
|
200
|
+
const modeRef: { value: PiiMode } = { value: isPiiMode(opts.piiMode) ? opts.piiMode : "soc2" };
|
|
201
|
+
|
|
202
|
+
const trust = opts.trust ?? new TrustEvaluator({ allowlist: [] });
|
|
203
|
+
const policy = opts.policy ?? new PolicyEngine();
|
|
204
|
+
const audit = opts.audit ?? new AuditService({ nodeId: opts.nodeId, path: opts.auditPath });
|
|
205
|
+
const budget = opts.budget ?? new FederationBudget({ path: opts.budgetPath });
|
|
206
|
+
const nonces = new NonceCache();
|
|
207
|
+
|
|
208
|
+
function getPiiMode(): PiiMode { return modeRef.value; }
|
|
209
|
+
function setPiiMode(m: PiiMode): void {
|
|
210
|
+
if (!isPiiMode(m)) throw new Error(`unknown PII mode: ${m}`);
|
|
211
|
+
modeRef.value = m;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function sign<T>(input: SignInput<T>): FederationEnvelope<T> {
|
|
215
|
+
const validated = input.budget
|
|
216
|
+
? validateBudgetInput(input.budget)
|
|
217
|
+
: { ok: true as const, maxTokens: Number.POSITIVE_INFINITY, maxUsd: Number.POSITIVE_INFINITY, maxHops: 8 };
|
|
218
|
+
if (!validated.ok) throw new Error(`sign: bad budget: ${validated.error}`);
|
|
219
|
+
|
|
220
|
+
const env: FederationEnvelope<T> = {
|
|
221
|
+
envelopeId: randomUUID(),
|
|
222
|
+
sourceNodeId: opts.nodeId,
|
|
223
|
+
targetNodeId: input.targetNodeId,
|
|
224
|
+
sessionId: input.sessionId,
|
|
225
|
+
messageType: input.messageType,
|
|
226
|
+
payload: input.payload,
|
|
227
|
+
timestamp: new Date().toISOString(),
|
|
228
|
+
nonce: freshNonce(),
|
|
229
|
+
hmacSignature: "", // filled in below
|
|
230
|
+
piiScanResult: emptyScanResult(),
|
|
231
|
+
budget: {
|
|
232
|
+
maxTokens: validated.maxTokens,
|
|
233
|
+
maxUsd: validated.maxUsd,
|
|
234
|
+
maxHops: validated.maxHops,
|
|
235
|
+
},
|
|
236
|
+
hopCount: 0,
|
|
237
|
+
hopsRemaining: validated.maxHops,
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
// PII redaction over the stringified payload — keep the result
|
|
241
|
+
// in `piiScanResult` so the receiver can audit what was stripped.
|
|
242
|
+
let pii: PiiResult = { transformed: "", detections: [], actionsApplied: [], blocked: false, mode: modeRef.value };
|
|
243
|
+
if (!input.skipPii && typeof input.payload === "string") {
|
|
244
|
+
pii = apply(input.payload, modeRef.value);
|
|
245
|
+
if (pii.blocked) {
|
|
246
|
+
audit.record({
|
|
247
|
+
ts: new Date().toISOString(),
|
|
248
|
+
envelopeId: env.envelopeId,
|
|
249
|
+
sourceNodeId: env.sourceNodeId,
|
|
250
|
+
targetNodeId: env.targetNodeId,
|
|
251
|
+
messageType: env.messageType,
|
|
252
|
+
nonce: env.nonce,
|
|
253
|
+
allowed: false,
|
|
254
|
+
reason: `pii_blocked (mode=${modeRef.value})`,
|
|
255
|
+
layer: "budget",
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const finalEnv: FederationEnvelope<T> = {
|
|
261
|
+
...env,
|
|
262
|
+
payload: (pii.transformed ? (pii.transformed as unknown as T) : input.payload),
|
|
263
|
+
piiScanResult: pii.detections.length > 0
|
|
264
|
+
? {
|
|
265
|
+
scanned: true,
|
|
266
|
+
piiFound: true,
|
|
267
|
+
detections: pii.detections.map((d) => ({
|
|
268
|
+
type: d.category,
|
|
269
|
+
action: "redact",
|
|
270
|
+
confidence: d.confidence,
|
|
271
|
+
count: 1,
|
|
272
|
+
})),
|
|
273
|
+
actionsApplied: pii.actionsApplied.map((a) => a.action),
|
|
274
|
+
scanDurationMs: 0,
|
|
275
|
+
}
|
|
276
|
+
: emptyScanResult(),
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
const signature = signEnvelope(finalEnv, opts.secret);
|
|
280
|
+
const signed: FederationEnvelope<T> = { ...finalEnv, hmacSignature: signature };
|
|
281
|
+
|
|
282
|
+
audit.record({
|
|
283
|
+
ts: signed.timestamp,
|
|
284
|
+
envelopeId: signed.envelopeId,
|
|
285
|
+
sourceNodeId: signed.sourceNodeId,
|
|
286
|
+
targetNodeId: signed.targetNodeId,
|
|
287
|
+
messageType: signed.messageType,
|
|
288
|
+
nonce: signed.nonce,
|
|
289
|
+
allowed: true,
|
|
290
|
+
reason: `signed (pii_mode=${modeRef.value})`,
|
|
291
|
+
layer: "sent",
|
|
292
|
+
});
|
|
293
|
+
return signed;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function receive<T>(envelope: FederationEnvelope<T>): ReceiveResult {
|
|
297
|
+
// 1. HMAC + freshness + nonce replay
|
|
298
|
+
const verifyOut = verifySignature(envelope, opts.secret, nonces as unknown as ReadonlySet<string>);
|
|
299
|
+
if (verifyOut.ok) {
|
|
300
|
+
nonces.check(envelope.nonce);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const piiScanResult = envelope.piiScanResult ?? emptyScanResult();
|
|
304
|
+
const pii: PiiResult = {
|
|
305
|
+
transformed: typeof envelope.payload === "string" ? envelope.payload : "",
|
|
306
|
+
detections: piiScanResult.detections.map((d) => ({
|
|
307
|
+
category: d.type as never,
|
|
308
|
+
value: "",
|
|
309
|
+
offset: 0,
|
|
310
|
+
confidence: d.confidence,
|
|
311
|
+
})),
|
|
312
|
+
actionsApplied: piiScanResult.actionsApplied.map((a) => ({ category: "email", action: a })),
|
|
313
|
+
blocked: false,
|
|
314
|
+
mode: modeRef.value,
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
let trusted = false;
|
|
318
|
+
let policyAllowed = false;
|
|
319
|
+
let rejectionReason: string | undefined;
|
|
320
|
+
|
|
321
|
+
if (!verifyOut.ok) {
|
|
322
|
+
rejectionReason = `hmac:${verifyOut.reason}`;
|
|
323
|
+
audit.record({
|
|
324
|
+
ts: new Date().toISOString(),
|
|
325
|
+
envelopeId: envelope.envelopeId,
|
|
326
|
+
sourceNodeId: envelope.sourceNodeId,
|
|
327
|
+
targetNodeId: envelope.targetNodeId,
|
|
328
|
+
messageType: envelope.messageType,
|
|
329
|
+
nonce: envelope.nonce,
|
|
330
|
+
allowed: false,
|
|
331
|
+
reason: rejectionReason,
|
|
332
|
+
layer: "hmac",
|
|
333
|
+
});
|
|
334
|
+
} else {
|
|
335
|
+
// 2. Trust
|
|
336
|
+
const peer: PeerState = trust.getPeerState(envelope.sourceNodeId);
|
|
337
|
+
const trustOut = trust.evaluate(envelope, peer);
|
|
338
|
+
trusted = trustOut.allowed;
|
|
339
|
+
if (!trusted) {
|
|
340
|
+
rejectionReason = `trust:${trustOut.reason}`;
|
|
341
|
+
trust.recordOutcome(envelope.sourceNodeId, false);
|
|
342
|
+
audit.record({
|
|
343
|
+
ts: new Date().toISOString(),
|
|
344
|
+
envelopeId: envelope.envelopeId,
|
|
345
|
+
sourceNodeId: envelope.sourceNodeId,
|
|
346
|
+
targetNodeId: envelope.targetNodeId,
|
|
347
|
+
messageType: envelope.messageType,
|
|
348
|
+
nonce: envelope.nonce,
|
|
349
|
+
allowed: false,
|
|
350
|
+
reason: rejectionReason,
|
|
351
|
+
layer: "trust",
|
|
352
|
+
});
|
|
353
|
+
} else {
|
|
354
|
+
trust.recordOutcome(envelope.sourceNodeId, true);
|
|
355
|
+
// 3. Policy (hops + blocklist + message allowlist)
|
|
356
|
+
const polOut: PolicyDecision = policy.enforce(envelope, envelope.budget);
|
|
357
|
+
policyAllowed = polOut.allowed;
|
|
358
|
+
if (!polOut.allowed) {
|
|
359
|
+
rejectionReason = `policy:${polOut.denialReason}`;
|
|
360
|
+
audit.record({
|
|
361
|
+
ts: new Date().toISOString(),
|
|
362
|
+
envelopeId: envelope.envelopeId,
|
|
363
|
+
sourceNodeId: envelope.sourceNodeId,
|
|
364
|
+
targetNodeId: envelope.targetNodeId,
|
|
365
|
+
messageType: envelope.messageType,
|
|
366
|
+
nonce: envelope.nonce,
|
|
367
|
+
allowed: false,
|
|
368
|
+
reason: rejectionReason,
|
|
369
|
+
layer: "policy",
|
|
370
|
+
});
|
|
371
|
+
} else {
|
|
372
|
+
audit.record({
|
|
373
|
+
ts: new Date().toISOString(),
|
|
374
|
+
envelopeId: envelope.envelopeId,
|
|
375
|
+
sourceNodeId: envelope.sourceNodeId,
|
|
376
|
+
targetNodeId: envelope.targetNodeId,
|
|
377
|
+
messageType: envelope.messageType,
|
|
378
|
+
nonce: envelope.nonce,
|
|
379
|
+
allowed: true,
|
|
380
|
+
reason: `accepted (score=${trustOut.score.toFixed(3)})`,
|
|
381
|
+
layer: "received",
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return {
|
|
388
|
+
trusted,
|
|
389
|
+
policyAllowed,
|
|
390
|
+
hmacValid: verifyOut.ok,
|
|
391
|
+
pii,
|
|
392
|
+
envelope,
|
|
393
|
+
rejectionReason,
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function status(): FederationStatusSnapshot {
|
|
398
|
+
// Synthesize a peer list from audit history + TrustEvaluator internal map.
|
|
399
|
+
const peerIds = new Set<string>();
|
|
400
|
+
const tail = audit.tail(1000);
|
|
401
|
+
for (const entry of tail) peerIds.add(entry.sourceNodeId);
|
|
402
|
+
const peerSnapshots: PeerState[] = [];
|
|
403
|
+
for (const id of peerIds) peerSnapshots.push(trust.getPeerState(id));
|
|
404
|
+
|
|
405
|
+
return {
|
|
406
|
+
nodeId: opts.nodeId,
|
|
407
|
+
nonceCacheSize: nonces.size(),
|
|
408
|
+
auditSizeBytes: audit.size(),
|
|
409
|
+
auditPath: audit.getPath(),
|
|
410
|
+
budget: {
|
|
411
|
+
path: opts.budgetPath ?? ".harness/federation-budget.json",
|
|
412
|
+
perPeer: budget.snapshot(),
|
|
413
|
+
outstandingReservations: budget.outstanding().length,
|
|
414
|
+
},
|
|
415
|
+
peers: peerSnapshots,
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return {
|
|
420
|
+
nodeId: opts.nodeId,
|
|
421
|
+
getPiiMode,
|
|
422
|
+
setPiiMode,
|
|
423
|
+
sign,
|
|
424
|
+
receive,
|
|
425
|
+
status,
|
|
426
|
+
services: { trust, policy, audit, budget, nonces },
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// ---------------------------------------------------------------------------
|
|
431
|
+
// Convenience helper — re-export `canonicalSignablePayload` so callers
|
|
432
|
+
// that want to compute their own signature can import it from the top.
|
|
433
|
+
// ---------------------------------------------------------------------------
|
|
434
|
+
|
|
435
|
+
export { canonicalSignablePayload as buildSignablePayload };
|
|
436
|
+
|
|
437
|
+
// ---------------------------------------------------------------------------
|
|
438
|
+
// Helper for tests — produce a fresh envelope-id.
|
|
439
|
+
// ---------------------------------------------------------------------------
|
|
440
|
+
|
|
441
|
+
export function freshEnvelopeId(): string {
|
|
442
|
+
return randomUUID();
|
|
443
|
+
}
|