@polderlabs/bizar 6.2.5 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (540) hide show
  1. package/.claude/CLAUDE.md +15 -0
  2. package/.claude/agents/_shared/AGENT_BASELINE.md +168 -0
  3. package/.claude/agents/_shared/CLAUDE_TOOLS.md +412 -0
  4. package/.claude/agents/_shared/SKILLS.md +109 -0
  5. package/.claude/agents/agent-browser.md +80 -0
  6. package/.claude/agents/baldr.md +49 -0
  7. package/.claude/agents/forseti.md +51 -0
  8. package/.claude/agents/frigg.md +42 -0
  9. package/.claude/agents/heimdall.md +33 -0
  10. package/.claude/agents/hermod.md +53 -0
  11. package/.claude/agents/mimir.md +49 -0
  12. package/.claude/agents/odin.md +287 -0
  13. package/.claude/agents/quick.md +34 -0
  14. package/.claude/agents/semble-search.md +50 -0
  15. package/.claude/agents/thor.md +53 -0
  16. package/.claude/agents/tyr.md +56 -0
  17. package/.claude/agents/vidarr.md +54 -0
  18. package/.claude/agents/vor.md +53 -0
  19. package/.claude/commands/audit.md +25 -0
  20. package/.claude/commands/bizar.md +22 -0
  21. package/.claude/commands/explain.md +17 -0
  22. package/.claude/commands/init.md +29 -0
  23. package/.claude/commands/learn.md +44 -0
  24. package/.claude/commands/plan.md +35 -0
  25. package/.claude/commands/plow-through.md +50 -0
  26. package/.claude/commands/pr-review.md +49 -0
  27. package/.claude/commands/setup-provider.md +96 -0
  28. package/.claude/commands/tailscale-serve.md +100 -0
  29. package/.claude/commands/team.md +132 -0
  30. package/.claude/commands/test.md +62 -0
  31. package/.claude/commands/validate.md +68 -0
  32. package/.claude/commands/visual-plan.md +24 -0
  33. package/.claude/hooks/README.md +92 -0
  34. package/.claude/hooks/posttooluse-editwrite.mjs +91 -0
  35. package/.claude/hooks/pretooluse-bash.mjs +81 -0
  36. package/.claude/hooks/pretooluse-editwrite.mjs +139 -0
  37. package/.claude/hooks/sessionend-recall.mjs +74 -0
  38. package/.claude/hooks/sessionstart-prime.mjs +80 -0
  39. package/.claude/hooks/userpromptsubmit-tag.mjs +80 -0
  40. package/.claude/hooks/worker-suggest.mjs +110 -0
  41. package/.claude/settings.json +125 -0
  42. package/.claude/skills/9router/SKILL.md +80 -0
  43. package/.claude/skills/9router-chat/SKILL.md +73 -0
  44. package/.claude/skills/9router-embeddings/SKILL.md +69 -0
  45. package/.claude/skills/9router-image/SKILL.md +86 -0
  46. package/.claude/skills/9router-stt/SKILL.md +79 -0
  47. package/.claude/skills/9router-tts/SKILL.md +80 -0
  48. package/.claude/skills/9router-web-fetch/SKILL.md +99 -0
  49. package/.claude/skills/9router-web-search/SKILL.md +91 -0
  50. package/.claude/skills/bizar/README.md +9 -0
  51. package/.claude/skills/bizar/SKILL.md +450 -0
  52. package/.claude/skills/cpp-coding-standards/README.md +28 -0
  53. package/.claude/skills/cpp-coding-standards/SKILL.md +634 -0
  54. package/.claude/skills/cpp-coding-standards/references/concurrency.md +320 -0
  55. package/.claude/skills/cpp-coding-standards/references/error-handling.md +229 -0
  56. package/.claude/skills/cpp-coding-standards/references/memory-safety.md +216 -0
  57. package/.claude/skills/cpp-coding-standards/references/modern-idioms.md +282 -0
  58. package/.claude/skills/cpp-coding-standards/references/review-checklist.md +96 -0
  59. package/.claude/skills/cpp-testing/README.md +28 -0
  60. package/.claude/skills/cpp-testing/SKILL.md +304 -0
  61. package/.claude/skills/cpp-testing/references/coverage.md +370 -0
  62. package/.claude/skills/cpp-testing/references/framework-compare.md +175 -0
  63. package/.claude/skills/cpp-testing/references/host-test-for-embedded.md +499 -0
  64. package/.claude/skills/cpp-testing/references/mocking.md +364 -0
  65. package/.claude/skills/cpp-testing/references/tdd-workflow.md +308 -0
  66. package/.claude/skills/cubesandbox/SKILL.md +148 -0
  67. package/.claude/skills/embedded-esp-idf/README.md +41 -0
  68. package/.claude/skills/embedded-esp-idf/SKILL.md +439 -0
  69. package/.claude/skills/embedded-esp-idf/references/freertos-patterns.md +214 -0
  70. package/.claude/skills/embedded-esp-idf/references/host-tests.md +164 -0
  71. package/.claude/skills/embedded-esp-idf/references/idf-py-commands.md +157 -0
  72. package/.claude/skills/embedded-esp-idf/references/kconfig.md +159 -0
  73. package/.claude/skills/embedded-esp-idf/references/logging-discipline.md +118 -0
  74. package/.claude/skills/embedded-esp-idf/references/memory-and-iram.md +137 -0
  75. package/.claude/skills/embedded-esp-idf/references/nvs.md +121 -0
  76. package/.claude/skills/embedded-esp-idf/references/packed-structs.md +192 -0
  77. package/.claude/skills/embedded-esp-idf/scripts/idf_env.sh +47 -0
  78. package/.claude/skills/embedded-esp-idf/scripts/size_check.sh +77 -0
  79. package/.claude/skills/glyph/SKILL.md +163 -0
  80. package/.claude/skills/harness-engineering/SKILL.md +143 -0
  81. package/.claude/skills/lightrag/SKILL.md +81 -0
  82. package/.claude/skills/memory-protocol/SKILL.md +105 -0
  83. package/.claude/skills/obsidian/SKILL.md +306 -0
  84. package/.claude/skills/read-the-damn-docs/SKILL.md +113 -0
  85. package/.claude/skills/self-improvement/SKILL.md +64 -0
  86. package/README.md +87 -59
  87. package/bizar-dash/dist/assets/{EnvVarsSection-DXM8gRm_.js → EnvVarsSection-CW6fWKds.js} +3 -3
  88. package/bizar-dash/dist/assets/{EnvVarsSection-DXM8gRm_.js.map → EnvVarsSection-CW6fWKds.js.map} +1 -1
  89. package/bizar-dash/dist/assets/MobileChat-D7zRhU9K.js +1 -0
  90. package/bizar-dash/dist/assets/{MobileChat-BnKN_Ks_.js.map → MobileChat-D7zRhU9K.js.map} +1 -1
  91. package/bizar-dash/dist/assets/MobileSettings-K-sNsn_r.js +1 -0
  92. package/bizar-dash/dist/assets/{MobileSettings-DjCPxC-Q.js.map → MobileSettings-K-sNsn_r.js.map} +1 -1
  93. package/bizar-dash/dist/assets/{Toast-Cpl6-E63.js → Toast-WTxc3m2g.js} +1 -1
  94. package/bizar-dash/dist/assets/{Toast-Cpl6-E63.js.map → Toast-WTxc3m2g.js.map} +1 -1
  95. package/bizar-dash/dist/assets/flow-BpU0-gK2.js +1 -0
  96. package/bizar-dash/dist/assets/flow-BpU0-gK2.js.map +1 -0
  97. package/bizar-dash/dist/assets/fuzzy-l0sNRNKZ.js +1 -0
  98. package/bizar-dash/dist/assets/fuzzy-l0sNRNKZ.js.map +1 -0
  99. package/bizar-dash/dist/assets/{icons-Do5N2jmX.js → icons-B0AKnYI3.js} +98 -103
  100. package/bizar-dash/dist/assets/icons-B0AKnYI3.js.map +1 -0
  101. package/bizar-dash/dist/assets/main-C-Md3DaY.css +1 -0
  102. package/bizar-dash/dist/assets/main-CO8fIxQK.js +18 -0
  103. package/bizar-dash/dist/assets/main-CO8fIxQK.js.map +1 -0
  104. package/bizar-dash/dist/assets/markdown-w2bDDkAH.js +1 -0
  105. package/bizar-dash/dist/assets/{markdown-C6mXtQxD.js.map → markdown-w2bDDkAH.js.map} +1 -1
  106. package/bizar-dash/dist/assets/mobile-DXYCA0hC.js +1 -0
  107. package/bizar-dash/dist/assets/{mobile-CMHqtLV2.js.map → mobile-DXYCA0hC.js.map} +1 -1
  108. package/bizar-dash/dist/assets/mobile-layout-DSmtNPja.js +2 -0
  109. package/bizar-dash/dist/assets/mobile-layout-DSmtNPja.js.map +1 -0
  110. package/bizar-dash/dist/assets/react-vendor-BnuhLJ6X.css +1 -0
  111. package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js +46 -0
  112. package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js.map +1 -0
  113. package/bizar-dash/dist/assets/useSlashCommands-3DFLOfpk.js +2 -0
  114. package/bizar-dash/dist/assets/useSlashCommands-3DFLOfpk.js.map +1 -0
  115. package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js +45 -0
  116. package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js.map +1 -0
  117. package/bizar-dash/dist/index.html +12 -10
  118. package/bizar-dash/dist/mobile.html +7 -4
  119. package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  120. package/bizar-dash/src/server/api.mjs +15 -4
  121. package/bizar-dash/src/server/background-store.mjs +15 -16
  122. package/bizar-dash/src/server/bg-poller.mjs +25 -35
  123. package/bizar-dash/src/server/bg-retry.mjs +66 -194
  124. package/bizar-dash/src/server/claude-bg-spawner.mjs +390 -0
  125. package/bizar-dash/src/server/claude-info.mjs +412 -0
  126. package/bizar-dash/src/server/{cline-runner.mjs → claude-runner.mjs} +53 -48
  127. package/bizar-dash/src/server/claude-sdk.mjs +191 -0
  128. package/bizar-dash/src/server/memory-consolidator.mjs +218 -0
  129. package/bizar-dash/src/server/memory-distillation.mjs +296 -0
  130. package/bizar-dash/src/server/providers-store.mjs +22 -49
  131. package/bizar-dash/src/server/routes/background.mjs +24 -20
  132. package/bizar-dash/src/server/routes/chat.mjs +223 -326
  133. package/bizar-dash/src/server/routes/claude-session-detail.mjs +329 -0
  134. package/bizar-dash/src/server/routes/claude-sessions.mjs +259 -0
  135. package/bizar-dash/src/server/routes/distill.mjs +129 -0
  136. package/bizar-dash/src/server/routes/goal-planner.mjs +236 -0
  137. package/bizar-dash/src/server/routes/tasks.mjs +20 -28
  138. package/bizar-dash/src/server/task-delegator.mjs +76 -77
  139. package/bizar-dash/src/web/App.tsx +4 -0
  140. package/bizar-dash/src/web/components/Tag.tsx +18 -5
  141. package/bizar-dash/src/web/components/Topbar.tsx +3 -0
  142. package/bizar-dash/src/web/components/agents/CommunicationLog.tsx +126 -0
  143. package/bizar-dash/src/web/components/agents/DependencyGraph.tsx +163 -0
  144. package/bizar-dash/src/web/components/agents/QualityGates.tsx +123 -0
  145. package/bizar-dash/src/web/components/agents/RealTimeEventLog.tsx +127 -0
  146. package/bizar-dash/src/web/components/agents/RoutingDecisions.test.tsx +148 -0
  147. package/bizar-dash/src/web/components/agents/RoutingDecisions.tsx +160 -0
  148. package/bizar-dash/src/web/components/chat/useChat.ts +23 -27
  149. package/bizar-dash/src/web/components/goals/GoalInput.tsx +86 -0
  150. package/bizar-dash/src/web/components/goals/PlanVisualization.tsx +111 -0
  151. package/bizar-dash/src/web/lib/goapPlanner.ts +614 -0
  152. package/bizar-dash/src/web/lib/types.ts +25 -0
  153. package/bizar-dash/src/web/styles/agents-redesign.css +311 -0
  154. package/bizar-dash/src/web/styles/main.css +251 -0
  155. package/bizar-dash/src/web/styles/tasks-redesign.css +231 -0
  156. package/bizar-dash/src/web/ui/controls/Button.tsx +77 -0
  157. package/bizar-dash/src/web/ui/controls/Checkbox.tsx +95 -0
  158. package/bizar-dash/src/web/ui/controls/IconButton.tsx +66 -0
  159. package/bizar-dash/src/web/ui/controls/Kbd.tsx +24 -0
  160. package/bizar-dash/src/web/ui/controls/NumberInput.tsx +77 -0
  161. package/bizar-dash/src/web/ui/controls/RadioGroup.tsx +109 -0
  162. package/bizar-dash/src/web/ui/controls/SearchInput.tsx +91 -0
  163. package/bizar-dash/src/web/ui/controls/Select.tsx +92 -0
  164. package/bizar-dash/src/web/ui/controls/Slider.tsx +70 -0
  165. package/bizar-dash/src/web/ui/controls/TextInput.tsx +67 -0
  166. package/bizar-dash/src/web/ui/controls/Toggle.tsx +79 -0
  167. package/bizar-dash/src/web/ui/controls/controls.css +454 -0
  168. package/bizar-dash/src/web/ui/controls/index.ts +33 -0
  169. package/bizar-dash/src/web/ui/data/BarChart.tsx +60 -0
  170. package/bizar-dash/src/web/ui/data/DataTable.tsx +216 -0
  171. package/bizar-dash/src/web/ui/data/EmptyState.tsx +60 -0
  172. package/bizar-dash/src/web/ui/data/ErrorState.tsx +56 -0
  173. package/bizar-dash/src/web/ui/data/KeyValueList.tsx +106 -0
  174. package/bizar-dash/src/web/ui/data/LoadingState.tsx +65 -0
  175. package/bizar-dash/src/web/ui/data/Sparkline.tsx +87 -0
  176. package/bizar-dash/src/web/ui/data/StatTile.tsx +87 -0
  177. package/bizar-dash/src/web/ui/data/data.css +535 -0
  178. package/bizar-dash/src/web/ui/data/index.ts +31 -0
  179. package/bizar-dash/src/web/ui/feedback/Badge.tsx +49 -0
  180. package/bizar-dash/src/web/ui/feedback/Dialog.tsx +209 -0
  181. package/bizar-dash/src/web/ui/feedback/ProgressBar.tsx +69 -0
  182. package/bizar-dash/src/web/ui/feedback/StatusDot.tsx +50 -0
  183. package/bizar-dash/src/web/ui/feedback/Toast.tsx +200 -0
  184. package/bizar-dash/src/web/ui/feedback/Tooltip.tsx +126 -0
  185. package/bizar-dash/src/web/ui/feedback/feedback.css +314 -0
  186. package/bizar-dash/src/web/ui/feedback/index.ts +43 -0
  187. package/bizar-dash/src/web/ui/index.ts +56 -0
  188. package/bizar-dash/src/web/ui/layout/AppShell.tsx +51 -0
  189. package/bizar-dash/src/web/ui/layout/Breadcrumbs.tsx +97 -0
  190. package/bizar-dash/src/web/ui/layout/Panel.tsx +73 -0
  191. package/bizar-dash/src/web/ui/layout/PanelHeader.tsx +39 -0
  192. package/bizar-dash/src/web/ui/layout/Sidebar.tsx +131 -0
  193. package/bizar-dash/src/web/ui/layout/Tabs.tsx +133 -0
  194. package/bizar-dash/src/web/ui/layout/Topbar.tsx +42 -0
  195. package/bizar-dash/src/web/ui/layout/index.ts +33 -0
  196. package/bizar-dash/src/web/ui/layout/layout.css +413 -0
  197. package/bizar-dash/src/web/ui/navigation/CommandPalette.tsx +199 -0
  198. package/bizar-dash/src/web/ui/navigation/NavGroup.tsx +95 -0
  199. package/bizar-dash/src/web/ui/navigation/NavLink.tsx +93 -0
  200. package/bizar-dash/src/web/ui/navigation/index.ts +17 -0
  201. package/bizar-dash/src/web/ui/navigation/navigation.css +343 -0
  202. package/bizar-dash/src/web/ui/primitives/Box.tsx +91 -0
  203. package/bizar-dash/src/web/ui/primitives/Grid.tsx +49 -0
  204. package/bizar-dash/src/web/ui/primitives/Inline.tsx +55 -0
  205. package/bizar-dash/src/web/ui/primitives/Separator.tsx +46 -0
  206. package/bizar-dash/src/web/ui/primitives/Stack.tsx +54 -0
  207. package/bizar-dash/src/web/ui/primitives/VisuallyHidden.tsx +26 -0
  208. package/bizar-dash/src/web/ui/primitives/index.ts +23 -0
  209. package/bizar-dash/src/web/ui/primitives/primitives.css +183 -0
  210. package/bizar-dash/src/web/ui/styles/globals.css +86 -0
  211. package/bizar-dash/src/web/ui/styles/reset.css +125 -0
  212. package/bizar-dash/src/web/ui/styles/tokens.css +195 -0
  213. package/bizar-dash/src/web/ui/theme/ThemeProvider.tsx +142 -0
  214. package/bizar-dash/src/web/ui/theme/tokens.ts +131 -0
  215. package/bizar-dash/src/web/ui/theme/useTheme.ts +22 -0
  216. package/bizar-dash/src/web/ui/utils/cx.ts +42 -0
  217. package/bizar-dash/src/web/views/Agents.tsx +506 -266
  218. package/bizar-dash/src/web/views/Chat.tsx +8 -8
  219. package/bizar-dash/src/web/views/GoalPlanner.tsx +158 -0
  220. package/bizar-dash/src/web/views/Overview.tsx +996 -448
  221. package/bizar-dash/src/web/views/Tasks.tsx +277 -273
  222. package/bizar-dash/tests/communication-log.test.tsx +67 -0
  223. package/bizar-dash/tests/dep-graph.test.tsx +40 -0
  224. package/bizar-dash/tests/event-log.test.tsx +61 -0
  225. package/bizar-dash/tests/goal-input.test.tsx +72 -0
  226. package/bizar-dash/tests/goap-planner.test.ts +68 -0
  227. package/bizar-dash/tests/quality-gates.test.tsx +59 -0
  228. package/bizar-dash/tests/setup.ts +31 -1
  229. package/bizar-dash/tests/ui/controls/Button.test.tsx +50 -0
  230. package/bizar-dash/tests/ui/controls/Checkbox.test.tsx +44 -0
  231. package/bizar-dash/tests/ui/controls/IconButton.test.tsx +37 -0
  232. package/bizar-dash/tests/ui/controls/Kbd.test.tsx +39 -0
  233. package/bizar-dash/tests/ui/controls/NumberInput.test.tsx +56 -0
  234. package/bizar-dash/tests/ui/controls/RadioGroup.test.tsx +58 -0
  235. package/bizar-dash/tests/ui/controls/SearchInput.test.tsx +52 -0
  236. package/bizar-dash/tests/ui/controls/Select.test.tsx +66 -0
  237. package/bizar-dash/tests/ui/controls/Slider.test.tsx +41 -0
  238. package/bizar-dash/tests/ui/controls/TextInput.test.tsx +44 -0
  239. package/bizar-dash/tests/ui/controls/Toggle.test.tsx +39 -0
  240. package/bizar-dash/tests/ui/data/BarChart.test.tsx +55 -0
  241. package/bizar-dash/tests/ui/data/DataTable.test.tsx +112 -0
  242. package/bizar-dash/tests/ui/data/EmptyState.test.tsx +57 -0
  243. package/bizar-dash/tests/ui/data/ErrorState.test.tsx +47 -0
  244. package/bizar-dash/tests/ui/data/KeyValueList.test.tsx +59 -0
  245. package/bizar-dash/tests/ui/data/LoadingState.test.tsx +37 -0
  246. package/bizar-dash/tests/ui/data/Sparkline.test.tsx +56 -0
  247. package/bizar-dash/tests/ui/data/StatTile.test.tsx +69 -0
  248. package/bizar-dash/tests/ui/feedback/Badge.test.tsx +41 -0
  249. package/bizar-dash/tests/ui/feedback/Dialog.test.tsx +99 -0
  250. package/bizar-dash/tests/ui/feedback/ProgressBar.test.tsx +66 -0
  251. package/bizar-dash/tests/ui/feedback/StatusDot.test.tsx +41 -0
  252. package/bizar-dash/tests/ui/feedback/Toast.test.tsx +124 -0
  253. package/bizar-dash/tests/ui/feedback/Tooltip.test.tsx +102 -0
  254. package/bizar-dash/tests/ui/layout/AppShell.test.tsx +50 -0
  255. package/bizar-dash/tests/ui/layout/Breadcrumbs.test.tsx +85 -0
  256. package/bizar-dash/tests/ui/layout/Panel.test.tsx +52 -0
  257. package/bizar-dash/tests/ui/layout/PanelHeader.test.tsx +32 -0
  258. package/bizar-dash/tests/ui/layout/Sidebar.test.tsx +95 -0
  259. package/bizar-dash/tests/ui/layout/Tabs.test.tsx +87 -0
  260. package/bizar-dash/tests/ui/layout/Topbar.test.tsx +41 -0
  261. package/bizar-dash/tests/ui/navigation/CommandPalette.test.tsx +105 -0
  262. package/bizar-dash/tests/ui/navigation/NavGroup.test.tsx +55 -0
  263. package/bizar-dash/tests/ui/navigation/NavLink.test.tsx +61 -0
  264. package/bizar-dash/tests/ui/primitives/Box.test.tsx +49 -0
  265. package/bizar-dash/tests/ui/primitives/Grid.test.tsx +37 -0
  266. package/bizar-dash/tests/ui/primitives/Inline.test.tsx +33 -0
  267. package/bizar-dash/tests/ui/primitives/Separator.test.tsx +34 -0
  268. package/bizar-dash/tests/ui/primitives/Stack.test.tsx +45 -0
  269. package/bizar-dash/tests/ui/primitives/VisuallyHidden.test.tsx +27 -0
  270. package/bizar-dash/tests/views/Agents.test.tsx +359 -0
  271. package/bizar-dash/tests/views/Overview.test.tsx +413 -0
  272. package/bizar-dash/tests/views/Tasks.test.tsx +250 -0
  273. package/bizar-dash/vitest.config.ts +3 -1
  274. package/cli/__tests__/cost-gate.test.mjs +301 -0
  275. package/cli/__tests__/feature-list-bridge.test.mjs +371 -0
  276. package/cli/bin.mjs +55 -18
  277. package/cli/commands/claim.mjs +282 -0
  278. package/cli/commands/claude-cmd.mjs +348 -0
  279. package/cli/commands/cost.mjs +279 -0
  280. package/cli/commands/install.mjs +12 -11
  281. package/cli/commands/validate.mjs +252 -325
  282. package/cli/cost-gate.mjs +501 -0
  283. package/cli/dev-link.test.mjs +11 -11
  284. package/cli/doctor.mjs +131 -213
  285. package/cli/feature-list-bridge.mjs +400 -0
  286. package/cli/install.mjs +33 -37
  287. package/cli/install.test.mjs +9 -9
  288. package/cli/memory-constants.mjs +17 -0
  289. package/cli/provision-claude.mjs +893 -0
  290. package/cli/provision.mjs +224 -227
  291. package/cli/utils.mjs +72 -58
  292. package/cli/worker-dispatcher.mjs +259 -0
  293. package/cli/worker-dispatcher.test.mjs +204 -0
  294. package/config/AGENTS.md +28 -28
  295. package/config/trigger-patterns.json +186 -0
  296. package/install.sh +66 -44
  297. package/package.json +19 -11
  298. package/packages/sdk/ARCHITECTURE.md +40 -36
  299. package/packages/sdk/package.json +15 -5
  300. package/packages/sdk/src/agent-registry.ts +304 -0
  301. package/packages/sdk/src/consensus/byzantine.ts +587 -0
  302. package/packages/sdk/src/consensus/index.ts +147 -0
  303. package/packages/sdk/src/consensus/queen.ts +173 -0
  304. package/packages/sdk/src/consensus/types.ts +199 -0
  305. package/{plugins/bizar → packages/sdk}/src/dangerous-patterns.ts +6 -28
  306. package/packages/sdk/src/federation/audit.ts +156 -0
  307. package/packages/sdk/src/federation/budget.ts +269 -0
  308. package/packages/sdk/src/federation/envelope.ts +144 -0
  309. package/packages/sdk/src/federation/hmac.ts +160 -0
  310. package/packages/sdk/src/federation/index.ts +443 -0
  311. package/packages/sdk/src/federation/pii.ts +241 -0
  312. package/packages/sdk/src/federation/policy.ts +112 -0
  313. package/packages/sdk/src/federation/trust.ts +146 -0
  314. package/packages/sdk/src/fingerprint.ts +0 -0
  315. package/packages/sdk/src/index.ts +101 -69
  316. package/packages/sdk/src/mcp/bin.ts +51 -0
  317. package/packages/sdk/src/mcp/server.ts +848 -0
  318. package/{plugins/bizar/src/memory-vault.ts → packages/sdk/src/memory/index.ts} +18 -21
  319. package/packages/sdk/src/router/codemod-intent.ts +125 -0
  320. package/packages/sdk/src/router/index.ts +247 -0
  321. package/packages/sdk/src/router/memory-distillation-shim.mjs +18 -0
  322. package/packages/sdk/src/router/memory-distillation.ts +249 -0
  323. package/packages/sdk/src/router/model-router.ts +235 -0
  324. package/packages/sdk/src/router/q-learning-router.ts +304 -0
  325. package/packages/sdk/src/swarm-topology.ts +279 -0
  326. package/packages/sdk/tests/agent-registry.test.ts +285 -0
  327. package/packages/sdk/tests/consensus.test.ts +376 -0
  328. package/packages/sdk/tests/federation/audit.test.ts +164 -0
  329. package/packages/sdk/tests/federation/budget.test.ts +189 -0
  330. package/packages/sdk/tests/federation/envelope.test.ts +120 -0
  331. package/packages/sdk/tests/federation/hmac.test.ts +194 -0
  332. package/packages/sdk/tests/federation/orchestrator.test.ts +176 -0
  333. package/packages/sdk/tests/federation/pii.test.ts +106 -0
  334. package/packages/sdk/tests/federation/policy.test.ts +140 -0
  335. package/packages/sdk/tests/federation/trust.test.ts +124 -0
  336. package/packages/sdk/tests/mcp-tools.test.ts +204 -0
  337. package/packages/sdk/tests/memory-distillation.test.mjs +186 -0
  338. package/packages/sdk/tests/model-router.test.mjs +108 -0
  339. package/packages/sdk/tests/q-learning-router.test.mjs +100 -0
  340. package/packages/sdk/tests/router-orchestrator.test.mjs +172 -0
  341. package/packages/sdk/tests/router.test.mjs +91 -0
  342. package/packages/sdk/tests/sdk.test.mjs +172 -0
  343. package/packages/sdk/tests/swarm-topology.test.ts +258 -0
  344. package/packages/sdk/vitest.config.ts +1 -1
  345. package/scripts/bh-full-e2e.mjs +167 -368
  346. package/scripts/check-deps.mjs +28 -46
  347. package/scripts/mirror-claude-md.sh +78 -0
  348. package/scripts/test-in-container.sh +31 -9
  349. package/templates/clean-state-checklist.md +3 -3
  350. package/templates/sprint-contract.md +2 -2
  351. package/bizar-dash/.bizar/activity.log +0 -3
  352. package/bizar-dash/.omx/logs/omx-2026-07-07.jsonl +0 -1
  353. package/bizar-dash/.omx/state/session.json +0 -10
  354. package/bizar-dash/bizar-design/EXPLANATION.md +0 -307
  355. package/bizar-dash/bizar-design/INSTRUCTIONS.md +0 -149
  356. package/bizar-dash/bizar-design/canvas.html +0 -281
  357. package/bizar-dash/bizar-design/canvas.html.artifact.json +0 -19
  358. package/bizar-dash/bizar-design/components.css +0 -1665
  359. package/bizar-dash/bizar-design/components.html +0 -557
  360. package/bizar-dash/bizar-design/components.html.artifact.json +0 -19
  361. package/bizar-dash/bizar-design/desktop/agents.html +0 -74
  362. package/bizar-dash/bizar-design/desktop/agents.html.artifact.json +0 -19
  363. package/bizar-dash/bizar-design/desktop/memory.html +0 -75
  364. package/bizar-dash/bizar-design/desktop/memory.html.artifact.json +0 -19
  365. package/bizar-dash/bizar-design/desktop/metrics.html +0 -63
  366. package/bizar-dash/bizar-design/desktop/metrics.html.artifact.json +0 -19
  367. package/bizar-dash/bizar-design/desktop/overview.html +0 -95
  368. package/bizar-dash/bizar-design/desktop/overview.html.artifact.json +0 -19
  369. package/bizar-dash/bizar-design/desktop/settings.html +0 -70
  370. package/bizar-dash/bizar-design/desktop/settings.html.artifact.json +0 -19
  371. package/bizar-dash/bizar-design/icons.svg +0 -44
  372. package/bizar-dash/bizar-design/index.html +0 -192
  373. package/bizar-dash/bizar-design/index.html.artifact.json +0 -19
  374. package/bizar-dash/bizar-design/mobile/agents.html +0 -29
  375. package/bizar-dash/bizar-design/mobile/agents.html.artifact.json +0 -19
  376. package/bizar-dash/bizar-design/mobile/memory.html +0 -28
  377. package/bizar-dash/bizar-design/mobile/memory.html.artifact.json +0 -19
  378. package/bizar-dash/bizar-design/mobile/metrics.html +0 -29
  379. package/bizar-dash/bizar-design/mobile/metrics.html.artifact.json +0 -19
  380. package/bizar-dash/bizar-design/mobile/overview.html +0 -33
  381. package/bizar-dash/bizar-design/mobile/overview.html.artifact.json +0 -19
  382. package/bizar-dash/bizar-design/mobile/settings.html +0 -28
  383. package/bizar-dash/bizar-design/mobile/settings.html.artifact.json +0 -19
  384. package/bizar-dash/bizar-design/prototype.js +0 -114
  385. package/bizar-dash/bizar-design/tokens.css +0 -106
  386. package/bizar-dash/dist/assets/MobileChat-BnKN_Ks_.js +0 -1
  387. package/bizar-dash/dist/assets/MobileSettings-DjCPxC-Q.js +0 -1
  388. package/bizar-dash/dist/assets/icons-Do5N2jmX.js.map +0 -1
  389. package/bizar-dash/dist/assets/main-DYiZqMrn.js +0 -18
  390. package/bizar-dash/dist/assets/main-DYiZqMrn.js.map +0 -1
  391. package/bizar-dash/dist/assets/main-ietCEg_R.css +0 -1
  392. package/bizar-dash/dist/assets/markdown-C6mXtQxD.js +0 -1
  393. package/bizar-dash/dist/assets/mobile-CMHqtLV2.js +0 -1
  394. package/bizar-dash/dist/assets/mobile-layout-3jIhHX_p.js +0 -2
  395. package/bizar-dash/dist/assets/mobile-layout-3jIhHX_p.js.map +0 -1
  396. package/bizar-dash/dist/assets/react-vendor-Dn4wqh4Z.js +0 -40
  397. package/bizar-dash/dist/assets/react-vendor-Dn4wqh4Z.js.map +0 -1
  398. package/bizar-dash/dist/assets/useSlashCommands-DtITw8Xv.js +0 -2
  399. package/bizar-dash/dist/assets/useSlashCommands-DtITw8Xv.js.map +0 -1
  400. package/bizar-dash/dist/assets/vendor-CeHGtduv.js +0 -29
  401. package/bizar-dash/dist/assets/vendor-CeHGtduv.js.map +0 -1
  402. package/bizar-dash/node_modules/.package-lock.json +0 -6
  403. package/bizar-dash/package-lock.json +0 -6
  404. package/bizar-dash/src/server/cline-sdk.mjs +0 -132
  405. package/bizar-dash/src/server/routes/cline-session-detail.mjs +0 -559
  406. package/bizar-dash/src/server/routes/cline-sessions.mjs +0 -291
  407. package/cli/commands/cline-cmd.mjs +0 -289
  408. package/cli/commands/validate.test.mjs +0 -379
  409. package/cli/doctor.test.mjs +0 -350
  410. package/config/cline.json.template +0 -342
  411. package/packages/sdk/src/client.ts +0 -188
  412. package/packages/sdk/src/cline-events.ts +0 -134
  413. package/packages/sdk/src/cline-types.ts +0 -66
  414. package/packages/sdk/src/cline.ts +0 -339
  415. package/packages/sdk/src/errors.ts +0 -129
  416. package/packages/sdk/src/events.ts +0 -153
  417. package/packages/sdk/src/types.ts +0 -176
  418. package/packages/sdk/tests/client.test.ts +0 -217
  419. package/packages/sdk/tests/errors.test.ts +0 -108
  420. package/packages/sdk/tests/events.test.ts +0 -139
  421. package/packages/sdk/tests/fixtures/fetch-mock.ts +0 -152
  422. package/packages/sdk/tests/fixtures/sse-mock.ts +0 -30
  423. package/plugins/bizar/ARCHITECTURE.md +0 -142
  424. package/plugins/bizar/CONSTRAINTS.md +0 -67
  425. package/plugins/bizar/LICENSE +0 -21
  426. package/plugins/bizar/README.md +0 -448
  427. package/plugins/bizar/index.ts +0 -925
  428. package/plugins/bizar/package.json +0 -39
  429. package/plugins/bizar/scripts/check-forbidden-imports.sh +0 -33
  430. package/plugins/bizar/src/background-state.ts +0 -641
  431. package/plugins/bizar/src/background.ts +0 -1806
  432. package/plugins/bizar/src/cline-runner.ts +0 -203
  433. package/plugins/bizar/src/clineruntime.ts +0 -227
  434. package/plugins/bizar/src/commands-impl.ts +0 -151
  435. package/plugins/bizar/src/commands.ts +0 -1799
  436. package/plugins/bizar/src/compaction.d.mts +0 -48
  437. package/plugins/bizar/src/compaction.mjs +0 -192
  438. package/plugins/bizar/src/dashboard-client.ts +0 -233
  439. package/plugins/bizar/src/event-stream.ts +0 -606
  440. package/plugins/bizar/src/fingerprint.ts +0 -120
  441. package/plugins/bizar/src/handoff.ts +0 -79
  442. package/plugins/bizar/src/hooks/memory-flush-on-compact.ts +0 -123
  443. package/plugins/bizar/src/hooks/memory-inject.ts +0 -247
  444. package/plugins/bizar/src/hooks/memory-write-on-end.ts +0 -188
  445. package/plugins/bizar/src/hooks/skill-curator.ts +0 -180
  446. package/plugins/bizar/src/http-client.ts +0 -467
  447. package/plugins/bizar/src/key-rotation.ts +0 -218
  448. package/plugins/bizar/src/logger.ts +0 -144
  449. package/plugins/bizar/src/loop-engineering.ts +0 -241
  450. package/plugins/bizar/src/loop.ts +0 -176
  451. package/plugins/bizar/src/mistake-recovery.ts +0 -98
  452. package/plugins/bizar/src/odin.ts +0 -227
  453. package/plugins/bizar/src/options.ts +0 -470
  454. package/plugins/bizar/src/plan-fs.ts +0 -323
  455. package/plugins/bizar/src/reasoning-clean.ts +0 -454
  456. package/plugins/bizar/src/report.ts +0 -178
  457. package/plugins/bizar/src/research-prompt.ts +0 -35
  458. package/plugins/bizar/src/serve-info.ts +0 -228
  459. package/plugins/bizar/src/serve.ts +0 -496
  460. package/plugins/bizar/src/settings.ts +0 -349
  461. package/plugins/bizar/src/state.ts +0 -298
  462. package/plugins/bizar/src/tool-discipline.ts +0 -105
  463. package/plugins/bizar/src/tools/agent-browser.ts +0 -315
  464. package/plugins/bizar/src/tools/bg-collect.ts +0 -131
  465. package/plugins/bizar/src/tools/bg-get-comments.ts +0 -266
  466. package/plugins/bizar/src/tools/bg-kill.ts +0 -116
  467. package/plugins/bizar/src/tools/bg-pause.ts +0 -99
  468. package/plugins/bizar/src/tools/bg-report-progress.ts +0 -115
  469. package/plugins/bizar/src/tools/bg-resume.ts +0 -94
  470. package/plugins/bizar/src/tools/bg-send-message.ts +0 -223
  471. package/plugins/bizar/src/tools/bg-spawn.ts +0 -502
  472. package/plugins/bizar/src/tools/bg-status.ts +0 -130
  473. package/plugins/bizar/src/tools/graph-query.ts +0 -278
  474. package/plugins/bizar/src/tools/loop-engineering.ts +0 -193
  475. package/plugins/bizar/src/tools/memory-list.ts +0 -43
  476. package/plugins/bizar/src/tools/memory-read.ts +0 -69
  477. package/plugins/bizar/src/tools/memory-search.ts +0 -47
  478. package/plugins/bizar/src/tools/memory-write.ts +0 -54
  479. package/plugins/bizar/src/tools/open-kb.ts +0 -198
  480. package/plugins/bizar/src/tools/plan-action.ts +0 -785
  481. package/plugins/bizar/src/tools/read-glyph-feedback.ts +0 -191
  482. package/plugins/bizar/src/tools/sandbox.ts +0 -232
  483. package/plugins/bizar/src/tools/team-spawn.ts +0 -73
  484. package/plugins/bizar/src/tools/team-status.ts +0 -76
  485. package/plugins/bizar/src/tools/wait-for-feedback.ts +0 -415
  486. package/plugins/bizar/src/trajectory.ts +0 -104
  487. package/plugins/bizar/tests/README.md +0 -99
  488. package/plugins/bizar/tests/attach-handler-bug.test.ts +0 -169
  489. package/plugins/bizar/tests/background-state.test.ts +0 -277
  490. package/plugins/bizar/tests/background.test.ts +0 -402
  491. package/plugins/bizar/tests/block.test.ts +0 -195
  492. package/plugins/bizar/tests/canonical-key-order.test.ts +0 -75
  493. package/plugins/bizar/tests/clineruntime-config.test.ts +0 -283
  494. package/plugins/bizar/tests/commands-impl.test.ts +0 -316
  495. package/plugins/bizar/tests/commands.test.ts +0 -584
  496. package/plugins/bizar/tests/compaction.test.ts +0 -264
  497. package/plugins/bizar/tests/config.test.ts +0 -128
  498. package/plugins/bizar/tests/dashboard-client.test.ts +0 -159
  499. package/plugins/bizar/tests/dispose.test.ts +0 -336
  500. package/plugins/bizar/tests/event-stream.test.ts +0 -409
  501. package/plugins/bizar/tests/event.test.ts +0 -262
  502. package/plugins/bizar/tests/fingerprint.test.ts +0 -190
  503. package/plugins/bizar/tests/http-client.test.ts +0 -404
  504. package/plugins/bizar/tests/init-helpers.test.ts +0 -203
  505. package/plugins/bizar/tests/integration/slash-command.test.ts +0 -349
  506. package/plugins/bizar/tests/integration/tool-routing.test.ts +0 -98
  507. package/plugins/bizar/tests/key-rotation.test.ts +0 -396
  508. package/plugins/bizar/tests/loop-engineering.test.ts +0 -168
  509. package/plugins/bizar/tests/loop.test.ts +0 -397
  510. package/plugins/bizar/tests/memory-write-on-end.test.ts +0 -92
  511. package/plugins/bizar/tests/mistake-recovery.test.ts +0 -116
  512. package/plugins/bizar/tests/odin.test.ts +0 -125
  513. package/plugins/bizar/tests/options.test.ts +0 -329
  514. package/plugins/bizar/tests/reasoning-clean.test.ts +0 -422
  515. package/plugins/bizar/tests/safety.test.ts +0 -281
  516. package/plugins/bizar/tests/serve.test.ts +0 -339
  517. package/plugins/bizar/tests/settings.test.ts +0 -351
  518. package/plugins/bizar/tests/stall-think.test.ts +0 -750
  519. package/plugins/bizar/tests/state.test.ts +0 -276
  520. package/plugins/bizar/tests/tool-discipline.test.ts +0 -77
  521. package/plugins/bizar/tests/tools/agent-browser.test.ts +0 -98
  522. package/plugins/bizar/tests/tools/bg-collect.test.ts +0 -337
  523. package/plugins/bizar/tests/tools/bg-get-comments.test.ts +0 -485
  524. package/plugins/bizar/tests/tools/bg-kill.test.ts +0 -235
  525. package/plugins/bizar/tests/tools/bg-pause.test.ts +0 -61
  526. package/plugins/bizar/tests/tools/bg-report-progress.test.ts +0 -79
  527. package/plugins/bizar/tests/tools/bg-resume.test.ts +0 -40
  528. package/plugins/bizar/tests/tools/bg-send-message.test.ts +0 -116
  529. package/plugins/bizar/tests/tools/bg-spawn-delegation.test.ts +0 -147
  530. package/plugins/bizar/tests/tools/bg-spawn-http.test.ts +0 -233
  531. package/plugins/bizar/tests/tools/bg-spawn.test.ts +0 -311
  532. package/plugins/bizar/tests/tools/bg-status.test.ts +0 -217
  533. package/plugins/bizar/tests/tools/cline-runner.test.ts +0 -115
  534. package/plugins/bizar/tests/tools/plan-action.test.ts +0 -599
  535. package/plugins/bizar/tests/tools/read-glyph-feedback.test.ts +0 -253
  536. package/plugins/bizar/tests/tools/sandbox.test.ts +0 -117
  537. package/plugins/bizar/tests/tools/wait-for-feedback.test.ts +0 -390
  538. package/plugins/bizar/tests/update-deadlock.test.ts +0 -151
  539. package/plugins/bizar/tsconfig.json +0 -29
  540. package/scripts/mirror-agents-md.sh +0 -69
@@ -0,0 +1,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
+ }