@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.
- package/.claude/hooks/sessionend-recall.mjs +3 -3
- package/.claude/hooks/worker-suggest.mjs +110 -0
- package/.claude/settings.json +9 -0
- package/bizar-dash/dist/assets/{EnvVarsSection-B58aiJiE.js → EnvVarsSection-CW6fWKds.js} +3 -3
- package/bizar-dash/dist/assets/{EnvVarsSection-B58aiJiE.js.map → EnvVarsSection-CW6fWKds.js.map} +1 -1
- package/bizar-dash/dist/assets/MobileChat-D7zRhU9K.js +1 -0
- package/bizar-dash/dist/assets/{MobileChat-BJrqwVDd.js.map → MobileChat-D7zRhU9K.js.map} +1 -1
- package/bizar-dash/dist/assets/MobileSettings-K-sNsn_r.js +1 -0
- package/bizar-dash/dist/assets/{MobileSettings-CEQNJNLJ.js.map → MobileSettings-K-sNsn_r.js.map} +1 -1
- package/bizar-dash/dist/assets/{Toast-Cpl6-E63.js → Toast-WTxc3m2g.js} +1 -1
- package/bizar-dash/dist/assets/{Toast-Cpl6-E63.js.map → Toast-WTxc3m2g.js.map} +1 -1
- package/bizar-dash/dist/assets/flow-BpU0-gK2.js +1 -0
- package/bizar-dash/dist/assets/flow-BpU0-gK2.js.map +1 -0
- package/bizar-dash/dist/assets/fuzzy-l0sNRNKZ.js +1 -0
- package/bizar-dash/dist/assets/fuzzy-l0sNRNKZ.js.map +1 -0
- package/bizar-dash/dist/assets/{icons-Do5N2jmX.js → icons-B0AKnYI3.js} +98 -103
- package/bizar-dash/dist/assets/icons-B0AKnYI3.js.map +1 -0
- package/bizar-dash/dist/assets/main-C-Md3DaY.css +1 -0
- package/bizar-dash/dist/assets/main-CO8fIxQK.js +18 -0
- package/bizar-dash/dist/assets/main-CO8fIxQK.js.map +1 -0
- package/bizar-dash/dist/assets/markdown-w2bDDkAH.js +1 -0
- package/bizar-dash/dist/assets/{markdown-tOLaD6nm.js.map → markdown-w2bDDkAH.js.map} +1 -1
- package/bizar-dash/dist/assets/mobile-DXYCA0hC.js +1 -0
- package/bizar-dash/dist/assets/{mobile-DYCHcUpq.js.map → mobile-DXYCA0hC.js.map} +1 -1
- package/bizar-dash/dist/assets/mobile-layout-DSmtNPja.js +2 -0
- package/bizar-dash/dist/assets/mobile-layout-DSmtNPja.js.map +1 -0
- package/bizar-dash/dist/assets/react-vendor-BnuhLJ6X.css +1 -0
- package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js +46 -0
- package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js.map +1 -0
- package/bizar-dash/dist/assets/useSlashCommands-3DFLOfpk.js +2 -0
- package/bizar-dash/dist/assets/useSlashCommands-3DFLOfpk.js.map +1 -0
- package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js +45 -0
- package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js.map +1 -0
- package/bizar-dash/dist/index.html +12 -10
- package/bizar-dash/dist/mobile.html +7 -4
- package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/bizar-dash/src/server/api.mjs +11 -0
- package/bizar-dash/src/server/memory-consolidator.mjs +218 -0
- package/bizar-dash/src/server/memory-distillation.mjs +296 -0
- package/bizar-dash/src/server/routes/distill.mjs +129 -0
- package/bizar-dash/src/server/routes/goal-planner.mjs +236 -0
- package/bizar-dash/src/web/App.tsx +4 -0
- package/bizar-dash/src/web/components/Tag.tsx +18 -5
- package/bizar-dash/src/web/components/Topbar.tsx +3 -0
- package/bizar-dash/src/web/components/agents/CommunicationLog.tsx +126 -0
- package/bizar-dash/src/web/components/agents/DependencyGraph.tsx +163 -0
- package/bizar-dash/src/web/components/agents/QualityGates.tsx +123 -0
- package/bizar-dash/src/web/components/agents/RealTimeEventLog.tsx +127 -0
- package/bizar-dash/src/web/components/agents/RoutingDecisions.test.tsx +148 -0
- package/bizar-dash/src/web/components/agents/RoutingDecisions.tsx +160 -0
- package/bizar-dash/src/web/components/goals/GoalInput.tsx +86 -0
- package/bizar-dash/src/web/components/goals/PlanVisualization.tsx +111 -0
- package/bizar-dash/src/web/lib/goapPlanner.ts +614 -0
- package/bizar-dash/src/web/lib/types.ts +25 -0
- package/bizar-dash/src/web/styles/agents-redesign.css +311 -0
- package/bizar-dash/src/web/styles/main.css +251 -0
- package/bizar-dash/src/web/styles/tasks-redesign.css +231 -0
- package/bizar-dash/src/web/ui/controls/Button.tsx +77 -0
- package/bizar-dash/src/web/ui/controls/Checkbox.tsx +95 -0
- package/bizar-dash/src/web/ui/controls/IconButton.tsx +66 -0
- package/bizar-dash/src/web/ui/controls/Kbd.tsx +24 -0
- package/bizar-dash/src/web/ui/controls/NumberInput.tsx +77 -0
- package/bizar-dash/src/web/ui/controls/RadioGroup.tsx +109 -0
- package/bizar-dash/src/web/ui/controls/SearchInput.tsx +91 -0
- package/bizar-dash/src/web/ui/controls/Select.tsx +92 -0
- package/bizar-dash/src/web/ui/controls/Slider.tsx +70 -0
- package/bizar-dash/src/web/ui/controls/TextInput.tsx +67 -0
- package/bizar-dash/src/web/ui/controls/Toggle.tsx +79 -0
- package/bizar-dash/src/web/ui/controls/controls.css +454 -0
- package/bizar-dash/src/web/ui/controls/index.ts +33 -0
- package/bizar-dash/src/web/ui/data/BarChart.tsx +60 -0
- package/bizar-dash/src/web/ui/data/DataTable.tsx +216 -0
- package/bizar-dash/src/web/ui/data/EmptyState.tsx +60 -0
- package/bizar-dash/src/web/ui/data/ErrorState.tsx +56 -0
- package/bizar-dash/src/web/ui/data/KeyValueList.tsx +106 -0
- package/bizar-dash/src/web/ui/data/LoadingState.tsx +65 -0
- package/bizar-dash/src/web/ui/data/Sparkline.tsx +87 -0
- package/bizar-dash/src/web/ui/data/StatTile.tsx +87 -0
- package/bizar-dash/src/web/ui/data/data.css +535 -0
- package/bizar-dash/src/web/ui/data/index.ts +31 -0
- package/bizar-dash/src/web/ui/feedback/Badge.tsx +49 -0
- package/bizar-dash/src/web/ui/feedback/Dialog.tsx +209 -0
- package/bizar-dash/src/web/ui/feedback/ProgressBar.tsx +69 -0
- package/bizar-dash/src/web/ui/feedback/StatusDot.tsx +50 -0
- package/bizar-dash/src/web/ui/feedback/Toast.tsx +200 -0
- package/bizar-dash/src/web/ui/feedback/Tooltip.tsx +126 -0
- package/bizar-dash/src/web/ui/feedback/feedback.css +314 -0
- package/bizar-dash/src/web/ui/feedback/index.ts +43 -0
- package/bizar-dash/src/web/ui/index.ts +56 -0
- package/bizar-dash/src/web/ui/layout/AppShell.tsx +51 -0
- package/bizar-dash/src/web/ui/layout/Breadcrumbs.tsx +97 -0
- package/bizar-dash/src/web/ui/layout/Panel.tsx +73 -0
- package/bizar-dash/src/web/ui/layout/PanelHeader.tsx +39 -0
- package/bizar-dash/src/web/ui/layout/Sidebar.tsx +131 -0
- package/bizar-dash/src/web/ui/layout/Tabs.tsx +133 -0
- package/bizar-dash/src/web/ui/layout/Topbar.tsx +42 -0
- package/bizar-dash/src/web/ui/layout/index.ts +33 -0
- package/bizar-dash/src/web/ui/layout/layout.css +413 -0
- package/bizar-dash/src/web/ui/navigation/CommandPalette.tsx +199 -0
- package/bizar-dash/src/web/ui/navigation/NavGroup.tsx +95 -0
- package/bizar-dash/src/web/ui/navigation/NavLink.tsx +93 -0
- package/bizar-dash/src/web/ui/navigation/index.ts +17 -0
- package/bizar-dash/src/web/ui/navigation/navigation.css +343 -0
- package/bizar-dash/src/web/ui/primitives/Box.tsx +91 -0
- package/bizar-dash/src/web/ui/primitives/Grid.tsx +49 -0
- package/bizar-dash/src/web/ui/primitives/Inline.tsx +55 -0
- package/bizar-dash/src/web/ui/primitives/Separator.tsx +46 -0
- package/bizar-dash/src/web/ui/primitives/Stack.tsx +54 -0
- package/bizar-dash/src/web/ui/primitives/VisuallyHidden.tsx +26 -0
- package/bizar-dash/src/web/ui/primitives/index.ts +23 -0
- package/bizar-dash/src/web/ui/primitives/primitives.css +183 -0
- package/bizar-dash/src/web/ui/styles/globals.css +86 -0
- package/bizar-dash/src/web/ui/styles/reset.css +125 -0
- package/bizar-dash/src/web/ui/styles/tokens.css +195 -0
- package/bizar-dash/src/web/ui/theme/ThemeProvider.tsx +142 -0
- package/bizar-dash/src/web/ui/theme/tokens.ts +131 -0
- package/bizar-dash/src/web/ui/theme/useTheme.ts +22 -0
- package/bizar-dash/src/web/ui/utils/cx.ts +42 -0
- package/bizar-dash/src/web/views/Agents.tsx +506 -266
- package/bizar-dash/src/web/views/GoalPlanner.tsx +158 -0
- package/bizar-dash/src/web/views/Overview.tsx +996 -448
- package/bizar-dash/src/web/views/Tasks.tsx +277 -273
- package/bizar-dash/tests/communication-log.test.tsx +67 -0
- package/bizar-dash/tests/dep-graph.test.tsx +40 -0
- package/bizar-dash/tests/event-log.test.tsx +61 -0
- package/bizar-dash/tests/goal-input.test.tsx +72 -0
- package/bizar-dash/tests/goap-planner.test.ts +68 -0
- package/bizar-dash/tests/quality-gates.test.tsx +59 -0
- package/bizar-dash/tests/setup.ts +31 -1
- package/bizar-dash/tests/ui/controls/Button.test.tsx +50 -0
- package/bizar-dash/tests/ui/controls/Checkbox.test.tsx +44 -0
- package/bizar-dash/tests/ui/controls/IconButton.test.tsx +37 -0
- package/bizar-dash/tests/ui/controls/Kbd.test.tsx +39 -0
- package/bizar-dash/tests/ui/controls/NumberInput.test.tsx +56 -0
- package/bizar-dash/tests/ui/controls/RadioGroup.test.tsx +58 -0
- package/bizar-dash/tests/ui/controls/SearchInput.test.tsx +52 -0
- package/bizar-dash/tests/ui/controls/Select.test.tsx +66 -0
- package/bizar-dash/tests/ui/controls/Slider.test.tsx +41 -0
- package/bizar-dash/tests/ui/controls/TextInput.test.tsx +44 -0
- package/bizar-dash/tests/ui/controls/Toggle.test.tsx +39 -0
- package/bizar-dash/tests/ui/data/BarChart.test.tsx +55 -0
- package/bizar-dash/tests/ui/data/DataTable.test.tsx +112 -0
- package/bizar-dash/tests/ui/data/EmptyState.test.tsx +57 -0
- package/bizar-dash/tests/ui/data/ErrorState.test.tsx +47 -0
- package/bizar-dash/tests/ui/data/KeyValueList.test.tsx +59 -0
- package/bizar-dash/tests/ui/data/LoadingState.test.tsx +37 -0
- package/bizar-dash/tests/ui/data/Sparkline.test.tsx +56 -0
- package/bizar-dash/tests/ui/data/StatTile.test.tsx +69 -0
- package/bizar-dash/tests/ui/feedback/Badge.test.tsx +41 -0
- package/bizar-dash/tests/ui/feedback/Dialog.test.tsx +99 -0
- package/bizar-dash/tests/ui/feedback/ProgressBar.test.tsx +66 -0
- package/bizar-dash/tests/ui/feedback/StatusDot.test.tsx +41 -0
- package/bizar-dash/tests/ui/feedback/Toast.test.tsx +124 -0
- package/bizar-dash/tests/ui/feedback/Tooltip.test.tsx +102 -0
- package/bizar-dash/tests/ui/layout/AppShell.test.tsx +50 -0
- package/bizar-dash/tests/ui/layout/Breadcrumbs.test.tsx +85 -0
- package/bizar-dash/tests/ui/layout/Panel.test.tsx +52 -0
- package/bizar-dash/tests/ui/layout/PanelHeader.test.tsx +32 -0
- package/bizar-dash/tests/ui/layout/Sidebar.test.tsx +95 -0
- package/bizar-dash/tests/ui/layout/Tabs.test.tsx +87 -0
- package/bizar-dash/tests/ui/layout/Topbar.test.tsx +41 -0
- package/bizar-dash/tests/ui/navigation/CommandPalette.test.tsx +105 -0
- package/bizar-dash/tests/ui/navigation/NavGroup.test.tsx +55 -0
- package/bizar-dash/tests/ui/navigation/NavLink.test.tsx +61 -0
- package/bizar-dash/tests/ui/primitives/Box.test.tsx +49 -0
- package/bizar-dash/tests/ui/primitives/Grid.test.tsx +37 -0
- package/bizar-dash/tests/ui/primitives/Inline.test.tsx +33 -0
- package/bizar-dash/tests/ui/primitives/Separator.test.tsx +34 -0
- package/bizar-dash/tests/ui/primitives/Stack.test.tsx +45 -0
- package/bizar-dash/tests/ui/primitives/VisuallyHidden.test.tsx +27 -0
- package/bizar-dash/tests/views/Agents.test.tsx +359 -0
- package/bizar-dash/tests/views/Overview.test.tsx +413 -0
- package/bizar-dash/tests/views/Tasks.test.tsx +250 -0
- package/bizar-dash/vitest.config.ts +3 -1
- package/cli/__tests__/cost-gate.test.mjs +301 -0
- package/cli/__tests__/feature-list-bridge.test.mjs +371 -0
- package/cli/bin.mjs +38 -0
- package/cli/commands/claim.mjs +282 -0
- package/cli/commands/cost.mjs +279 -0
- package/cli/cost-gate.mjs +501 -0
- package/cli/feature-list-bridge.mjs +400 -0
- package/cli/memory-constants.mjs +17 -0
- package/cli/worker-dispatcher.mjs +259 -0
- package/cli/worker-dispatcher.test.mjs +204 -0
- package/config/trigger-patterns.json +186 -0
- package/package.json +2 -2
- package/packages/sdk/package.json +1 -1
- package/packages/sdk/src/agent-registry.ts +304 -0
- package/packages/sdk/src/consensus/byzantine.ts +587 -0
- package/packages/sdk/src/consensus/index.ts +147 -0
- package/packages/sdk/src/consensus/queen.ts +173 -0
- package/packages/sdk/src/consensus/types.ts +199 -0
- package/packages/sdk/src/federation/audit.ts +156 -0
- package/packages/sdk/src/federation/budget.ts +269 -0
- package/packages/sdk/src/federation/envelope.ts +144 -0
- package/packages/sdk/src/federation/hmac.ts +160 -0
- package/packages/sdk/src/federation/index.ts +443 -0
- package/packages/sdk/src/federation/pii.ts +241 -0
- package/packages/sdk/src/federation/policy.ts +112 -0
- package/packages/sdk/src/federation/trust.ts +146 -0
- package/packages/sdk/src/index.ts +67 -0
- package/packages/sdk/src/mcp/server.ts +351 -1
- package/packages/sdk/src/router/codemod-intent.ts +125 -0
- package/packages/sdk/src/router/index.ts +247 -0
- package/packages/sdk/src/router/memory-distillation-shim.mjs +18 -0
- package/packages/sdk/src/router/memory-distillation.ts +249 -0
- package/packages/sdk/src/router/model-router.ts +235 -0
- package/packages/sdk/src/router/q-learning-router.ts +304 -0
- package/packages/sdk/src/swarm-topology.ts +279 -0
- package/packages/sdk/tests/agent-registry.test.ts +285 -0
- package/packages/sdk/tests/consensus.test.ts +376 -0
- package/packages/sdk/tests/federation/audit.test.ts +164 -0
- package/packages/sdk/tests/federation/budget.test.ts +189 -0
- package/packages/sdk/tests/federation/envelope.test.ts +120 -0
- package/packages/sdk/tests/federation/hmac.test.ts +194 -0
- package/packages/sdk/tests/federation/orchestrator.test.ts +176 -0
- package/packages/sdk/tests/federation/pii.test.ts +106 -0
- package/packages/sdk/tests/federation/policy.test.ts +140 -0
- package/packages/sdk/tests/federation/trust.test.ts +124 -0
- package/packages/sdk/tests/mcp-tools.test.ts +204 -0
- package/packages/sdk/tests/memory-distillation.test.mjs +186 -0
- package/packages/sdk/tests/model-router.test.mjs +108 -0
- package/packages/sdk/tests/q-learning-router.test.mjs +100 -0
- package/packages/sdk/tests/router-orchestrator.test.mjs +172 -0
- package/packages/sdk/tests/router.test.mjs +91 -0
- package/packages/sdk/tests/sdk.test.mjs +26 -2
- package/packages/sdk/tests/swarm-topology.test.ts +258 -0
- package/scripts/bh-full-e2e.mjs +11 -15
- package/bizar-dash/dist/assets/MobileChat-BJrqwVDd.js +0 -1
- package/bizar-dash/dist/assets/MobileSettings-CEQNJNLJ.js +0 -1
- package/bizar-dash/dist/assets/icons-Do5N2jmX.js.map +0 -1
- package/bizar-dash/dist/assets/main-IvfQAOfy.js +0 -18
- package/bizar-dash/dist/assets/main-IvfQAOfy.js.map +0 -1
- package/bizar-dash/dist/assets/main-ietCEg_R.css +0 -1
- package/bizar-dash/dist/assets/markdown-tOLaD6nm.js +0 -1
- package/bizar-dash/dist/assets/mobile-DYCHcUpq.js +0 -1
- package/bizar-dash/dist/assets/mobile-layout-CBHjpwsb.js +0 -2
- package/bizar-dash/dist/assets/mobile-layout-CBHjpwsb.js.map +0 -1
- package/bizar-dash/dist/assets/react-vendor-Dn4wqh4Z.js +0 -40
- package/bizar-dash/dist/assets/react-vendor-Dn4wqh4Z.js.map +0 -1
- package/bizar-dash/dist/assets/useSlashCommands-Bd7_FA6U.js +0 -2
- package/bizar-dash/dist/assets/useSlashCommands-Bd7_FA6U.js.map +0 -1
- package/bizar-dash/dist/assets/vendor-C843201K.js +0 -29
- package/bizar-dash/dist/assets/vendor-C843201K.js.map +0 -1
- package/packages/sdk/package-lock.json +0 -32
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import { Grid } from '../../../src/web/ui/primitives/Grid';
|
|
4
|
+
|
|
5
|
+
describe('Grid', () => {
|
|
6
|
+
it('renders a grid container with default 1 column', () => {
|
|
7
|
+
render(<Grid data-testid="g" />);
|
|
8
|
+
const el = screen.getByTestId('g');
|
|
9
|
+
expect(el).toHaveClass('grid', 'cols-1');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('applies the requested column count class', () => {
|
|
13
|
+
render(<Grid cols={3} data-testid="g" />);
|
|
14
|
+
expect(screen.getByTestId('g')).toHaveClass('cols-3');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('applies gap class from prop', () => {
|
|
18
|
+
render(<Grid cols={2} gap={5} data-testid="g" />);
|
|
19
|
+
expect(screen.getByTestId('g')).toHaveClass('gap-5');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('switches to autoFit class when autoFit is true', () => {
|
|
23
|
+
render(<Grid autoFit cols={4} data-testid="g" />);
|
|
24
|
+
const el = screen.getByTestId('g');
|
|
25
|
+
expect(el).toHaveClass('grid-autofit');
|
|
26
|
+
expect(el).not.toHaveClass('cols-4');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('renders children inside the grid', () => {
|
|
30
|
+
render(
|
|
31
|
+
<Grid>
|
|
32
|
+
<div data-testid="cell">x</div>
|
|
33
|
+
</Grid>,
|
|
34
|
+
);
|
|
35
|
+
expect(screen.getByTestId('cell')).toBeInTheDocument();
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import { Inline } from '../../../src/web/ui/primitives/Inline';
|
|
4
|
+
|
|
5
|
+
describe('Inline', () => {
|
|
6
|
+
it('renders as a row with wrap by default', () => {
|
|
7
|
+
const { container } = render(<Inline data-testid="i">a</Inline>);
|
|
8
|
+
const el = screen.getByTestId('i');
|
|
9
|
+
expect(el).toHaveClass('inline', 'wrap');
|
|
10
|
+
expect(el).not.toHaveClass('inline-col');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('uses gap-3 by default', () => {
|
|
14
|
+
render(<Inline data-testid="i" />);
|
|
15
|
+
expect(screen.getByTestId('i')).toHaveClass('gap-3');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('switches direction to column', () => {
|
|
19
|
+
render(<Inline direction="column" data-testid="i" />);
|
|
20
|
+
expect(screen.getByTestId('i')).toHaveClass('inline-col');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('applies nowrap when wrap is false', () => {
|
|
24
|
+
render(<Inline wrap={false} data-testid="i" />);
|
|
25
|
+
expect(screen.getByTestId('i')).toHaveClass('nowrap');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('forwards alignment props', () => {
|
|
29
|
+
render(<Inline align="baseline" justify="end" data-testid="i" />);
|
|
30
|
+
const el = screen.getByTestId('i');
|
|
31
|
+
expect(el).toHaveClass('align-baseline', 'justify-end');
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import { Separator } from '../../../src/web/ui/primitives/Separator';
|
|
4
|
+
|
|
5
|
+
describe('Separator', () => {
|
|
6
|
+
it('renders an <hr> by default in horizontal orientation', () => {
|
|
7
|
+
const { container } = render(<Separator />);
|
|
8
|
+
const hr = container.querySelector('hr');
|
|
9
|
+
expect(hr).toBeInTheDocument();
|
|
10
|
+
expect(hr).toHaveClass('separator');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('applies the vertical orientation class to <hr>', () => {
|
|
14
|
+
const { container } = render(<Separator orientation="vertical" />);
|
|
15
|
+
const hr = container.querySelector('hr');
|
|
16
|
+
expect(hr).toHaveClass('separator-vertical');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('renders a labelled separator as a div with role=separator', () => {
|
|
20
|
+
const { container } = render(<Separator label="OR" />);
|
|
21
|
+
const el = container.querySelector('[role="separator"]');
|
|
22
|
+
expect(el).toBeInTheDocument();
|
|
23
|
+
expect(el).toHaveClass('separator-with-label');
|
|
24
|
+
expect(el).toHaveTextContent('OR');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('sets aria-orientation on the labelled variant', () => {
|
|
28
|
+
const { container } = render(
|
|
29
|
+
<Separator label="AND" orientation="horizontal" />,
|
|
30
|
+
);
|
|
31
|
+
const el = container.querySelector('[role="separator"]');
|
|
32
|
+
expect(el?.getAttribute('aria-orientation')).toBe('horizontal');
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import { Stack } from '../../../src/web/ui/primitives/Stack';
|
|
4
|
+
|
|
5
|
+
describe('Stack', () => {
|
|
6
|
+
it('renders a column by default with gap-3', () => {
|
|
7
|
+
const { container } = render(
|
|
8
|
+
<Stack data-testid="s">
|
|
9
|
+
<span>a</span>
|
|
10
|
+
</Stack>,
|
|
11
|
+
);
|
|
12
|
+
const el = screen.getByTestId('s');
|
|
13
|
+
expect(el).toHaveClass('stack');
|
|
14
|
+
expect(el).toHaveClass('gap-3');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('switches to row direction', () => {
|
|
18
|
+
const { container } = render(<Stack direction="row" data-testid="s" />);
|
|
19
|
+
expect(screen.getByTestId('s')).toHaveClass('stack-row');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('applies alignment and justify modifiers', () => {
|
|
23
|
+
const { container } = render(
|
|
24
|
+
<Stack align="center" justify="between" data-testid="s" />,
|
|
25
|
+
);
|
|
26
|
+
const el = screen.getByTestId('s');
|
|
27
|
+
expect(el).toHaveClass('align-center', 'justify-between');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('enables flex-wrap when wrap is true', () => {
|
|
31
|
+
const { container } = render(<Stack wrap data-testid="s" />);
|
|
32
|
+
expect(screen.getByTestId('s')).toHaveClass('wrap');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('renders children inside the flex container', () => {
|
|
36
|
+
render(
|
|
37
|
+
<Stack>
|
|
38
|
+
<span data-testid="c1">a</span>
|
|
39
|
+
<span data-testid="c2">b</span>
|
|
40
|
+
</Stack>,
|
|
41
|
+
);
|
|
42
|
+
expect(screen.getByTestId('c1')).toBeInTheDocument();
|
|
43
|
+
expect(screen.getByTestId('c2')).toBeInTheDocument();
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import { VisuallyHidden } from '../../../src/web/ui/primitives/VisuallyHidden';
|
|
4
|
+
|
|
5
|
+
describe('VisuallyHidden', () => {
|
|
6
|
+
it('renders its children inside a span', () => {
|
|
7
|
+
render(<VisuallyHidden>hidden text</VisuallyHidden>);
|
|
8
|
+
const el = screen.getByText('hidden text');
|
|
9
|
+
expect(el.tagName).toBe('SPAN');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('applies the vh class for SR-only styling', () => {
|
|
13
|
+
render(<VisuallyHidden>sr-only</VisuallyHidden>);
|
|
14
|
+
expect(screen.getByText('sr-only')).toHaveClass('vh');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('passes additional attributes through to the span', () => {
|
|
18
|
+
render(
|
|
19
|
+
<VisuallyHidden id="vh-1" data-testid="vh">
|
|
20
|
+
x
|
|
21
|
+
</VisuallyHidden>,
|
|
22
|
+
);
|
|
23
|
+
const el = screen.getByTestId('vh');
|
|
24
|
+
expect(el.id).toBe('vh-1');
|
|
25
|
+
expect(el).toHaveClass('vh');
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
// tests/views/Agents.test.tsx — Wave 3 Agents view (split-pane roster + detail panel).
|
|
2
|
+
//
|
|
3
|
+
// Covers the redesign contract: DataTable renders rows from snapshot, clicking
|
|
4
|
+
// a row selects an agent and updates the detail panel, category + search
|
|
5
|
+
// filters narrow the table, and the Edit button fires the handler. The
|
|
6
|
+
// create/delete/invoke modals use the legacy Modal and are exercised here
|
|
7
|
+
// only at the click-button level — their form contents are unchanged from
|
|
8
|
+
// the previous view and live in the legacy test suite.
|
|
9
|
+
|
|
10
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
11
|
+
import { render, screen, waitFor, within } from '@testing-library/react';
|
|
12
|
+
import userEvent from '@testing-library/user-event';
|
|
13
|
+
import React from 'react';
|
|
14
|
+
import { ToastProvider } from '../../src/web/components/Toast';
|
|
15
|
+
import { ModalProvider } from '../../src/web/components/Modal';
|
|
16
|
+
import { Agents } from '../../src/web/views/Agents';
|
|
17
|
+
import type { Agent, Settings, Snapshot } from '../../src/web/lib/types';
|
|
18
|
+
|
|
19
|
+
// ─── API mock ────────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
const apiGet = vi.fn();
|
|
22
|
+
const apiPost = vi.fn();
|
|
23
|
+
const apiPut = vi.fn();
|
|
24
|
+
const apiDel = vi.fn();
|
|
25
|
+
|
|
26
|
+
vi.mock('../../src/web/lib/api', () => ({
|
|
27
|
+
api: {
|
|
28
|
+
get: (...args: unknown[]) => apiGet(...args),
|
|
29
|
+
post: (...args: unknown[]) => apiPost(...args),
|
|
30
|
+
put: (...args: unknown[]) => apiPut(...args),
|
|
31
|
+
del: (...args: unknown[]) => apiDel(...args),
|
|
32
|
+
},
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
// The Agents view does not own a WS subscription (parent App.tsx does), but
|
|
36
|
+
// stubbing Ws makes the file load-safe if a future change introduces one.
|
|
37
|
+
vi.mock('../../src/web/lib/ws', () => ({
|
|
38
|
+
Ws: class {
|
|
39
|
+
on() { return () => undefined; }
|
|
40
|
+
onStatus() { return () => undefined; }
|
|
41
|
+
close() {}
|
|
42
|
+
},
|
|
43
|
+
}));
|
|
44
|
+
|
|
45
|
+
// ─── Test fixtures ───────────────────────────────────────────────────────
|
|
46
|
+
|
|
47
|
+
const odin: Agent = {
|
|
48
|
+
name: 'odin',
|
|
49
|
+
description: 'Top-tier orchestrator',
|
|
50
|
+
model: 'anthropic/claude-3-5-sonnet',
|
|
51
|
+
mode: 'primary',
|
|
52
|
+
file: 'odin.md',
|
|
53
|
+
path: '/agents/odin.md',
|
|
54
|
+
mtime: Date.now() - 3600_000,
|
|
55
|
+
tools: ['bash', 'read', 'edit'],
|
|
56
|
+
tags: ['orchestrator', 'reasoning'],
|
|
57
|
+
category: 'reasoning',
|
|
58
|
+
color: '#8b5cf6',
|
|
59
|
+
status: 'working',
|
|
60
|
+
isStuck: false,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const frigg: Agent = {
|
|
64
|
+
name: 'frigg',
|
|
65
|
+
description: 'Quality + memory curator',
|
|
66
|
+
model: 'anthropic/claude-3-5-haiku',
|
|
67
|
+
mode: 'subagent',
|
|
68
|
+
file: 'frigg.md',
|
|
69
|
+
path: '/agents/frigg.md',
|
|
70
|
+
mtime: Date.now() - 7200_000,
|
|
71
|
+
tools: ['read', 'grep'],
|
|
72
|
+
tags: ['curator', 'analysis'],
|
|
73
|
+
category: 'analysis',
|
|
74
|
+
status: 'idle',
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const tyr: Agent = {
|
|
78
|
+
name: 'tyr',
|
|
79
|
+
description: 'Implementation engine',
|
|
80
|
+
model: 'openai/gpt-4o',
|
|
81
|
+
mode: 'subagent',
|
|
82
|
+
file: 'tyr.md',
|
|
83
|
+
path: '/agents/tyr.md',
|
|
84
|
+
mtime: Date.now() - 1800_000,
|
|
85
|
+
tools: ['bash', 'edit', 'write'],
|
|
86
|
+
tags: ['implementation', 'code'],
|
|
87
|
+
category: 'code',
|
|
88
|
+
color: '#2563eb',
|
|
89
|
+
status: 'working',
|
|
90
|
+
isStuck: true,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
function makeSnapshot(agents: Agent[] = [odin, frigg, tyr]): Snapshot {
|
|
94
|
+
return {
|
|
95
|
+
agents,
|
|
96
|
+
} as unknown as Snapshot;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const dummySettings = {} as Settings;
|
|
100
|
+
|
|
101
|
+
const Harness: React.FC<{ children: React.ReactNode }> = ({ children }) => (
|
|
102
|
+
<ToastProvider>
|
|
103
|
+
<ModalProvider>{children}</ModalProvider>
|
|
104
|
+
</ToastProvider>
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
// ─── Tests ───────────────────────────────────────────────────────────────
|
|
108
|
+
|
|
109
|
+
describe('Agents view (Wave 3 redesign)', () => {
|
|
110
|
+
beforeEach(() => {
|
|
111
|
+
apiGet.mockReset();
|
|
112
|
+
apiPost.mockReset();
|
|
113
|
+
apiPut.mockReset();
|
|
114
|
+
apiDel.mockReset();
|
|
115
|
+
// Default mock for any GET that may sneak through (modal loads).
|
|
116
|
+
apiGet.mockImplementation(async () => ({}));
|
|
117
|
+
apiPost.mockResolvedValue({});
|
|
118
|
+
apiPut.mockResolvedValue({});
|
|
119
|
+
apiDel.mockResolvedValue({});
|
|
120
|
+
// confirm() always true for delete tests
|
|
121
|
+
vi.spyOn(window, 'confirm').mockReturnValue(true);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('renders the DataTable rows from the snapshot', async () => {
|
|
125
|
+
render(
|
|
126
|
+
<Harness>
|
|
127
|
+
<Agents
|
|
128
|
+
snapshot={makeSnapshot()}
|
|
129
|
+
settings={dummySettings}
|
|
130
|
+
activeTab="agents"
|
|
131
|
+
setActiveTab={() => undefined}
|
|
132
|
+
refreshSnapshot={async () => undefined}
|
|
133
|
+
/>
|
|
134
|
+
</Harness>,
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
expect(await screen.findByTestId('agents-table')).toBeInTheDocument();
|
|
138
|
+
expect(screen.getByTestId('agents-count').textContent).toContain('(3)');
|
|
139
|
+
|
|
140
|
+
const table = screen.getByTestId('agents-table');
|
|
141
|
+
expect(within(table).getByText('odin')).toBeInTheDocument();
|
|
142
|
+
expect(within(table).getByText('frigg')).toBeInTheDocument();
|
|
143
|
+
expect(within(table).getByText('tyr')).toBeInTheDocument();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('shows the empty state when no agents match', async () => {
|
|
147
|
+
render(
|
|
148
|
+
<Harness>
|
|
149
|
+
<Agents
|
|
150
|
+
snapshot={makeSnapshot([])}
|
|
151
|
+
settings={dummySettings}
|
|
152
|
+
activeTab="agents"
|
|
153
|
+
setActiveTab={() => undefined}
|
|
154
|
+
refreshSnapshot={async () => undefined}
|
|
155
|
+
/>
|
|
156
|
+
</Harness>,
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
expect(await screen.findByText(/No agents found/i)).toBeInTheDocument();
|
|
160
|
+
expect(screen.getByTestId('agents-count').textContent).toContain('(0)');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('clicking a row populates the detail panel', async () => {
|
|
164
|
+
const user = userEvent.setup();
|
|
165
|
+
render(
|
|
166
|
+
<Harness>
|
|
167
|
+
<Agents
|
|
168
|
+
snapshot={makeSnapshot()}
|
|
169
|
+
settings={dummySettings}
|
|
170
|
+
activeTab="agents"
|
|
171
|
+
setActiveTab={() => undefined}
|
|
172
|
+
refreshSnapshot={async () => undefined}
|
|
173
|
+
/>
|
|
174
|
+
</Harness>,
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
const table = await screen.findByTestId('agents-table');
|
|
178
|
+
await user.click(within(table).getByText('frigg'));
|
|
179
|
+
|
|
180
|
+
// Detail panel shows the agent's metadata — use unique marker
|
|
181
|
+
await waitFor(() => {
|
|
182
|
+
expect(screen.getByText('Quality + memory curator')).toBeInTheDocument();
|
|
183
|
+
});
|
|
184
|
+
// Frigg-specific tag (curator) appears in the detail panel
|
|
185
|
+
expect(screen.getAllByText('curator').length).toBeGreaterThan(0);
|
|
186
|
+
// Edit button is rendered with frigg-specific testid
|
|
187
|
+
expect(screen.getByTestId('agents-detail-edit-frigg')).toBeInTheDocument();
|
|
188
|
+
// Frigg's path shows in the panel description
|
|
189
|
+
expect(screen.getByText('/agents/frigg.md')).toBeInTheDocument();
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('category filter narrows the visible agents', async () => {
|
|
193
|
+
const user = userEvent.setup();
|
|
194
|
+
render(
|
|
195
|
+
<Harness>
|
|
196
|
+
<Agents
|
|
197
|
+
snapshot={makeSnapshot()}
|
|
198
|
+
settings={dummySettings}
|
|
199
|
+
activeTab="agents"
|
|
200
|
+
setActiveTab={() => undefined}
|
|
201
|
+
refreshSnapshot={async () => undefined}
|
|
202
|
+
/>
|
|
203
|
+
</Harness>,
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
const table = await screen.findByTestId('agents-table');
|
|
207
|
+
expect(within(table).getByText('tyr')).toBeInTheDocument();
|
|
208
|
+
|
|
209
|
+
const filter = screen.getByTestId('agents-category-filter') as HTMLSelectElement;
|
|
210
|
+
await user.selectOptions(filter, 'code');
|
|
211
|
+
|
|
212
|
+
expect(within(table).queryByText('odin')).not.toBeInTheDocument();
|
|
213
|
+
expect(within(table).queryByText('frigg')).not.toBeInTheDocument();
|
|
214
|
+
expect(within(table).getByText('tyr')).toBeInTheDocument();
|
|
215
|
+
expect(screen.getByTestId('agents-count').textContent).toContain('(1)');
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('search filters by name / description / tag', async () => {
|
|
219
|
+
const user = userEvent.setup();
|
|
220
|
+
render(
|
|
221
|
+
<Harness>
|
|
222
|
+
<Agents
|
|
223
|
+
snapshot={makeSnapshot()}
|
|
224
|
+
settings={dummySettings}
|
|
225
|
+
activeTab="agents"
|
|
226
|
+
setActiveTab={() => undefined}
|
|
227
|
+
refreshSnapshot={async () => undefined}
|
|
228
|
+
/>
|
|
229
|
+
</Harness>,
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
const table = await screen.findByTestId('agents-table');
|
|
233
|
+
const search = screen.getByTestId('agents-search');
|
|
234
|
+
|
|
235
|
+
// Tag match
|
|
236
|
+
await user.type(search, 'curator');
|
|
237
|
+
expect(within(table).queryByText('odin')).not.toBeInTheDocument();
|
|
238
|
+
expect(within(table).queryByText('frigg')).toBeInTheDocument();
|
|
239
|
+
expect(within(table).queryByText('tyr')).not.toBeInTheDocument();
|
|
240
|
+
|
|
241
|
+
// Description match
|
|
242
|
+
await user.clear(search);
|
|
243
|
+
await user.type(search, 'Implementation');
|
|
244
|
+
expect(within(table).getByText('tyr')).toBeInTheDocument();
|
|
245
|
+
expect(within(table).queryByText('frigg')).not.toBeInTheDocument();
|
|
246
|
+
|
|
247
|
+
// Name match (case-insensitive)
|
|
248
|
+
await user.clear(search);
|
|
249
|
+
await user.type(search, 'ODIN');
|
|
250
|
+
expect(within(table).getByText('odin')).toBeInTheDocument();
|
|
251
|
+
expect(within(table).queryByText('tyr')).not.toBeInTheDocument();
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it('Edit button on the detail panel loads the agent and opens the edit modal', async () => {
|
|
255
|
+
const user = userEvent.setup();
|
|
256
|
+
apiGet.mockImplementation(async (path: string) => {
|
|
257
|
+
if (path === '/agents/odin') {
|
|
258
|
+
return { ...odin, prompt: 'You are Odin.' };
|
|
259
|
+
}
|
|
260
|
+
return {};
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
render(
|
|
264
|
+
<Harness>
|
|
265
|
+
<Agents
|
|
266
|
+
snapshot={makeSnapshot()}
|
|
267
|
+
settings={dummySettings}
|
|
268
|
+
activeTab="agents"
|
|
269
|
+
setActiveTab={() => undefined}
|
|
270
|
+
refreshSnapshot={async () => undefined}
|
|
271
|
+
/>
|
|
272
|
+
</Harness>,
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
const table = await screen.findByTestId('agents-table');
|
|
276
|
+
await user.click(within(table).getByText('odin'));
|
|
277
|
+
|
|
278
|
+
const editBtn = await screen.findByTestId('agents-detail-edit-odin');
|
|
279
|
+
await user.click(editBtn);
|
|
280
|
+
|
|
281
|
+
await waitFor(() => {
|
|
282
|
+
expect(apiGet).toHaveBeenCalledWith('/agents/odin');
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// Edit modal renders with the existing prompt pre-filled.
|
|
286
|
+
await waitFor(() => {
|
|
287
|
+
const promptArea = screen.getByLabelText(/system prompt/i) as HTMLTextAreaElement;
|
|
288
|
+
expect(promptArea.value).toBe('You are Odin.');
|
|
289
|
+
});
|
|
290
|
+
expect(screen.getByText('Edit odin')).toBeInTheDocument();
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('pulse fires on StatusDot when agent is stuck', async () => {
|
|
294
|
+
render(
|
|
295
|
+
<Harness>
|
|
296
|
+
<Agents
|
|
297
|
+
snapshot={makeSnapshot()}
|
|
298
|
+
settings={dummySettings}
|
|
299
|
+
activeTab="agents"
|
|
300
|
+
setActiveTab={() => undefined}
|
|
301
|
+
refreshSnapshot={async () => undefined}
|
|
302
|
+
/>
|
|
303
|
+
</Harness>,
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
const table = await screen.findByTestId('agents-table');
|
|
307
|
+
// Tyr is stuck — find the row and confirm the pulsing status dot is present.
|
|
308
|
+
const tyrRow = within(table).getByText('tyr').closest('tr');
|
|
309
|
+
expect(tyrRow).not.toBeNull();
|
|
310
|
+
const pulseDot = tyrRow!.querySelector('.bizar-status-dot--pulse');
|
|
311
|
+
expect(pulseDot).not.toBeNull();
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('renders the "No agent selected" placeholder when nothing is picked', async () => {
|
|
315
|
+
render(
|
|
316
|
+
<Harness>
|
|
317
|
+
<Agents
|
|
318
|
+
snapshot={makeSnapshot()}
|
|
319
|
+
settings={dummySettings}
|
|
320
|
+
activeTab="agents"
|
|
321
|
+
setActiveTab={() => undefined}
|
|
322
|
+
refreshSnapshot={async () => undefined}
|
|
323
|
+
/>
|
|
324
|
+
</Harness>,
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
expect(await screen.findByText(/No agent selected/i)).toBeInTheDocument();
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('refresh button reloads /agents and updates count', async () => {
|
|
331
|
+
const user = userEvent.setup();
|
|
332
|
+
apiGet.mockImplementation(async (path: string) => {
|
|
333
|
+
if (path === '/agents') return { agents: [odin] };
|
|
334
|
+
return {};
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
render(
|
|
338
|
+
<Harness>
|
|
339
|
+
<Agents
|
|
340
|
+
snapshot={makeSnapshot()}
|
|
341
|
+
settings={dummySettings}
|
|
342
|
+
activeTab="agents"
|
|
343
|
+
setActiveTab={() => undefined}
|
|
344
|
+
refreshSnapshot={async () => undefined}
|
|
345
|
+
/>
|
|
346
|
+
</Harness>,
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
expect(screen.getByTestId('agents-count').textContent).toContain('(3)');
|
|
350
|
+
await user.click(screen.getByTestId('agents-refresh'));
|
|
351
|
+
|
|
352
|
+
await waitFor(() => {
|
|
353
|
+
expect(apiGet).toHaveBeenCalledWith('/agents');
|
|
354
|
+
});
|
|
355
|
+
await waitFor(() => {
|
|
356
|
+
expect(screen.getByTestId('agents-count').textContent).toContain('(1)');
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
});
|