@polderlabs/bizar 6.3.0 → 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 (245) hide show
  1. package/.claude/hooks/sessionend-recall.mjs +3 -3
  2. package/.claude/hooks/worker-suggest.mjs +110 -0
  3. package/.claude/settings.json +9 -0
  4. package/bizar-dash/dist/assets/{EnvVarsSection-B58aiJiE.js → EnvVarsSection-CW6fWKds.js} +3 -3
  5. package/bizar-dash/dist/assets/{EnvVarsSection-B58aiJiE.js.map → EnvVarsSection-CW6fWKds.js.map} +1 -1
  6. package/bizar-dash/dist/assets/MobileChat-D7zRhU9K.js +1 -0
  7. package/bizar-dash/dist/assets/{MobileChat-BJrqwVDd.js.map → MobileChat-D7zRhU9K.js.map} +1 -1
  8. package/bizar-dash/dist/assets/MobileSettings-K-sNsn_r.js +1 -0
  9. package/bizar-dash/dist/assets/{MobileSettings-CEQNJNLJ.js.map → MobileSettings-K-sNsn_r.js.map} +1 -1
  10. package/bizar-dash/dist/assets/{Toast-Cpl6-E63.js → Toast-WTxc3m2g.js} +1 -1
  11. package/bizar-dash/dist/assets/{Toast-Cpl6-E63.js.map → Toast-WTxc3m2g.js.map} +1 -1
  12. package/bizar-dash/dist/assets/flow-BpU0-gK2.js +1 -0
  13. package/bizar-dash/dist/assets/flow-BpU0-gK2.js.map +1 -0
  14. package/bizar-dash/dist/assets/fuzzy-l0sNRNKZ.js +1 -0
  15. package/bizar-dash/dist/assets/fuzzy-l0sNRNKZ.js.map +1 -0
  16. package/bizar-dash/dist/assets/{icons-Do5N2jmX.js → icons-B0AKnYI3.js} +98 -103
  17. package/bizar-dash/dist/assets/icons-B0AKnYI3.js.map +1 -0
  18. package/bizar-dash/dist/assets/main-C-Md3DaY.css +1 -0
  19. package/bizar-dash/dist/assets/main-CO8fIxQK.js +18 -0
  20. package/bizar-dash/dist/assets/main-CO8fIxQK.js.map +1 -0
  21. package/bizar-dash/dist/assets/markdown-w2bDDkAH.js +1 -0
  22. package/bizar-dash/dist/assets/{markdown-tOLaD6nm.js.map → markdown-w2bDDkAH.js.map} +1 -1
  23. package/bizar-dash/dist/assets/mobile-DXYCA0hC.js +1 -0
  24. package/bizar-dash/dist/assets/{mobile-DYCHcUpq.js.map → mobile-DXYCA0hC.js.map} +1 -1
  25. package/bizar-dash/dist/assets/mobile-layout-DSmtNPja.js +2 -0
  26. package/bizar-dash/dist/assets/mobile-layout-DSmtNPja.js.map +1 -0
  27. package/bizar-dash/dist/assets/react-vendor-BnuhLJ6X.css +1 -0
  28. package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js +46 -0
  29. package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js.map +1 -0
  30. package/bizar-dash/dist/assets/useSlashCommands-3DFLOfpk.js +2 -0
  31. package/bizar-dash/dist/assets/useSlashCommands-3DFLOfpk.js.map +1 -0
  32. package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js +45 -0
  33. package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js.map +1 -0
  34. package/bizar-dash/dist/index.html +12 -10
  35. package/bizar-dash/dist/mobile.html +7 -4
  36. package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  37. package/bizar-dash/src/server/api.mjs +11 -0
  38. package/bizar-dash/src/server/memory-consolidator.mjs +218 -0
  39. package/bizar-dash/src/server/memory-distillation.mjs +296 -0
  40. package/bizar-dash/src/server/routes/distill.mjs +129 -0
  41. package/bizar-dash/src/server/routes/goal-planner.mjs +236 -0
  42. package/bizar-dash/src/web/App.tsx +4 -0
  43. package/bizar-dash/src/web/components/Tag.tsx +18 -5
  44. package/bizar-dash/src/web/components/Topbar.tsx +3 -0
  45. package/bizar-dash/src/web/components/agents/CommunicationLog.tsx +126 -0
  46. package/bizar-dash/src/web/components/agents/DependencyGraph.tsx +163 -0
  47. package/bizar-dash/src/web/components/agents/QualityGates.tsx +123 -0
  48. package/bizar-dash/src/web/components/agents/RealTimeEventLog.tsx +127 -0
  49. package/bizar-dash/src/web/components/agents/RoutingDecisions.test.tsx +148 -0
  50. package/bizar-dash/src/web/components/agents/RoutingDecisions.tsx +160 -0
  51. package/bizar-dash/src/web/components/goals/GoalInput.tsx +86 -0
  52. package/bizar-dash/src/web/components/goals/PlanVisualization.tsx +111 -0
  53. package/bizar-dash/src/web/lib/goapPlanner.ts +614 -0
  54. package/bizar-dash/src/web/lib/types.ts +25 -0
  55. package/bizar-dash/src/web/styles/agents-redesign.css +311 -0
  56. package/bizar-dash/src/web/styles/main.css +251 -0
  57. package/bizar-dash/src/web/styles/tasks-redesign.css +231 -0
  58. package/bizar-dash/src/web/ui/controls/Button.tsx +77 -0
  59. package/bizar-dash/src/web/ui/controls/Checkbox.tsx +95 -0
  60. package/bizar-dash/src/web/ui/controls/IconButton.tsx +66 -0
  61. package/bizar-dash/src/web/ui/controls/Kbd.tsx +24 -0
  62. package/bizar-dash/src/web/ui/controls/NumberInput.tsx +77 -0
  63. package/bizar-dash/src/web/ui/controls/RadioGroup.tsx +109 -0
  64. package/bizar-dash/src/web/ui/controls/SearchInput.tsx +91 -0
  65. package/bizar-dash/src/web/ui/controls/Select.tsx +92 -0
  66. package/bizar-dash/src/web/ui/controls/Slider.tsx +70 -0
  67. package/bizar-dash/src/web/ui/controls/TextInput.tsx +67 -0
  68. package/bizar-dash/src/web/ui/controls/Toggle.tsx +79 -0
  69. package/bizar-dash/src/web/ui/controls/controls.css +454 -0
  70. package/bizar-dash/src/web/ui/controls/index.ts +33 -0
  71. package/bizar-dash/src/web/ui/data/BarChart.tsx +60 -0
  72. package/bizar-dash/src/web/ui/data/DataTable.tsx +216 -0
  73. package/bizar-dash/src/web/ui/data/EmptyState.tsx +60 -0
  74. package/bizar-dash/src/web/ui/data/ErrorState.tsx +56 -0
  75. package/bizar-dash/src/web/ui/data/KeyValueList.tsx +106 -0
  76. package/bizar-dash/src/web/ui/data/LoadingState.tsx +65 -0
  77. package/bizar-dash/src/web/ui/data/Sparkline.tsx +87 -0
  78. package/bizar-dash/src/web/ui/data/StatTile.tsx +87 -0
  79. package/bizar-dash/src/web/ui/data/data.css +535 -0
  80. package/bizar-dash/src/web/ui/data/index.ts +31 -0
  81. package/bizar-dash/src/web/ui/feedback/Badge.tsx +49 -0
  82. package/bizar-dash/src/web/ui/feedback/Dialog.tsx +209 -0
  83. package/bizar-dash/src/web/ui/feedback/ProgressBar.tsx +69 -0
  84. package/bizar-dash/src/web/ui/feedback/StatusDot.tsx +50 -0
  85. package/bizar-dash/src/web/ui/feedback/Toast.tsx +200 -0
  86. package/bizar-dash/src/web/ui/feedback/Tooltip.tsx +126 -0
  87. package/bizar-dash/src/web/ui/feedback/feedback.css +314 -0
  88. package/bizar-dash/src/web/ui/feedback/index.ts +43 -0
  89. package/bizar-dash/src/web/ui/index.ts +56 -0
  90. package/bizar-dash/src/web/ui/layout/AppShell.tsx +51 -0
  91. package/bizar-dash/src/web/ui/layout/Breadcrumbs.tsx +97 -0
  92. package/bizar-dash/src/web/ui/layout/Panel.tsx +73 -0
  93. package/bizar-dash/src/web/ui/layout/PanelHeader.tsx +39 -0
  94. package/bizar-dash/src/web/ui/layout/Sidebar.tsx +131 -0
  95. package/bizar-dash/src/web/ui/layout/Tabs.tsx +133 -0
  96. package/bizar-dash/src/web/ui/layout/Topbar.tsx +42 -0
  97. package/bizar-dash/src/web/ui/layout/index.ts +33 -0
  98. package/bizar-dash/src/web/ui/layout/layout.css +413 -0
  99. package/bizar-dash/src/web/ui/navigation/CommandPalette.tsx +199 -0
  100. package/bizar-dash/src/web/ui/navigation/NavGroup.tsx +95 -0
  101. package/bizar-dash/src/web/ui/navigation/NavLink.tsx +93 -0
  102. package/bizar-dash/src/web/ui/navigation/index.ts +17 -0
  103. package/bizar-dash/src/web/ui/navigation/navigation.css +343 -0
  104. package/bizar-dash/src/web/ui/primitives/Box.tsx +91 -0
  105. package/bizar-dash/src/web/ui/primitives/Grid.tsx +49 -0
  106. package/bizar-dash/src/web/ui/primitives/Inline.tsx +55 -0
  107. package/bizar-dash/src/web/ui/primitives/Separator.tsx +46 -0
  108. package/bizar-dash/src/web/ui/primitives/Stack.tsx +54 -0
  109. package/bizar-dash/src/web/ui/primitives/VisuallyHidden.tsx +26 -0
  110. package/bizar-dash/src/web/ui/primitives/index.ts +23 -0
  111. package/bizar-dash/src/web/ui/primitives/primitives.css +183 -0
  112. package/bizar-dash/src/web/ui/styles/globals.css +86 -0
  113. package/bizar-dash/src/web/ui/styles/reset.css +125 -0
  114. package/bizar-dash/src/web/ui/styles/tokens.css +195 -0
  115. package/bizar-dash/src/web/ui/theme/ThemeProvider.tsx +142 -0
  116. package/bizar-dash/src/web/ui/theme/tokens.ts +131 -0
  117. package/bizar-dash/src/web/ui/theme/useTheme.ts +22 -0
  118. package/bizar-dash/src/web/ui/utils/cx.ts +42 -0
  119. package/bizar-dash/src/web/views/Agents.tsx +506 -266
  120. package/bizar-dash/src/web/views/GoalPlanner.tsx +158 -0
  121. package/bizar-dash/src/web/views/Overview.tsx +996 -448
  122. package/bizar-dash/src/web/views/Tasks.tsx +277 -273
  123. package/bizar-dash/tests/communication-log.test.tsx +67 -0
  124. package/bizar-dash/tests/dep-graph.test.tsx +40 -0
  125. package/bizar-dash/tests/event-log.test.tsx +61 -0
  126. package/bizar-dash/tests/goal-input.test.tsx +72 -0
  127. package/bizar-dash/tests/goap-planner.test.ts +68 -0
  128. package/bizar-dash/tests/quality-gates.test.tsx +59 -0
  129. package/bizar-dash/tests/setup.ts +31 -1
  130. package/bizar-dash/tests/ui/controls/Button.test.tsx +50 -0
  131. package/bizar-dash/tests/ui/controls/Checkbox.test.tsx +44 -0
  132. package/bizar-dash/tests/ui/controls/IconButton.test.tsx +37 -0
  133. package/bizar-dash/tests/ui/controls/Kbd.test.tsx +39 -0
  134. package/bizar-dash/tests/ui/controls/NumberInput.test.tsx +56 -0
  135. package/bizar-dash/tests/ui/controls/RadioGroup.test.tsx +58 -0
  136. package/bizar-dash/tests/ui/controls/SearchInput.test.tsx +52 -0
  137. package/bizar-dash/tests/ui/controls/Select.test.tsx +66 -0
  138. package/bizar-dash/tests/ui/controls/Slider.test.tsx +41 -0
  139. package/bizar-dash/tests/ui/controls/TextInput.test.tsx +44 -0
  140. package/bizar-dash/tests/ui/controls/Toggle.test.tsx +39 -0
  141. package/bizar-dash/tests/ui/data/BarChart.test.tsx +55 -0
  142. package/bizar-dash/tests/ui/data/DataTable.test.tsx +112 -0
  143. package/bizar-dash/tests/ui/data/EmptyState.test.tsx +57 -0
  144. package/bizar-dash/tests/ui/data/ErrorState.test.tsx +47 -0
  145. package/bizar-dash/tests/ui/data/KeyValueList.test.tsx +59 -0
  146. package/bizar-dash/tests/ui/data/LoadingState.test.tsx +37 -0
  147. package/bizar-dash/tests/ui/data/Sparkline.test.tsx +56 -0
  148. package/bizar-dash/tests/ui/data/StatTile.test.tsx +69 -0
  149. package/bizar-dash/tests/ui/feedback/Badge.test.tsx +41 -0
  150. package/bizar-dash/tests/ui/feedback/Dialog.test.tsx +99 -0
  151. package/bizar-dash/tests/ui/feedback/ProgressBar.test.tsx +66 -0
  152. package/bizar-dash/tests/ui/feedback/StatusDot.test.tsx +41 -0
  153. package/bizar-dash/tests/ui/feedback/Toast.test.tsx +124 -0
  154. package/bizar-dash/tests/ui/feedback/Tooltip.test.tsx +102 -0
  155. package/bizar-dash/tests/ui/layout/AppShell.test.tsx +50 -0
  156. package/bizar-dash/tests/ui/layout/Breadcrumbs.test.tsx +85 -0
  157. package/bizar-dash/tests/ui/layout/Panel.test.tsx +52 -0
  158. package/bizar-dash/tests/ui/layout/PanelHeader.test.tsx +32 -0
  159. package/bizar-dash/tests/ui/layout/Sidebar.test.tsx +95 -0
  160. package/bizar-dash/tests/ui/layout/Tabs.test.tsx +87 -0
  161. package/bizar-dash/tests/ui/layout/Topbar.test.tsx +41 -0
  162. package/bizar-dash/tests/ui/navigation/CommandPalette.test.tsx +105 -0
  163. package/bizar-dash/tests/ui/navigation/NavGroup.test.tsx +55 -0
  164. package/bizar-dash/tests/ui/navigation/NavLink.test.tsx +61 -0
  165. package/bizar-dash/tests/ui/primitives/Box.test.tsx +49 -0
  166. package/bizar-dash/tests/ui/primitives/Grid.test.tsx +37 -0
  167. package/bizar-dash/tests/ui/primitives/Inline.test.tsx +33 -0
  168. package/bizar-dash/tests/ui/primitives/Separator.test.tsx +34 -0
  169. package/bizar-dash/tests/ui/primitives/Stack.test.tsx +45 -0
  170. package/bizar-dash/tests/ui/primitives/VisuallyHidden.test.tsx +27 -0
  171. package/bizar-dash/tests/views/Agents.test.tsx +359 -0
  172. package/bizar-dash/tests/views/Overview.test.tsx +413 -0
  173. package/bizar-dash/tests/views/Tasks.test.tsx +250 -0
  174. package/bizar-dash/vitest.config.ts +3 -1
  175. package/cli/__tests__/cost-gate.test.mjs +301 -0
  176. package/cli/__tests__/feature-list-bridge.test.mjs +371 -0
  177. package/cli/bin.mjs +38 -0
  178. package/cli/commands/claim.mjs +282 -0
  179. package/cli/commands/cost.mjs +279 -0
  180. package/cli/cost-gate.mjs +501 -0
  181. package/cli/feature-list-bridge.mjs +400 -0
  182. package/cli/memory-constants.mjs +17 -0
  183. package/cli/worker-dispatcher.mjs +259 -0
  184. package/cli/worker-dispatcher.test.mjs +204 -0
  185. package/config/trigger-patterns.json +186 -0
  186. package/package.json +2 -2
  187. package/packages/sdk/package.json +1 -1
  188. package/packages/sdk/src/agent-registry.ts +304 -0
  189. package/packages/sdk/src/consensus/byzantine.ts +587 -0
  190. package/packages/sdk/src/consensus/index.ts +147 -0
  191. package/packages/sdk/src/consensus/queen.ts +173 -0
  192. package/packages/sdk/src/consensus/types.ts +199 -0
  193. package/packages/sdk/src/federation/audit.ts +156 -0
  194. package/packages/sdk/src/federation/budget.ts +269 -0
  195. package/packages/sdk/src/federation/envelope.ts +144 -0
  196. package/packages/sdk/src/federation/hmac.ts +160 -0
  197. package/packages/sdk/src/federation/index.ts +443 -0
  198. package/packages/sdk/src/federation/pii.ts +241 -0
  199. package/packages/sdk/src/federation/policy.ts +112 -0
  200. package/packages/sdk/src/federation/trust.ts +146 -0
  201. package/packages/sdk/src/index.ts +67 -0
  202. package/packages/sdk/src/mcp/server.ts +351 -1
  203. package/packages/sdk/src/router/codemod-intent.ts +125 -0
  204. package/packages/sdk/src/router/index.ts +247 -0
  205. package/packages/sdk/src/router/memory-distillation-shim.mjs +18 -0
  206. package/packages/sdk/src/router/memory-distillation.ts +249 -0
  207. package/packages/sdk/src/router/model-router.ts +235 -0
  208. package/packages/sdk/src/router/q-learning-router.ts +304 -0
  209. package/packages/sdk/src/swarm-topology.ts +279 -0
  210. package/packages/sdk/tests/agent-registry.test.ts +285 -0
  211. package/packages/sdk/tests/consensus.test.ts +376 -0
  212. package/packages/sdk/tests/federation/audit.test.ts +164 -0
  213. package/packages/sdk/tests/federation/budget.test.ts +189 -0
  214. package/packages/sdk/tests/federation/envelope.test.ts +120 -0
  215. package/packages/sdk/tests/federation/hmac.test.ts +194 -0
  216. package/packages/sdk/tests/federation/orchestrator.test.ts +176 -0
  217. package/packages/sdk/tests/federation/pii.test.ts +106 -0
  218. package/packages/sdk/tests/federation/policy.test.ts +140 -0
  219. package/packages/sdk/tests/federation/trust.test.ts +124 -0
  220. package/packages/sdk/tests/mcp-tools.test.ts +204 -0
  221. package/packages/sdk/tests/memory-distillation.test.mjs +186 -0
  222. package/packages/sdk/tests/model-router.test.mjs +108 -0
  223. package/packages/sdk/tests/q-learning-router.test.mjs +100 -0
  224. package/packages/sdk/tests/router-orchestrator.test.mjs +172 -0
  225. package/packages/sdk/tests/router.test.mjs +91 -0
  226. package/packages/sdk/tests/sdk.test.mjs +26 -2
  227. package/packages/sdk/tests/swarm-topology.test.ts +258 -0
  228. package/scripts/bh-full-e2e.mjs +11 -15
  229. package/bizar-dash/dist/assets/MobileChat-BJrqwVDd.js +0 -1
  230. package/bizar-dash/dist/assets/MobileSettings-CEQNJNLJ.js +0 -1
  231. package/bizar-dash/dist/assets/icons-Do5N2jmX.js.map +0 -1
  232. package/bizar-dash/dist/assets/main-IvfQAOfy.js +0 -18
  233. package/bizar-dash/dist/assets/main-IvfQAOfy.js.map +0 -1
  234. package/bizar-dash/dist/assets/main-ietCEg_R.css +0 -1
  235. package/bizar-dash/dist/assets/markdown-tOLaD6nm.js +0 -1
  236. package/bizar-dash/dist/assets/mobile-DYCHcUpq.js +0 -1
  237. package/bizar-dash/dist/assets/mobile-layout-CBHjpwsb.js +0 -2
  238. package/bizar-dash/dist/assets/mobile-layout-CBHjpwsb.js.map +0 -1
  239. package/bizar-dash/dist/assets/react-vendor-Dn4wqh4Z.js +0 -40
  240. package/bizar-dash/dist/assets/react-vendor-Dn4wqh4Z.js.map +0 -1
  241. package/bizar-dash/dist/assets/useSlashCommands-Bd7_FA6U.js +0 -2
  242. package/bizar-dash/dist/assets/useSlashCommands-Bd7_FA6U.js.map +0 -1
  243. package/bizar-dash/dist/assets/vendor-C843201K.js +0 -29
  244. package/bizar-dash/dist/assets/vendor-C843201K.js.map +0 -1
  245. package/packages/sdk/package-lock.json +0 -32
