updates on the hook and endpoints for the ai agents

This commit is contained in:
2026-09-30 15:08:33 +05:30
parent ede1731fa5
commit d33fff01d3
5 changed files with 180 additions and 38 deletions

View File

@@ -25,6 +25,7 @@ jest.mock('@/api/doormile', () => ({
createAiSkill: jest.fn(),
updateAiAgent: jest.fn(),
getAiInsights: jest.fn(),
getAiStatus: jest.fn(),
getAiDecisions: jest.fn(),
runAiPlayground: jest.fn(),
}));
@@ -148,6 +149,7 @@ beforeEach(() => {
api.updateAiAgent.mockResolvedValue({ success: true });
api.getAiInsights.mockResolvedValue(INSIGHTS);
api.getAiDecisions.mockResolvedValue(DECISIONS);
api.getAiStatus.mockResolvedValue({ engine: { readingsettings: false, lastreadat: null }, playground: { configured: false } });
});
// ── Adapters ────────────────────────────────────────────────────────────────
@@ -184,18 +186,36 @@ describe('registry adapters', () => {
// ── The page ────────────────────────────────────────────────────────────────
describe('Agent Studio page', () => {
it('says on screen which agents the registry drives, and when', async () => {
it('says "All connected" only when the engine reads the registry and the Test tab has a model', async () => {
api.getAiStatus.mockResolvedValue({
engine: { readingsettings: true, lastreadat: new Date().toISOString(), telemetry: true, liveagents: 4 },
playground: { configured: true, model: 'openai/gpt-oss-120b' },
});
renderStudio();
const note = screen.getByRole('note');
expect(note).toHaveTextContent(/saved to the Doormile agent registry/);
// The engine reads it only once its Phase 5 build is deployed; say so, not "live".
expect(note).toHaveTextContent(/once its registry-reading build is deployed/);
expect(note).toHaveTextContent(/environment defaults/);
expect(note).toHaveTextContent(/The Test tab runs real AI model calls/);
expect(note).not.toHaveTextContent(/The Test tab is a simulation/);
// Since Phase 3 the console skills DO run on these settings; the note must not say otherwise.
expect(note).toHaveTextContent(/run on these settings now/);
expect(note).not.toHaveTextContent(/not merged/);
const note = await screen.findByText('All connected.');
const banner = note.closest('[role="note"]');
expect(banner).toHaveTextContent(/AI_engine is following these settings/);
expect(banner).toHaveTextContent(/The Test tab is live \(model openai\/gpt-oss-120b\)/);
expect(banner).not.toHaveTextContent(/Half wired/);
});
it('says what is missing when the engine has not read the registry', async () => {
api.getAiStatus.mockResolvedValue({
engine: { readingsettings: false, lastreadat: null, telemetry: false, liveagents: 0 },
playground: { configured: false },
});
renderStudio();
const banner = (await screen.findByText('Partly connected.')).closest('[role="note"]');
expect(banner).toHaveTextContent(/AI_engine has not read these settings yet/);
expect(banner).toHaveTextContent(/environment defaults/);
expect(banner).toHaveTextContent(/Test tab is off/);
expect(banner).toHaveTextContent(/saved to the Doormile agent registry/);
});
it('says the backend needs deploying when it has no status endpoint', async () => {
api.getAiStatus.mockRejectedValue({ response: { status: 404 } });
renderStudio();
expect(await screen.findByText(/does not report agent status yet/)).toBeInTheDocument();
});
it("shows only the selected agent's skills", async () => {
@@ -257,7 +277,7 @@ describe('Agent Studio page', () => {
renderStudio();
await screen.findAllByText('Stalled-rider response');
await switchTo('Console Ops Agent');
fireEvent.click(await screen.findByText('New skill'));
fireEvent.click(await screen.findByText(/^New skill$/i));
fireEvent.change(screen.getByLabelText('Skill Title *'), { target: { value: 'Night shift watch' } });
fireEvent.change(screen.getByLabelText('Description *'), { target: { value: 'Watch the night shift.' } });
@@ -281,7 +301,7 @@ describe('Agent Studio page', () => {
it('does not offer a new skill on an AI_engine agent', async () => {
renderStudio();
await screen.findAllByText('Stalled-rider response');
expect(screen.getByText('New skill').closest('button')).toBeDisabled();
expect(screen.getByText(/^New skill$/i).closest('button')).toBeDisabled();
});
it('needs the agent id typed before switching autonomy on', async () => {
@@ -319,7 +339,7 @@ describe('Agent Studio page', () => {
renderStudio();
await screen.findAllByText('Stalled-rider response');
expect(screen.getByText('You can view the registry. Only an admin can change it.')).toBeInTheDocument();
expect(screen.getByText('New skill').closest('button')).toBeDisabled();
expect(screen.getByText(/^New skill$/i).closest('button')).toBeDisabled();
fireEvent.click(screen.getByText('Details'));
expect(screen.getByRole('switch', { name: 'Stalled-rider response enabled' })).toBeDisabled();
@@ -331,12 +351,16 @@ describe('Agent Studio page', () => {
renderStudio();
await screen.findAllByText('Stalled-rider response');
fireEvent.click(screen.getAllByText('Tools')[0]);
const row = screen.getAllByText('nearby_milers').map((el) => el.closest('tr')).find(Boolean);
expect(within(row).getByText('Read-only')).toBeInTheDocument();
expect(within(row).getByText('No confirmation needed')).toBeInTheDocument();
const gated = screen.getAllByText('reassign_booking').map((el) => el.closest('tr')).find(Boolean);
expect(within(gated).getByText('Writes')).toBeInTheDocument();
expect(within(gated).getByText('Confirmation Required')).toBeInTheDocument();
// Tools render as cards (a header button per tool): kind + safety badges.
// The name also appears in the agent navigator; the tool card is the button carrying its kind badge.
const toolCard = (name) =>
screen.getAllByText(name).map((el) => el.closest('button')).find((btn) => btn && within(btn).queryByText(/^(Read|Write|Notify|General)$/));
const card = toolCard('nearby_milers');
expect(within(card).getByText('Read')).toBeInTheDocument();
expect(within(card).getByText('Open')).toBeInTheDocument();
const gated = toolCard('reassign_booking');
expect(within(gated).getByText('Write')).toBeInTheDocument();
expect(within(gated).getByText('Gated')).toBeInTheDocument();
expect(screen.queryByText('Autonomous')).not.toBeInTheDocument();
});
@@ -406,7 +430,8 @@ describe('Agent Studio Test tab', () => {
const openTest = async () => {
renderStudio();
await screen.findAllByText('Stalled-rider response');
fireEvent.click(screen.getByText('Test'));
// The tab row renders before the skill cards, whose buttons also say Test.
fireEvent.click(screen.getAllByText('Test')[0]);
};
it('runs the prompt on the backend and shows its real trace', async () => {