agent-nuvira 2.7.0 → 2.7.2
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/dist/agents/orchestrator.d.ts.map +1 -1
- package/dist/agents/orchestrator.js +17 -3
- package/dist/agents/orchestrator.js.map +1 -1
- package/dist/app.js +26 -0
- package/dist/cli/chat.d.ts.map +1 -1
- package/dist/cli/chat.js +108 -2
- package/dist/cli/chat.js.map +1 -1
- package/dist/config/auth.js +18 -0
- package/dist/config/jwt.js +24 -0
- package/dist/config/keys.js +14 -0
- package/dist/gateway/chat-store.d.ts +32 -5
- package/dist/gateway/chat-store.d.ts.map +1 -1
- package/dist/gateway/chat-store.js +52 -6
- package/dist/gateway/chat-store.js.map +1 -1
- package/dist/gateway/registry.d.ts.map +1 -1
- package/dist/gateway/registry.js +54 -10
- package/dist/gateway/registry.js.map +1 -1
- package/dist/inference/model-validator.d.ts.map +1 -1
- package/dist/inference/model-validator.js +11 -1
- package/dist/inference/model-validator.js.map +1 -1
- package/dist/inference/tool-call-utils.d.ts +31 -0
- package/dist/inference/tool-call-utils.d.ts.map +1 -1
- package/dist/inference/tool-call-utils.js +41 -0
- package/dist/inference/tool-call-utils.js.map +1 -1
- package/dist/learning/reasoning-trace.d.ts +5 -3
- package/dist/learning/reasoning-trace.d.ts.map +1 -1
- package/dist/learning/reasoning-trace.js +2 -2
- package/dist/learning/reasoning-trace.js.map +1 -1
- package/dist/middleware/auth.js +30 -0
- package/dist/nlu/conversation-gate.js +1 -1
- package/dist/nlu/conversation-gate.js.map +1 -1
- package/dist/nlu/entities.d.ts.map +1 -1
- package/dist/nlu/entities.js +12 -0
- package/dist/nlu/entities.js.map +1 -1
- package/dist/nlu/intent.d.ts.map +1 -1
- package/dist/nlu/intent.js +83 -15
- package/dist/nlu/intent.js.map +1 -1
- package/dist/passport.js +34 -0
- package/dist/routes/auth.js +42 -0
- package/dist/routes/user.js +31 -0
- package/dist/server.js +17 -0
- package/dist/tools/pipeline-tool.d.ts.map +1 -1
- package/dist/tools/pipeline-tool.js +14 -0
- package/dist/tools/pipeline-tool.js.map +1 -1
- package/dist/tools/release-sync.d.ts +52 -16
- package/dist/tools/release-sync.d.ts.map +1 -1
- package/dist/tools/release-sync.js +143 -79
- package/dist/tools/release-sync.js.map +1 -1
- package/dist/web-dashboard/src/App.js +85 -0
- package/dist/web-dashboard/src/admin-auth.test.js +186 -0
- package/dist/web-dashboard/src/ansi.js +23 -0
- package/dist/web-dashboard/src/ansi.test.js +31 -0
- package/dist/web-dashboard/src/api-admin-auth.test.js +172 -0
- package/dist/web-dashboard/src/api-admin.test.js +65 -0
- package/dist/web-dashboard/src/api-hub.test.js +117 -0
- package/dist/web-dashboard/src/api.js +1421 -0
- package/dist/web-dashboard/src/api.test.js +51 -0
- package/dist/web-dashboard/src/artifacts.js +128 -0
- package/dist/web-dashboard/src/artifacts.test.js +139 -0
- package/dist/web-dashboard/src/components/AdminPanel.js +567 -0
- package/dist/web-dashboard/src/components/AdminPanel.test.js +288 -0
- package/dist/web-dashboard/src/components/AgentHub.js +1580 -0
- package/dist/web-dashboard/src/components/AgentHub.test.js +343 -0
- package/dist/web-dashboard/src/components/BedrockOnboarding.js +320 -0
- package/dist/web-dashboard/src/components/BenchmarkCharts.js +228 -0
- package/dist/web-dashboard/src/components/ChatPage.js +1586 -0
- package/dist/web-dashboard/src/components/ChatPage.test.js +899 -0
- package/dist/web-dashboard/src/components/ContactsPage.js +141 -0
- package/dist/web-dashboard/src/components/CostDashboard.js +105 -0
- package/dist/web-dashboard/src/components/DAGView.js +477 -0
- package/dist/web-dashboard/src/components/DAGView.test.js +147 -0
- package/dist/web-dashboard/src/components/EnvVarEditor.js +138 -0
- package/dist/web-dashboard/src/components/EvalsPage.js +73 -0
- package/dist/web-dashboard/src/components/EvalsPage.test.js +120 -0
- package/dist/web-dashboard/src/components/ExecutionHistory.js +201 -0
- package/dist/web-dashboard/src/components/GatewayPage.js +40 -0
- package/dist/web-dashboard/src/components/GatewayPage.test.js +74 -0
- package/dist/web-dashboard/src/components/HealthPanel.js +68 -0
- package/dist/web-dashboard/src/components/HistoryBrowser.js +34 -0
- package/dist/web-dashboard/src/components/Layout.js +50 -0
- package/dist/web-dashboard/src/components/Markdown.js +152 -0
- package/dist/web-dashboard/src/components/Markdown.test.js +126 -0
- package/dist/web-dashboard/src/components/MarkdownZeroDep.js +237 -0
- package/dist/web-dashboard/src/components/MemoryPanel.js +81 -0
- package/dist/web-dashboard/src/components/ModelTimeline.js +298 -0
- package/dist/web-dashboard/src/components/ModelsPanel.js +1484 -0
- package/dist/web-dashboard/src/components/ModelsPanel.test.js +460 -0
- package/dist/web-dashboard/src/components/Overview.js +123 -0
- package/dist/web-dashboard/src/components/PhaseTimeline.js +359 -0
- package/dist/web-dashboard/src/components/PhaseTimeline.test.js +234 -0
- package/dist/web-dashboard/src/components/PlatformConfigSection.js +384 -0
- package/dist/web-dashboard/src/components/PlatformConfigSection.test.js +106 -0
- package/dist/web-dashboard/src/components/PlatformsPage.js +250 -0
- package/dist/web-dashboard/src/components/QuotaPanel.js +159 -0
- package/dist/web-dashboard/src/components/QuotaPanel.test.js +85 -0
- package/dist/web-dashboard/src/components/RequestsPanel.js +229 -0
- package/dist/web-dashboard/src/components/RequestsPanel.test.js +103 -0
- package/dist/web-dashboard/src/components/RoutingInsightsPanel.js +938 -0
- package/dist/web-dashboard/src/components/RoutingInsightsPanel.test.js +339 -0
- package/dist/web-dashboard/src/components/RoutingWalkthrough.js +408 -0
- package/dist/web-dashboard/src/components/RoutingWalkthrough.test.js +209 -0
- package/dist/web-dashboard/src/components/TaskConsole.js +119 -0
- package/dist/web-dashboard/src/components/TaskConsole.test.js +123 -0
- package/dist/web-dashboard/src/components/TasksPage.js +256 -0
- package/dist/web-dashboard/src/components/TasksPage.test.js +103 -0
- package/dist/web-dashboard/src/components/TracePanel.js +306 -0
- package/dist/web-dashboard/src/components/WhatsAppPanel.js +195 -0
- package/dist/web-dashboard/src/components/WhatsAppPanel.test.js +122 -0
- package/dist/web-dashboard/src/jsonOrNull.js +37 -0
- package/dist/web-dashboard/src/main.js +15 -0
- package/dist/web-dashboard/src/mask.js +11 -0
- package/dist/web-dashboard/vite.config.js +23 -0
- package/dist/web-dashboard/vitest.config.js +15 -0
- package/package.json +5 -3
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Unit tests for the PromotionGateSection rendered by RoutingInsightsPanel.
|
|
4
|
+
*
|
|
5
|
+
* Covers the three verdict states (promoted / not-promoted / collecting data),
|
|
6
|
+
* the honest neutral latency chip when latency is unmeasured, and the
|
|
7
|
+
* hidden-when-empty behavior. Rendered through the default RoutingInsightsPanel
|
|
8
|
+
* export so the `hasPromotion` wiring and empty-state gating are exercised too.
|
|
9
|
+
*/
|
|
10
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
13
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
14
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
15
|
+
}
|
|
16
|
+
Object.defineProperty(o, k2, desc);
|
|
17
|
+
}) : (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
o[k2] = m[k];
|
|
20
|
+
}));
|
|
21
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
22
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
23
|
+
}) : function(o, v) {
|
|
24
|
+
o["default"] = v;
|
|
25
|
+
});
|
|
26
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
27
|
+
var ownKeys = function(o) {
|
|
28
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
29
|
+
var ar = [];
|
|
30
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
31
|
+
return ar;
|
|
32
|
+
};
|
|
33
|
+
return ownKeys(o);
|
|
34
|
+
};
|
|
35
|
+
return function (mod) {
|
|
36
|
+
if (mod && mod.__esModule) return mod;
|
|
37
|
+
var result = {};
|
|
38
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
39
|
+
__setModuleDefault(result, mod);
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
})();
|
|
43
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
+
const vitest_1 = require("vitest");
|
|
45
|
+
const react_1 = require("@testing-library/react");
|
|
46
|
+
const RoutingInsightsPanel_1 = __importStar(require("./RoutingInsightsPanel"));
|
|
47
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
48
|
+
function makeData(promotion) {
|
|
49
|
+
return {
|
|
50
|
+
routing: {
|
|
51
|
+
providers: [],
|
|
52
|
+
bestModels: [],
|
|
53
|
+
preference: [],
|
|
54
|
+
promotion,
|
|
55
|
+
updatedAt: Date.now(),
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// ─── Governance card helpers (P6 M6.5) ─────────────────────────────────────
|
|
60
|
+
function makeGovernanceData(governance) {
|
|
61
|
+
return {
|
|
62
|
+
routing: {
|
|
63
|
+
providers: [],
|
|
64
|
+
bestModels: [],
|
|
65
|
+
preference: [],
|
|
66
|
+
governance,
|
|
67
|
+
updatedAt: Date.now(),
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
const basePromotion = {
|
|
72
|
+
decisionCount: 30,
|
|
73
|
+
divergedCount: 22,
|
|
74
|
+
minDecisions: 20,
|
|
75
|
+
qualityDelta: 0.05,
|
|
76
|
+
costDelta: 0.005,
|
|
77
|
+
latencyDelta: 0.02,
|
|
78
|
+
latencyMeasured: true,
|
|
79
|
+
criteria: { quality: true, cost: true, latency: true },
|
|
80
|
+
sufficient: true,
|
|
81
|
+
promoted: true,
|
|
82
|
+
};
|
|
83
|
+
// ─── Tests ──────────────────────────────────────────────────────────────────
|
|
84
|
+
(0, vitest_1.describe)('PromotionGateSection (via RoutingInsightsPanel)', () => {
|
|
85
|
+
(0, vitest_1.it)('renders the promoted verdict when all criteria pass with sufficient data', () => {
|
|
86
|
+
(0, react_1.render)(<RoutingInsightsPanel_1.default data={makeData(basePromotion)}/>);
|
|
87
|
+
(0, vitest_1.expect)(react_1.screen.getByText('Promoted — the bandit beats the heuristic')).toBeTruthy();
|
|
88
|
+
(0, vitest_1.expect)(react_1.screen.getByText('Quality ↑')).toBeTruthy();
|
|
89
|
+
(0, vitest_1.expect)(react_1.screen.getByText('Cost ↓')).toBeTruthy();
|
|
90
|
+
(0, vitest_1.expect)(react_1.screen.getByText('Latency ↓')).toBeTruthy();
|
|
91
|
+
// All three criteria pass
|
|
92
|
+
(0, vitest_1.expect)(react_1.screen.getAllByText('✓ pass')).toHaveLength(3);
|
|
93
|
+
(0, vitest_1.expect)(react_1.screen.queryByText('✗ fail')).toBeNull();
|
|
94
|
+
});
|
|
95
|
+
(0, vitest_1.it)('shows the collecting-data state when there are not enough diverged decisions', () => {
|
|
96
|
+
const promo = {
|
|
97
|
+
...basePromotion,
|
|
98
|
+
divergedCount: 5,
|
|
99
|
+
sufficient: false,
|
|
100
|
+
promoted: false,
|
|
101
|
+
};
|
|
102
|
+
(0, react_1.render)(<RoutingInsightsPanel_1.default data={makeData(promo)}/>);
|
|
103
|
+
(0, vitest_1.expect)(react_1.screen.getByText('Collecting data — need more diverged decisions')).toBeTruthy();
|
|
104
|
+
(0, vitest_1.expect)(react_1.screen.getByText('5 diverged decisions')).toBeTruthy();
|
|
105
|
+
(0, vitest_1.expect)(react_1.screen.getByText('need 20 for a verdict')).toBeTruthy();
|
|
106
|
+
// Not enough data → no promotion claim
|
|
107
|
+
(0, vitest_1.expect)(react_1.screen.queryByText('Promoted — the bandit beats the heuristic')).toBeNull();
|
|
108
|
+
(0, vitest_1.expect)(react_1.screen.queryByText('Not promoted — the bandit is not (yet) better')).toBeNull();
|
|
109
|
+
});
|
|
110
|
+
(0, vitest_1.it)('shows the not-promoted verdict when sufficient but a criterion fails', () => {
|
|
111
|
+
const promo = {
|
|
112
|
+
...basePromotion,
|
|
113
|
+
criteria: { quality: false, cost: true, latency: true },
|
|
114
|
+
promoted: false,
|
|
115
|
+
};
|
|
116
|
+
(0, react_1.render)(<RoutingInsightsPanel_1.default data={makeData(promo)}/>);
|
|
117
|
+
(0, vitest_1.expect)(react_1.screen.getByText('Not promoted — the bandit is not (yet) better')).toBeTruthy();
|
|
118
|
+
// Quality fails, cost + latency pass
|
|
119
|
+
(0, vitest_1.expect)(react_1.screen.getByText('✗ fail')).toBeTruthy();
|
|
120
|
+
(0, vitest_1.expect)(react_1.screen.getAllByText('✓ pass')).toHaveLength(2);
|
|
121
|
+
});
|
|
122
|
+
(0, vitest_1.it)('renders a neutral latency chip when latency is unmeasured — never a green pass', () => {
|
|
123
|
+
const promo = {
|
|
124
|
+
...basePromotion,
|
|
125
|
+
latencyMeasured: false,
|
|
126
|
+
};
|
|
127
|
+
(0, react_1.render)(<RoutingInsightsPanel_1.default data={makeData(promo)}/>);
|
|
128
|
+
(0, vitest_1.expect)(react_1.screen.getByText('○ neutral')).toBeTruthy();
|
|
129
|
+
(0, vitest_1.expect)(react_1.screen.getByText('no latency measurements yet')).toBeTruthy();
|
|
130
|
+
// Measured criteria (quality + cost) still show green — only latency is neutral
|
|
131
|
+
(0, vitest_1.expect)(react_1.screen.getAllByText('✓ pass')).toHaveLength(2);
|
|
132
|
+
(0, vitest_1.expect)(react_1.screen.getAllByText('○ neutral')).toHaveLength(1);
|
|
133
|
+
(0, vitest_1.expect)(react_1.screen.queryByText('✗ fail')).toBeNull();
|
|
134
|
+
});
|
|
135
|
+
(0, vitest_1.it)('hides the promotion card entirely when there are no decisions yet', () => {
|
|
136
|
+
(0, react_1.render)(<RoutingInsightsPanel_1.default data={makeData({ ...basePromotion, decisionCount: 0 })}/>);
|
|
137
|
+
(0, vitest_1.expect)(react_1.screen.queryByText('Promotion Gate — is the bandit better than the heuristic?')).toBeNull();
|
|
138
|
+
});
|
|
139
|
+
(0, vitest_1.it)('renders nothing promotion-related when routing data is absent', () => {
|
|
140
|
+
(0, react_1.render)(<RoutingInsightsPanel_1.default data={null}/>);
|
|
141
|
+
(0, vitest_1.expect)(react_1.screen.queryByText('Promotion Gate — is the bandit better than the heuristic?')).toBeNull();
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
// ─── v1.58.0 M2.x chips on the preference table ─────────────────────────────
|
|
145
|
+
(0, vitest_1.describe)('PreferenceSection M2.x chips (v1.58.0)', () => {
|
|
146
|
+
(0, vitest_1.it)('renders 🎯 fit / 📏 measured / ⏳ ctx chips on provider rows when data is present', () => {
|
|
147
|
+
(0, react_1.render)(<RoutingInsightsPanel_1.default data={makePreferenceData({
|
|
148
|
+
provider: 'gemini',
|
|
149
|
+
score: 0.87,
|
|
150
|
+
reason: 'strong reasoning',
|
|
151
|
+
capabilityFit: 85,
|
|
152
|
+
costSource: 'measured',
|
|
153
|
+
costBasis: { inputTokens: 12480, outputTokens: 3110 },
|
|
154
|
+
contextUtilization: 3,
|
|
155
|
+
contextWindowTokens: 1048576,
|
|
156
|
+
})}/>);
|
|
157
|
+
(0, vitest_1.expect)(react_1.screen.getByText('Auto Router — What the agent would pick')).toBeTruthy();
|
|
158
|
+
(0, vitest_1.expect)(react_1.screen.getByText('🎯 fit 85%')).toBeTruthy();
|
|
159
|
+
(0, vitest_1.expect)(react_1.screen.getByText('📏 measured 12,480→3,110 tok')).toBeTruthy();
|
|
160
|
+
(0, vitest_1.expect)(react_1.screen.getByText('⏳ ctx 3% (1,048,576 tok)')).toBeTruthy();
|
|
161
|
+
});
|
|
162
|
+
(0, vitest_1.it)('shows 📐 estimated when no measured wire usage exists and omits 🎯/⏳ chips when their fields are absent (gates OFF)', () => {
|
|
163
|
+
(0, react_1.render)(<RoutingInsightsPanel_1.default data={makePreferenceData({
|
|
164
|
+
provider: 'groq',
|
|
165
|
+
score: 0.7,
|
|
166
|
+
reason: 'fast + free',
|
|
167
|
+
// costSource present (always sent by the server); capabilityFit / context
|
|
168
|
+
// fields intentionally absent → gates OFF leaves only the cost chip.
|
|
169
|
+
costSource: 'estimated',
|
|
170
|
+
})}/>);
|
|
171
|
+
(0, vitest_1.expect)(react_1.screen.getByText('📐 estimated')).toBeTruthy();
|
|
172
|
+
(0, vitest_1.expect)(react_1.screen.queryByText(/🎯 fit/)).toBeNull();
|
|
173
|
+
(0, vitest_1.expect)(react_1.screen.queryByText(/⏳ ctx/)).toBeNull();
|
|
174
|
+
(0, vitest_1.expect)(react_1.screen.queryByText(/📏 measured/)).toBeNull();
|
|
175
|
+
});
|
|
176
|
+
(0, vitest_1.it)('renders no chip row at all when every chip field is absent (hand-built data)', () => {
|
|
177
|
+
(0, react_1.render)(<RoutingInsightsPanel_1.default data={makePreferenceData({
|
|
178
|
+
provider: 'groq',
|
|
179
|
+
score: 0.7,
|
|
180
|
+
reason: 'fast + free',
|
|
181
|
+
})}/>);
|
|
182
|
+
(0, vitest_1.expect)(react_1.screen.queryByText(/📐 estimated/)).toBeNull();
|
|
183
|
+
(0, vitest_1.expect)(react_1.screen.queryByText(/📏 measured/)).toBeNull();
|
|
184
|
+
(0, vitest_1.expect)(react_1.screen.queryByText(/🎯 fit/)).toBeNull();
|
|
185
|
+
(0, vitest_1.expect)(react_1.screen.queryByText(/⏳ ctx/)).toBeNull();
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
// Build routing data with a single preference entry whose providers carry the
|
|
189
|
+
// given (partial) chip fields.
|
|
190
|
+
function makePreferenceData(prov) {
|
|
191
|
+
return {
|
|
192
|
+
routing: {
|
|
193
|
+
providers: [],
|
|
194
|
+
bestModels: [],
|
|
195
|
+
preference: [{
|
|
196
|
+
complexity: 'moderate',
|
|
197
|
+
winner: `${prov.provider || 'gemini'}/${prov.provider || 'gemini'}-model`,
|
|
198
|
+
score: prov.score ?? 0.87,
|
|
199
|
+
providers: [{
|
|
200
|
+
provider: prov.provider || 'gemini',
|
|
201
|
+
score: prov.score ?? 0.87,
|
|
202
|
+
reason: prov.reason || 'reason',
|
|
203
|
+
...(prov.capabilityFit !== undefined ? { capabilityFit: prov.capabilityFit } : {}),
|
|
204
|
+
...(prov.costSource ? { costSource: prov.costSource } : {}),
|
|
205
|
+
...(prov.costBasis ? { costBasis: prov.costBasis } : {}),
|
|
206
|
+
...(prov.contextUtilization !== undefined ? { contextUtilization: prov.contextUtilization } : {}),
|
|
207
|
+
...(prov.contextWindowTokens !== undefined ? { contextWindowTokens: prov.contextWindowTokens } : {}),
|
|
208
|
+
}],
|
|
209
|
+
}],
|
|
210
|
+
updatedAt: Date.now(),
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
// ─── Governance card (P6 M6.5) ─────────────────────────────────────────────
|
|
215
|
+
// The policy card mirrors `buff admin policy`: permissive empty state, rule
|
|
216
|
+
// chips for allow/deny lists + caps, and the hard-constraint enforcement note.
|
|
217
|
+
(0, vitest_1.describe)('GovernanceSection (via RoutingInsightsPanel)', () => {
|
|
218
|
+
(0, vitest_1.it)('shows the fully-permissive state when no policy is configured', () => {
|
|
219
|
+
(0, react_1.render)(<RoutingInsightsPanel_1.default data={makeGovernanceData({ enabled: false, updatedAt: Date.now() })}/>);
|
|
220
|
+
(0, vitest_1.expect)(react_1.screen.getByText(/Fully permissive/)).toBeTruthy();
|
|
221
|
+
(0, vitest_1.expect)(react_1.screen.getByText(/buff admin allow/)).toBeTruthy();
|
|
222
|
+
(0, vitest_1.expect)(react_1.screen.getByText(/Admin Governance Policy/)).toBeTruthy();
|
|
223
|
+
});
|
|
224
|
+
(0, vitest_1.it)('renders allow/deny rule chips and cap extras when a policy is active', () => {
|
|
225
|
+
(0, react_1.render)(<RoutingInsightsPanel_1.default data={makeGovernanceData({
|
|
226
|
+
enabled: true,
|
|
227
|
+
allowProviders: ['groq', 'local'],
|
|
228
|
+
denyProviders: ['gemini'],
|
|
229
|
+
denyModels: ['gemini-2.5-pro'],
|
|
230
|
+
maxCostUsd: 0.01,
|
|
231
|
+
piiPatterns: ['api[_-]?key'],
|
|
232
|
+
allowUnblock: false,
|
|
233
|
+
updatedAt: Date.now(),
|
|
234
|
+
})}/>);
|
|
235
|
+
(0, vitest_1.expect)(react_1.screen.getByText(/allow providers: groq, local/)).toBeTruthy();
|
|
236
|
+
(0, vitest_1.expect)(react_1.screen.getByText(/deny providers: gemini/)).toBeTruthy();
|
|
237
|
+
(0, vitest_1.expect)(react_1.screen.getByText(/deny models: gemini-2.5-pro/)).toBeTruthy();
|
|
238
|
+
(0, vitest_1.expect)(react_1.screen.getByText(/max cost \$0.01/)).toBeTruthy();
|
|
239
|
+
(0, vitest_1.expect)(react_1.screen.getByText(/PII guard/)).toBeTruthy();
|
|
240
|
+
(0, vitest_1.expect)(react_1.screen.getByText(/unblock admin-hard/)).toBeTruthy();
|
|
241
|
+
// Hard-constraint enforcement note.
|
|
242
|
+
(0, vitest_1.expect)(react_1.screen.getByText(/violating providers are eliminated/)).toBeTruthy();
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
// ─── RBAC identity card (P6 M6.1) ──────────────────────────────────────────
|
|
246
|
+
function makeRbacData(rbac) {
|
|
247
|
+
return {
|
|
248
|
+
routing: {
|
|
249
|
+
providers: [],
|
|
250
|
+
bestModels: [],
|
|
251
|
+
preference: [],
|
|
252
|
+
rbac,
|
|
253
|
+
updatedAt: Date.now(),
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
(0, vitest_1.describe)('RbacSection (P6 M6.1)', () => {
|
|
258
|
+
(0, vitest_1.it)('renders the acting identity, role, and user→role map', () => {
|
|
259
|
+
(0, react_1.render)(<RoutingInsightsPanel_1.RbacSection rbac={{
|
|
260
|
+
legacy: false,
|
|
261
|
+
identity: 'alice',
|
|
262
|
+
role: 'admin',
|
|
263
|
+
users: [
|
|
264
|
+
{ user: 'alice', role: 'admin' },
|
|
265
|
+
{ user: 'bob', role: 'viewer' },
|
|
266
|
+
],
|
|
267
|
+
updatedAt: Date.now(),
|
|
268
|
+
}}/>);
|
|
269
|
+
(0, vitest_1.expect)(react_1.screen.getByText(/You are/)).toBeTruthy();
|
|
270
|
+
(0, vitest_1.expect)(react_1.screen.getByText('alice')).toBeTruthy();
|
|
271
|
+
// 'admin' appears in the role badge AND the "writes require admin" hint.
|
|
272
|
+
(0, vitest_1.expect)(react_1.screen.getAllByText('admin').length).toBeGreaterThan(0);
|
|
273
|
+
(0, vitest_1.expect)(react_1.screen.getByText(/bob · viewer/)).toBeTruthy();
|
|
274
|
+
// The em/code tags split the hint text into separate nodes — match on textContent.
|
|
275
|
+
(0, vitest_1.expect)(react_1.screen.getAllByText((_, el) => !!el?.textContent?.includes('Policy writes require admin')).length).toBeGreaterThan(0);
|
|
276
|
+
});
|
|
277
|
+
(0, vitest_1.it)('renders the legacy permissive notice when no roles are assigned', () => {
|
|
278
|
+
(0, react_1.render)(<RoutingInsightsPanel_1.RbacSection rbac={{ legacy: true, identity: 'dheeraj', role: null, users: [], updatedAt: Date.now() }}/>);
|
|
279
|
+
(0, vitest_1.expect)(react_1.screen.getByText(/Legacy single-user mode/)).toBeTruthy();
|
|
280
|
+
(0, vitest_1.expect)(react_1.screen.getByText(/policy writes are/)).toBeTruthy();
|
|
281
|
+
});
|
|
282
|
+
(0, vitest_1.it)('renders an unassigned role honestly (null role)', () => {
|
|
283
|
+
(0, react_1.render)(<RoutingInsightsPanel_1.RbacSection rbac={{ legacy: false, identity: 'mallory', role: null, users: [{ user: 'alice', role: 'admin' }], updatedAt: Date.now() }}/>);
|
|
284
|
+
(0, vitest_1.expect)(react_1.screen.getByText(/unassigned/)).toBeTruthy();
|
|
285
|
+
});
|
|
286
|
+
(0, vitest_1.it)('renders through the panel when routing.rbac is present', () => {
|
|
287
|
+
(0, react_1.render)(<RoutingInsightsPanel_1.default data={makeRbacData({
|
|
288
|
+
legacy: false,
|
|
289
|
+
identity: 'alice',
|
|
290
|
+
role: 'admin',
|
|
291
|
+
users: [{ user: 'alice', role: 'admin' }],
|
|
292
|
+
updatedAt: Date.now(),
|
|
293
|
+
})}/>);
|
|
294
|
+
(0, vitest_1.expect)(react_1.screen.getByText(/RBAC Identity/)).toBeTruthy();
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
// ─── v1.71.0 ML task-similarity router card ─────────────────────────────────
|
|
298
|
+
(0, vitest_1.describe)('MlSection (via RoutingInsightsPanel)', () => {
|
|
299
|
+
function makeMlData(ml) {
|
|
300
|
+
return {
|
|
301
|
+
routing: {
|
|
302
|
+
providers: [],
|
|
303
|
+
bestModels: [],
|
|
304
|
+
preference: [],
|
|
305
|
+
ml,
|
|
306
|
+
updatedAt: Date.now(),
|
|
307
|
+
},
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
(0, vitest_1.it)('renders learned state — record count, trusted vs learning providers, win rates', () => {
|
|
311
|
+
const ml = {
|
|
312
|
+
enabled: true,
|
|
313
|
+
recordCount: 42,
|
|
314
|
+
providers: [
|
|
315
|
+
{ provider: 'groq', samples: 18, winRate: 0.85, factor: 1.175, trusted: true, model: 'llama-3.3-70b' },
|
|
316
|
+
{ provider: 'gemini', samples: 3, winRate: 0.4, factor: 0.95, trusted: false, model: 'gemini-2.0-flash' },
|
|
317
|
+
],
|
|
318
|
+
updatedAt: Date.now(),
|
|
319
|
+
};
|
|
320
|
+
(0, react_1.render)(<RoutingInsightsPanel_1.default data={makeMlData(ml)}/>);
|
|
321
|
+
(0, vitest_1.expect)(react_1.screen.getByText('ML Router — learned from similar tasks')).toBeTruthy();
|
|
322
|
+
(0, vitest_1.expect)(react_1.screen.getByText('42')).toBeTruthy();
|
|
323
|
+
(0, vitest_1.expect)(react_1.screen.getByText('learned tasks')).toBeTruthy();
|
|
324
|
+
(0, vitest_1.expect)(react_1.screen.getByText('trusted providers')).toBeTruthy();
|
|
325
|
+
(0, vitest_1.expect)(react_1.screen.getByText('still learning')).toBeTruthy();
|
|
326
|
+
(0, vitest_1.expect)(react_1.screen.getByText(/trusted · 18 samples/)).toBeTruthy();
|
|
327
|
+
(0, vitest_1.expect)(react_1.screen.getByText(/3\/5 samples/)).toBeTruthy();
|
|
328
|
+
(0, vitest_1.expect)(react_1.screen.getByText(/85%/)).toBeTruthy();
|
|
329
|
+
(0, vitest_1.expect)(react_1.screen.getByText(/1\.1\d×/)).toBeTruthy(); // 0.85 winRate → factor 1.175 → "1.18×"
|
|
330
|
+
});
|
|
331
|
+
(0, vitest_1.it)('hides the ML card entirely when there are no learned records', () => {
|
|
332
|
+
(0, react_1.render)(<RoutingInsightsPanel_1.default data={makeMlData({ enabled: false, recordCount: 0, providers: [], updatedAt: Date.now() })}/>);
|
|
333
|
+
(0, vitest_1.expect)(react_1.screen.queryByText('ML Router — learned from similar tasks')).toBeNull();
|
|
334
|
+
});
|
|
335
|
+
(0, vitest_1.it)('hides the ML card when the server predates the feature (no ml field)', () => {
|
|
336
|
+
(0, react_1.render)(<RoutingInsightsPanel_1.default data={makeData(undefined)}/>);
|
|
337
|
+
(0, vitest_1.expect)(react_1.screen.queryByText('ML Router — learned from similar tasks')).toBeNull();
|
|
338
|
+
});
|
|
339
|
+
});
|