@@ -23,7 +23,7 @@
23
23
  * allowedTools: ["mcp__bizar__*"],
24
24
  * },
25
25
  * })) {
26
- * console.log(msg);
26
+ * handleAgentMessage(msg);
27
27
  * }
28
28
  *
29
29
  * The MCP server can also be registered in `~/.claude/settings.json`
@@ -47,6 +47,22 @@ import {
47
47
  resolveVaultRoot,
48
48
  } from "../memory/index.js";
49
49
  import { checkDangerous } from "../dangerous-patterns.js";
50
+ import { ModelRouter } from "../router/model-router.js";
51
+ import { QLearningRouter } from "../router/q-learning-router.js";
52
+ import { runDistillation } from "../router/memory-distillation.js";
53
+ import { decideAgentWith } from "../router/index.js";
54
+ import { bizarAgentRegistry } from "../agent-registry.js";
55
+ import {
56
+ initSwarm as initSwarmOnRegistry,
57
+ DEFAULT_TOPOLOGY,
58
+ DEFAULT_MAX_AGENTS,
59
+ type SwarmTopology,
60
+ } from "../swarm-topology.js";
61
+ import { getSharedConsensus } from "../consensus/index.js";
62
+ import {
63
+ createFederation,
64
+ type FederationHandle,
65
+ } from "../federation/index.js";
50
66
 
51
67
  // We don't import from @anthropic-ai/claude-agent-sdk as a hard dep —
52
68
  // the package is optional. Callers pass the result of `tool()` and
@@ -372,6 +388,117 @@ const graphPathTool = defineTool<{ from: string; to: string }>(
372
388
  { readOnlyHint: true },
373
389
  );
374
390
 
391
+ // ---------------------------------------------------------------------------
392
+ // Swarm / agent lifecycle tools (F-032)
393
+ // ---------------------------------------------------------------------------
394
+
395
+ const agentSpawnTool = defineTool<{
396
+ type: string;
397
+ name?: string;
398
+ priority?: "low" | "normal" | "high" | "critical";
399
+ metadata?: string;
400
+ }>(
401
+ "agent_spawn",
402
+ "Spawn a new agent in the in-process BizarAgentRegistry. Returns the new agentId. Heartbeat is a stub — this registry is a coordination surface, not an agent runtime.",
403
+ { type: "string", name: "string", priority: "string", metadata: "string" },
404
+ async ({ type, name, priority, metadata }) => {
405
+ try {
406
+ let parsedMetadata: Record<string, unknown> | undefined;
407
+ if (metadata && typeof metadata === "string" && metadata.trim().length > 0) {
408
+ try { parsedMetadata = JSON.parse(metadata) as Record<string, unknown>; }
409
+ catch { return err("agent_spawn: `metadata` must be a JSON string"); }
410
+ }
411
+ const result = bizarAgentRegistry.registerAgent({
412
+ type,
413
+ name,
414
+ priority,
415
+ metadata: parsedMetadata,
416
+ });
417
+ return ok(JSON.stringify(result, null, 2));
418
+ } catch (e) { return err(String(e)); }
419
+ },
420
+ );
421
+
422
+ const agentListTool = defineTool<{
423
+ status?: string;
424
+ type?: string;
425
+ limit?: number;
426
+ offset?: number;
427
+ }>(
428
+ "agent_list",
429
+ "List agents tracked by the in-process BizarAgentRegistry. Filter by status (active|terminated|all) and/or type. Supports limit/offset pagination.",
430
+ { status: "string", type: "string", limit: "number", offset: "number" },
431
+ async ({ status, type, limit, offset }) => {
432
+ try {
433
+ const allowedStatuses = ["active", "terminated", "all"] as const;
434
+ type StatusFilter = typeof allowedStatuses[number];
435
+ const s: StatusFilter = (allowedStatuses as readonly string[]).includes(status ?? "")
436
+ ? (status as StatusFilter)
437
+ : "all";
438
+ const result = bizarAgentRegistry.listAgents({
439
+ status: s,
440
+ type,
441
+ limit: typeof limit === "number" ? limit : undefined,
442
+ offset: typeof offset === "number" ? offset : undefined,
443
+ });
444
+ return ok(JSON.stringify(result, null, 2));
445
+ } catch (e) { return err(String(e)); }
446
+ },
447
+ { readOnlyHint: true },
448
+ );
449
+
450
+ const agentTerminateTool = defineTool<{
451
+ agentId: string;
452
+ graceful?: boolean;
453
+ reason?: string;
454
+ }>(
455
+ "agent_terminate",
456
+ "Terminate a registered agent. Returns the agentId, status, and terminatedAt timestamp. No-op if the agent is already terminated.",
457
+ { agentId: "string", graceful: "boolean", reason: "string" },
458
+ async ({ agentId, graceful, reason }) => {
459
+ try {
460
+ const result = bizarAgentRegistry.terminateAgent({
461
+ agentId,
462
+ reason,
463
+ graceful: graceful ?? true,
464
+ });
465
+ return ok(JSON.stringify(result, null, 2));
466
+ } catch (e) { return err(String(e)); }
467
+ },
468
+ );
469
+
470
+ const swarmInitTool = defineTool<{
471
+ topology?: string;
472
+ maxAgents?: number;
473
+ metadata?: string;
474
+ }>(
475
+ "swarm_init",
476
+ "Initialize a new swarm in the SwarmTopologyRegistry. topology ∈ hierarchical|mesh|adaptive|collective|hierarchical-mesh (default 'hierarchical-mesh'); maxAgents clamped to [1,1000] (default 15). The 'default' swarm is created lazily on first call.",
477
+ { topology: "string", maxAgents: "number", metadata: "string" },
478
+ async ({ topology, maxAgents, metadata }) => {
479
+ try {
480
+ let parsedMetadata: Record<string, unknown> | undefined;
481
+ if (metadata && typeof metadata === "string" && metadata.trim().length > 0) {
482
+ try { parsedMetadata = JSON.parse(metadata) as Record<string, unknown>; }
483
+ catch { return err("swarm_init: `metadata` must be a JSON string"); }
484
+ }
485
+ const allowedTopologies: readonly SwarmTopology[] = [
486
+ "hierarchical", "mesh", "adaptive", "collective", "hierarchical-mesh",
487
+ ];
488
+ const topo: SwarmTopology = (allowedTopologies as readonly string[]).includes(topology ?? "")
489
+ ? (topology as SwarmTopology)
490
+ : DEFAULT_TOPOLOGY;
491
+ const max = typeof maxAgents === "number" ? maxAgents : DEFAULT_MAX_AGENTS;
492
+ const result = initSwarmOnRegistry({
493
+ topology: topo,
494
+ maxAgents: max,
495
+ metadata: parsedMetadata,
496
+ });
497
+ return ok(JSON.stringify(result, null, 2));
498
+ } catch (e) { return err(String(e)); }
499
+ },
500
+ );
501
+
375
502
  // ---------------------------------------------------------------------------
376
503
  // Safety tool — exposed so any model can self-audit a Bash command
377
504
  // before running it.
@@ -390,6 +517,219 @@ const dangerCheckTool = defineTool<{ command: string }>(
390
517
  { readOnlyHint: true },
391
518
  );
392
519
 
520
+ // ---------------------------------------------------------------------------
521
+ // Self-learning tools (F-033 / ADR-174) — Thompson-sampling model
522
+ // router + Q-learning agent router + memory distillation trigger.
523
+ // ---------------------------------------------------------------------------
524
+
525
+ /** Singleton model router — the priors accumulate across tool calls
526
+ * within one MCP server instance, so the bandit can learn from
527
+ * outcomes in real time. Tests construct their own router via the
528
+ * exported `BIZAR_TOOLS` indirection. */
529
+ const modelRouter = new ModelRouter();
530
+
531
+ /** Singleton Q-learning router — same reasoning as above. */
532
+ const agentRouter = new QLearningRouter();
533
+
534
+ const modelRouteTool = defineTool<{ prompt: string }>(
535
+ "model_route",
536
+ "Adaptive model-tier routing. Returns the recommended tier ('flash'|'mid'|'expensive') for a prompt using a Thompson-sampling bandit over Beta(α,β) priors. Codemod-eligible prompts (var-to-const, remove-console, add-logging) short-circuit to 'flash' with codemodIntent set.",
537
+ { prompt: "string" },
538
+ async ({ prompt }) => {
539
+ try {
540
+ const decision = modelRouter.route(prompt);
541
+ return ok(JSON.stringify(decision, null, 2));
542
+ } catch (e) { return err(String(e)); }
543
+ },
544
+ { readOnlyHint: true },
545
+ );
546
+
547
+ const agentRouteTool = defineTool<{ task: string }>(
548
+ "agent_route",
549
+ "Adaptive agent routing. Returns the recommended Bizar agent (odin|frigg|vor|mimir|heimdall|thor|tyr|forseti) for a task using Q-learning over a 64-dim bag-of-words feature hash. Codemod-eligible tasks route to heimdall (the routine-implementation agent).",
550
+ { task: "string" },
551
+ async ({ task }) => {
552
+ try {
553
+ const decision = agentRouter.selectAgent(task);
554
+ return ok(JSON.stringify(decision, null, 2));
555
+ } catch (e) { return err(String(e)); }
556
+ },
557
+ { readOnlyHint: true },
558
+ );
559
+
560
+ const memoryDistillTool = defineTool<{ since?: string }>(
561
+ "memory_distill",
562
+ "Run the ReasoningBank distillation pipeline (ADR-174: RETRIEVE → JUDGE → DISTILL → CONSOLIDATE) over the in-process memory vault. Optionally filter entries by createdAt >= since. Returns {distilled, promoted, byTier} — promoted patterns are only emitted for oracle:test-exec or judge:fable tiers.",
563
+ { since: "string" },
564
+ async ({ since }) => {
565
+ try {
566
+ // The dashboard owns the consolidator module; the SDK tool
567
+ // shim reads directly from the vault and runs the same
568
+ // RETRIEVE → JUDGE → DISTILL → CONSOLIDATE pipeline inline,
569
+ // so the tool works without the dashboard process.
570
+ const root = resolveVaultRoot();
571
+ const all = listNotes(root, "", 10000);
572
+ const cutoff = since ? new Date(since) : null;
573
+ const entries = all
574
+ .filter((n) => !cutoff || !Number.isNaN(cutoff.getTime()) && new Date(
575
+ String(n.frontmatter?.createdAt ?? n.frontmatter?.created ?? 0),
576
+ ) >= cutoff)
577
+ .map((n) => ({
578
+ memory_id: n.relPath,
579
+ relPath: n.relPath,
580
+ frontmatter: n.frontmatter ?? {},
581
+ body: n.body ?? "",
582
+ kind: typeof (n.frontmatter?.kind ?? n.frontmatter?.type) === "string"
583
+ ? (n.frontmatter?.kind ?? n.frontmatter?.type) as string
584
+ : undefined,
585
+ created: typeof (n.frontmatter?.createdAt ?? n.frontmatter?.created) === "string"
586
+ ? (n.frontmatter?.createdAt ?? n.frontmatter?.created) as string
587
+ : undefined,
588
+ }));
589
+
590
+ // Static import — the SDK owns `memory-distillation.ts` so the
591
+ // tool can run the RETRIEVE → JUDGE → DISTILL → CONSOLIDATE
592
+ // pipeline without depending on the dashboard process.
593
+ const result = runDistillation(entries);
594
+ return ok(JSON.stringify({
595
+ distilled: result.patterns.length,
596
+ promoted: result.promoted.map((p: { id: string }) => p.id),
597
+ byTier: result.byTier,
598
+ }, null, 2));
599
+ } catch (e) { return err(String(e)); }
600
+ },
601
+ );
602
+
603
+ // ---------------------------------------------------------------------------
604
+ // Self-learning orchestrator tool (F-033)
605
+ //
606
+ // Replaces the prompt-time heuristics in the Odin agent with a single
607
+ // tool call. Combines the codemod tier-1 short-circuit, the
608
+ // Q-learning agent pick, and the Thompson-bandit model-tier pick
609
+ // behind one MCP `hooks_route` tool (matches ruflo
610
+ // `v3/mcp/tools/hooks-tools.ts:1207`).
611
+ // ---------------------------------------------------------------------------
612
+
613
+ const hooksRouteTool = defineTool<{ task: string; explicitAgent?: string }>(
614
+ "hooks_route",
615
+ "Self-Learning router (F-033): decide which agent + model tier should handle a task. Returns {agent, modelTier, agentConfidence, modelConfidence, codemodIntent, surfacedTags}. surfacedTags contains the human-readable markers (e.g. [CODEMOD_AVAILABLE] / [TASK_MODEL_RECOMMENDATION]) that should be prepended to the prompt for downstream observability.",
616
+ { task: "string", explicitAgent: "string" },
617
+ async ({ task, explicitAgent }) => {
618
+ try {
619
+ const decision = decideAgentWith(modelRouter, agentRouter, {
620
+ task: String(task ?? ""),
621
+ explicitAgent: explicitAgent ? String(explicitAgent) : undefined,
622
+ });
623
+ return ok(JSON.stringify(decision, null, 2));
624
+ } catch (e) { return err(String(e)); }
625
+ },
626
+ { readOnlyHint: true },
627
+ );
628
+
629
+ // ---------------------------------------------------------------------------
630
+ // Consensus tool (F-039) — thin PBFT-style 3-of-5 majority for
631
+ // review/decision steps. Wraps the shared `getSharedConsensus()`
632
+ // orchestrator; the MCP surface is intentionally minimal (one tool)
633
+ // because the consensus layer is a coordination primitive — model
634
+ // callers propose payloads, the cluster of 5 agents (odin / frigg /
635
+ // vor / mimir / heimdall) reaches quorum, and the tool returns the
636
+ // proposalId + status. Vote tally and view-change are internal — the
637
+ // MCP caller drives the lifecycle through the orchestrator's
638
+ // `castVote` / `viewChange` if it wants finer control.
639
+ //
640
+ // The payload is a JSON string so callers don't need a zod schema for
641
+ // every consensus call; the orchestrator's replay-protection hash
642
+ // keys on the canonicalized JSON anyway, so semantically equivalent
643
+ // payloads collapse to the same proposalId.
644
+ // ---------------------------------------------------------------------------
645
+
646
+ const consensusProposeTool = defineTool<{
647
+ payload: string;
648
+ quorum?: number;
649
+ vote?: string;
650
+ agentId?: string;
651
+ }>(
652
+ "consensus_propose",
653
+ "PBFT-style 3-of-5 majority consensus for Bizar review/decision steps. payload is a JSON-encoded string (canonicalized via JSON.stringify). vote ∈ yes|no|abstain (defaults to 'yes' from the local agent). agentId is the peer whose vote this call represents (defaults to the local agent = 'odin'). Returns { proposalId, status, phase, approvals, rejections }. Re-submitting the same payload in the same view returns the cached proposalId (replay protection).",
654
+ { payload: "string", quorum: "number", vote: "string", agentId: "string" },
655
+ async ({ payload, quorum, vote, agentId }) => {
656
+ try {
657
+ if (typeof payload !== "string" || payload.trim().length === 0) {
658
+ return err("consensus_propose: `payload` must be a non-empty JSON string");
659
+ }
660
+ let parsedPayload: unknown;
661
+ try {
662
+ parsedPayload = JSON.parse(payload);
663
+ } catch (e) {
664
+ return err(`consensus_propose: \`payload\` is not valid JSON: ${String(e)}`);
665
+ }
666
+ const consensus = getSharedConsensus();
667
+ const proposeResult = consensus.propose(parsedPayload);
668
+ const v: "yes" | "no" | "abstain" =
669
+ vote === "no" || vote === "abstain" ? vote : "yes";
670
+ const voter = agentId && agentId.trim().length > 0 ? agentId.trim() : consensus.localAgentId;
671
+ const voteResult = consensus.castVote(
672
+ proposeResult.proposalId,
673
+ voter,
674
+ v,
675
+ );
676
+ return ok(JSON.stringify({
677
+ proposalId: voteResult.proposalId,
678
+ status: voteResult.status,
679
+ phase: voteResult.phase,
680
+ approvals: voteResult.approvals,
681
+ rejections: voteResult.rejections,
682
+ abstentions: voteResult.abstentions,
683
+ committed: voteResult.committed,
684
+ quorum: quorum ?? consensus.getQuorum(),
685
+ currentProposer: consensus.getCurrentProposer(),
686
+ viewNumber: consensus.getViewNumber(),
687
+ }, null, 2));
688
+ } catch (e) { return err(String(e)); }
689
+ },
690
+ );
691
+
692
+ // ---------------------------------------------------------------------------
693
+ // Federation tool (F-038) — Cross-installation agent federation
694
+ // skeleton. Reads status from the shared `createFederation()` handle
695
+ // (one per MCP server instance). The handle is configured against
696
+ // `.harness/federation-audit.log` + `.harness/federation-budget.json`
697
+ // under the project root. Per-call signing/receiving is not exposed
698
+ // as a tool here — it's a library API used by the dashboard / future
699
+ // transport layer; the MCP surface is intentionally a status probe
700
+ // so callers can check peer trust + audit size + budget headroom
701
+ // without needing to import the SDK.
702
+ // ---------------------------------------------------------------------------
703
+
704
+ function resolveFederationHandle(): FederationHandle {
705
+ const root = findRepoRoot();
706
+ const nodeId = `${root.replace(/[^a-zA-Z0-9_-]+/g, "_")}-mcp`;
707
+ // Shared secret per repo — the F-038 skeleton is in-process, so a
708
+ // hard-coded secret is fine for the status probe. Real deployments
709
+ // would source this from the env (BIZAR_FEDERATION_SECRET).
710
+ const secret = process.env.BIZAR_FEDERATION_SECRET ?? `bizar-federation-${root}`;
711
+ return createFederation({
712
+ nodeId,
713
+ secret,
714
+ auditPath: join(root, ".harness", "federation-audit.log"),
715
+ budgetPath: join(root, ".harness", "federation-budget.json"),
716
+ });
717
+ }
718
+
719
+ const federationStatusTool = defineTool<Record<string, never>>(
720
+ "federation_status",
721
+ "Federation skeleton status probe (F-038). Returns { nodeId, peers, nonceCacheSize, auditSizeBytes, auditPath, budget: { path, perPeer, outstandingReservations } }. Reads from the local createFederation() handle configured against .harness/federation-audit.log + .harness/federation-budget.json. Read-only.",
722
+ {},
723
+ async () => {
724
+ try {
725
+ const handle = resolveFederationHandle();
726
+ const snap = handle.status();
727
+ return ok(JSON.stringify(snap, null, 2));
728
+ } catch (e) { return err(String(e)); }
729
+ },
730
+ { readOnlyHint: true },
731
+ );
732
+
393
733
  // ---------------------------------------------------------------------------
394
734
  // Factory — wire all tools into an MCP server
395
735
  // ---------------------------------------------------------------------------
@@ -408,6 +748,16 @@ export const BIZAR_TOOLS: SdkMcpToolDef[] = [
408
748
  graphQueryTool,
409
749
  graphPathTool,
410
750
  dangerCheckTool,
751
+ agentSpawnTool,
752
+ agentListTool,
753
+ agentTerminateTool,
754
+ swarmInitTool,
755
+ modelRouteTool,
756
+ agentRouteTool,
757
+ memoryDistillTool,
758
+ hooksRouteTool,
759
+ consensusProposeTool,
760
+ federationStatusTool,
411
761
  ];
412
762
 
413
763
  /**
@@ -0,0 +1,125 @@
1
+ /**
2
+ * router/codemod-intent.ts — Tier-1 codemod intent detection.
3
+ *
4
+ * v6.4.0 — ported from ruflo `enhanced-model-router.ts` (ADR-026, ADR-143).
5
+ *
6
+ * Three deterministic intents are eligible for $0 Tier-1 codemod
7
+ * execution — they can be applied structurally, no LLM in the loop:
8
+ *
9
+ * - `var-to-const` — convert `var` declarations to `const` / `let`.
10
+ * - `remove-console` — strip `console.*` calls.
11
+ * - `add-logging` — add `console.*` / logger statements.
12
+ *
13
+ * Other intents (`add-types`, `add-error-handling`, `async-await`, …)
14
+ * REQUIRE judgement and route to a model tier (Tier-2/3); they are
15
+ * explicitly OUT OF SCOPE for this detector. See ADR-143.
16
+ *
17
+ * Pure function — no I/O. Confidence is the match strength
18
+ * (number of distinct regex patterns that hit, normalised by pattern
19
+ * count; capped at 1.0). Returning `null` means the prompt is NOT
20
+ * codemod-eligible and the caller should consult the bandit / neural
21
+ * router.
22
+ */
23
+
24
+ export type CodemodIntent = "var-to-const" | "remove-console" | "add-logging";
25
+
26
+ export interface CodemodIntentHit {
27
+ intent: CodemodIntent;
28
+ confidence: number;
29
+ }
30
+
31
+ const PATTERNS: Record<CodemodIntent, RegExp[]> = {
32
+ "var-to-const": [
33
+ /convert\s+var\s+to\s+const/i,
34
+ /convert\s+var\s+declarations?\s+to\s+const/i,
35
+ /change\s+var\s+to\s+const/i,
36
+ /change\s+var\s+declarations?\s+to\s+const/i,
37
+ /replace\s+var\s+with\s+const/i,
38
+ /var\s*(?:→|->|to)\s*const/i,
39
+ /use\s+const\s+instead\s+of\s+var/i,
40
+ /\bvar\s+to\s+const\b/i,
41
+ ],
42
+ "remove-console": [
43
+ /remove\s+(?:all\s+)?console\.log/i,
44
+ /remove\s+(?:all\s+)?console\s+statements?/i,
45
+ /delete\s+(?:all\s+)?console\s+statements?/i,
46
+ /strip\s+console/i,
47
+ /clean\s+up\s+console/i,
48
+ /clean\s+up\s+debug\s+logs?/i,
49
+ /remove\s+(?:all\s+)?debug\s+logs?/i,
50
+ /delete\s+(?:all\s+)?console\.log/i,
51
+ /strip\s+(?:all\s+)?console/i,
52
+ ],
53
+ "add-logging": [
54
+ /\badd\s+logging\b/i,
55
+ /\badd\s+console\.log\b/i,
56
+ /\badd\s+debug\s+logs?\b/i,
57
+ /\blog\s+this\s+function\b/i,
58
+ /\badd\s+trace\s+logging\b/i,
59
+ /\binstrument\s+with\s+logs?\b/i,
60
+ ],
61
+ };
62
+
63
+ /**
64
+ * Default confidence threshold for `detectCodemodIntent`. The function
65
+ * returns `null` for pattern-set hits with confidence below this.
66
+ *
67
+ * Kept intentionally low (0.1) so a single canonical phrase on a
68
+ * 7-pattern intent — natural prompts like "convert var to const" —
69
+ * still short-circuits the bandit. Callers that want stricter
70
+ * filtering (the F-033 orchestrator `decideAgentWith`, see ADR-174)
71
+ * can pass `threshold` explicitly.
72
+ *
73
+ * Strong threshold the spec mentions ("Return null if confidence < 0.6")
74
+ * is exported as `STRICT_CODEMOD_CONFIDENCE_THRESHOLD` below.
75
+ */
76
+ export const DEFAULT_CODEMOD_CONFIDENCE_THRESHOLD = 0.1;
77
+ export const STRICT_CODEMOD_CONFIDENCE_THRESHOLD = 0.6;
78
+ /** Alias used in the spec — kept for backwards compat. */
79
+ export const CODEMOD_CONFIDENCE_THRESHOLD = DEFAULT_CODEMOD_CONFIDENCE_THRESHOLD;
80
+
81
+ export interface CodemodDetectionOptions {
82
+ /** Override the threshold. Default:
83
+ * `DEFAULT_CODEMOD_CONFIDENCE_THRESHOLD` (=0.1). */
84
+ threshold?: number;
85
+ }
86
+
87
+ /**
88
+ * Probe whether a prompt matches one of the deterministic codemod
89
+ * intents. Order matters only for tie-breaking: we return the FIRST
90
+ * intent whose overall score is highest in case of a multi-intent
91
+ * tie.
92
+ *
93
+ * Confidence is `hits / patterns.length`. A single regex hit on a
94
+ * intent with 7 patterns yields confidence 1/7 ≈ 0.14 — enough to
95
+ * flag the intent but well below 1.0 so the caller can decide
96
+ * whether to escalate. Two hits on a smaller intent (say 4
97
+ * patterns) yields 0.5 — past the 0.4 bandit-confirmation
98
+ * threshold.
99
+ *
100
+ * Result filtered by `opts.threshold` (default 0.1). When the
101
+ * strongest pattern-set hit is below the threshold the function
102
+ * returns `null` so the caller falls through to Q-learning / bandit.
103
+ */
104
+ export function detectCodemodIntent(
105
+ prompt: string,
106
+ opts: CodemodDetectionOptions = {},
107
+ ): CodemodIntentHit | null {
108
+ if (!prompt || typeof prompt !== "string") return null;
109
+
110
+ let best: { intent: CodemodIntent; confidence: number } | null = null;
111
+ for (const intent of Object.keys(PATTERNS) as CodemodIntent[]) {
112
+ const patterns = PATTERNS[intent];
113
+ let hits = 0;
114
+ for (const p of patterns) if (p.test(prompt)) hits += 1;
115
+ if (hits === 0) continue;
116
+ const confidence = hits / patterns.length;
117
+ if (best === null || confidence > best.confidence) {
118
+ best = { intent, confidence };
119
+ }
120
+ }
121
+ if (best === null) return null;
122
+ const threshold = opts.threshold ?? DEFAULT_CODEMOD_CONFIDENCE_THRESHOLD;
123
+ if (best.confidence < threshold) return null;
124
+ return best;
125
+ }