feat: Add intelligent auto-router and enhanced integrations
- Add intelligent-router.sh hook for automatic agent routing - Add AUTO-TRIGGER-SUMMARY.md documentation - Add FINAL-INTEGRATION-SUMMARY.md documentation - Complete Prometheus integration (6 commands + 4 tools) - Complete Dexto integration (12 commands + 5 tools) - Enhanced Ralph with access to all agents - Fix /clawd command (removed disable-model-invocation) - Update hooks.json to v5 with intelligent routing - 291 total skills now available - All 21 commands with automatic routing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
331
dexto/packages/webui/lib/stores/approvalStore.test.ts
Normal file
331
dexto/packages/webui/lib/stores/approvalStore.test.ts
Normal file
@@ -0,0 +1,331 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { useApprovalStore } from './approvalStore.js';
|
||||
import type { ApprovalRequest, ApprovalResponse } from '@dexto/core';
|
||||
import { ApprovalType, ApprovalStatus } from '@dexto/core';
|
||||
|
||||
// Helper to create test approval requests
|
||||
function createTestApprovalRequest(overrides: Partial<ApprovalRequest> = {}): ApprovalRequest {
|
||||
return {
|
||||
approvalId: `approval-${Math.random().toString(36).slice(2, 11)}`,
|
||||
type: ApprovalType.TOOL_CONFIRMATION,
|
||||
sessionId: 'test-session',
|
||||
timeout: 30000,
|
||||
timestamp: new Date(),
|
||||
metadata: {
|
||||
toolName: 'test_tool',
|
||||
args: {},
|
||||
},
|
||||
...overrides,
|
||||
} as ApprovalRequest;
|
||||
}
|
||||
|
||||
// Helper to create test approval responses
|
||||
function createTestApprovalResponse(
|
||||
approvalId: string,
|
||||
status: ApprovalStatus,
|
||||
overrides: Partial<ApprovalResponse> = {}
|
||||
): ApprovalResponse {
|
||||
return {
|
||||
approvalId,
|
||||
status,
|
||||
sessionId: 'test-session',
|
||||
...overrides,
|
||||
} as ApprovalResponse;
|
||||
}
|
||||
|
||||
describe('approvalStore', () => {
|
||||
beforeEach(() => {
|
||||
// Reset store to default state
|
||||
useApprovalStore.setState({
|
||||
pendingApproval: null,
|
||||
queue: [],
|
||||
});
|
||||
});
|
||||
|
||||
describe('addApproval', () => {
|
||||
it('should set pendingApproval when empty', () => {
|
||||
const request = createTestApprovalRequest();
|
||||
useApprovalStore.getState().addApproval(request);
|
||||
|
||||
expect(useApprovalStore.getState().pendingApproval).toEqual(request);
|
||||
expect(useApprovalStore.getState().queue).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should queue when pendingApproval exists', () => {
|
||||
const request1 = createTestApprovalRequest();
|
||||
const request2 = createTestApprovalRequest();
|
||||
|
||||
useApprovalStore.getState().addApproval(request1);
|
||||
useApprovalStore.getState().addApproval(request2);
|
||||
|
||||
expect(useApprovalStore.getState().pendingApproval).toEqual(request1);
|
||||
expect(useApprovalStore.getState().queue).toHaveLength(1);
|
||||
expect(useApprovalStore.getState().queue[0]).toEqual(request2);
|
||||
});
|
||||
|
||||
it('should queue multiple requests in order', () => {
|
||||
const request1 = createTestApprovalRequest();
|
||||
const request2 = createTestApprovalRequest();
|
||||
const request3 = createTestApprovalRequest();
|
||||
|
||||
useApprovalStore.getState().addApproval(request1);
|
||||
useApprovalStore.getState().addApproval(request2);
|
||||
useApprovalStore.getState().addApproval(request3);
|
||||
|
||||
expect(useApprovalStore.getState().pendingApproval).toEqual(request1);
|
||||
expect(useApprovalStore.getState().queue).toHaveLength(2);
|
||||
expect(useApprovalStore.getState().queue[0]).toEqual(request2);
|
||||
expect(useApprovalStore.getState().queue[1]).toEqual(request3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('processResponse', () => {
|
||||
it('should clear pendingApproval for approved status', () => {
|
||||
const request = createTestApprovalRequest();
|
||||
useApprovalStore.getState().addApproval(request);
|
||||
|
||||
const response = createTestApprovalResponse(
|
||||
request.approvalId,
|
||||
ApprovalStatus.APPROVED
|
||||
);
|
||||
useApprovalStore.getState().processResponse(response);
|
||||
|
||||
expect(useApprovalStore.getState().pendingApproval).toBeNull();
|
||||
});
|
||||
|
||||
it('should clear pendingApproval for denied status', () => {
|
||||
const request = createTestApprovalRequest();
|
||||
useApprovalStore.getState().addApproval(request);
|
||||
|
||||
const response = createTestApprovalResponse(request.approvalId, ApprovalStatus.DENIED);
|
||||
useApprovalStore.getState().processResponse(response);
|
||||
|
||||
expect(useApprovalStore.getState().pendingApproval).toBeNull();
|
||||
});
|
||||
|
||||
it('should clear pendingApproval for cancelled status', () => {
|
||||
const request = createTestApprovalRequest();
|
||||
useApprovalStore.getState().addApproval(request);
|
||||
|
||||
const response = createTestApprovalResponse(
|
||||
request.approvalId,
|
||||
ApprovalStatus.CANCELLED
|
||||
);
|
||||
useApprovalStore.getState().processResponse(response);
|
||||
|
||||
expect(useApprovalStore.getState().pendingApproval).toBeNull();
|
||||
});
|
||||
|
||||
it('should process next in queue after terminal status', () => {
|
||||
const request1 = createTestApprovalRequest();
|
||||
const request2 = createTestApprovalRequest();
|
||||
|
||||
useApprovalStore.getState().addApproval(request1);
|
||||
useApprovalStore.getState().addApproval(request2);
|
||||
|
||||
const response = createTestApprovalResponse(
|
||||
request1.approvalId,
|
||||
ApprovalStatus.APPROVED
|
||||
);
|
||||
useApprovalStore.getState().processResponse(response);
|
||||
|
||||
expect(useApprovalStore.getState().pendingApproval).toEqual(request2);
|
||||
expect(useApprovalStore.getState().queue).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should handle multiple queued items', () => {
|
||||
const request1 = createTestApprovalRequest();
|
||||
const request2 = createTestApprovalRequest();
|
||||
const request3 = createTestApprovalRequest();
|
||||
|
||||
useApprovalStore.getState().addApproval(request1);
|
||||
useApprovalStore.getState().addApproval(request2);
|
||||
useApprovalStore.getState().addApproval(request3);
|
||||
|
||||
// Process first
|
||||
const response1 = createTestApprovalResponse(
|
||||
request1.approvalId,
|
||||
ApprovalStatus.APPROVED
|
||||
);
|
||||
useApprovalStore.getState().processResponse(response1);
|
||||
|
||||
expect(useApprovalStore.getState().pendingApproval).toEqual(request2);
|
||||
expect(useApprovalStore.getState().queue).toHaveLength(1);
|
||||
expect(useApprovalStore.getState().queue[0]).toEqual(request3);
|
||||
|
||||
// Process second
|
||||
const response2 = createTestApprovalResponse(
|
||||
request2.approvalId,
|
||||
ApprovalStatus.DENIED
|
||||
);
|
||||
useApprovalStore.getState().processResponse(response2);
|
||||
|
||||
expect(useApprovalStore.getState().pendingApproval).toEqual(request3);
|
||||
expect(useApprovalStore.getState().queue).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should not process response for mismatched approvalId', () => {
|
||||
const request = createTestApprovalRequest();
|
||||
useApprovalStore.getState().addApproval(request);
|
||||
|
||||
const response = createTestApprovalResponse('wrong-id', ApprovalStatus.APPROVED);
|
||||
useApprovalStore.getState().processResponse(response);
|
||||
|
||||
expect(useApprovalStore.getState().pendingApproval).toEqual(request);
|
||||
});
|
||||
|
||||
it('should handle response when no pending approval', () => {
|
||||
const response = createTestApprovalResponse('some-id', ApprovalStatus.APPROVED);
|
||||
useApprovalStore.getState().processResponse(response);
|
||||
|
||||
expect(useApprovalStore.getState().pendingApproval).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('clearApproval', () => {
|
||||
it('should clear current and process next in queue', () => {
|
||||
const request1 = createTestApprovalRequest();
|
||||
const request2 = createTestApprovalRequest();
|
||||
|
||||
useApprovalStore.getState().addApproval(request1);
|
||||
useApprovalStore.getState().addApproval(request2);
|
||||
|
||||
useApprovalStore.getState().clearApproval();
|
||||
|
||||
expect(useApprovalStore.getState().pendingApproval).toEqual(request2);
|
||||
expect(useApprovalStore.getState().queue).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should set pendingApproval to null when queue is empty', () => {
|
||||
const request = createTestApprovalRequest();
|
||||
useApprovalStore.getState().addApproval(request);
|
||||
|
||||
useApprovalStore.getState().clearApproval();
|
||||
|
||||
expect(useApprovalStore.getState().pendingApproval).toBeNull();
|
||||
});
|
||||
|
||||
it('should handle clearApproval when nothing is pending', () => {
|
||||
useApprovalStore.getState().clearApproval();
|
||||
|
||||
expect(useApprovalStore.getState().pendingApproval).toBeNull();
|
||||
expect(useApprovalStore.getState().queue).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('clearAll', () => {
|
||||
it('should clear everything', () => {
|
||||
const request1 = createTestApprovalRequest();
|
||||
const request2 = createTestApprovalRequest();
|
||||
const request3 = createTestApprovalRequest();
|
||||
|
||||
useApprovalStore.getState().addApproval(request1);
|
||||
useApprovalStore.getState().addApproval(request2);
|
||||
useApprovalStore.getState().addApproval(request3);
|
||||
|
||||
useApprovalStore.getState().clearAll();
|
||||
|
||||
expect(useApprovalStore.getState().pendingApproval).toBeNull();
|
||||
expect(useApprovalStore.getState().queue).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('selectors', () => {
|
||||
describe('getPendingCount', () => {
|
||||
it('should return 0 when nothing is pending', () => {
|
||||
expect(useApprovalStore.getState().getPendingCount()).toBe(0);
|
||||
});
|
||||
|
||||
it('should return 1 when only pendingApproval exists', () => {
|
||||
const request = createTestApprovalRequest();
|
||||
useApprovalStore.getState().addApproval(request);
|
||||
|
||||
expect(useApprovalStore.getState().getPendingCount()).toBe(1);
|
||||
});
|
||||
|
||||
it('should return correct count with queue', () => {
|
||||
const request1 = createTestApprovalRequest();
|
||||
const request2 = createTestApprovalRequest();
|
||||
const request3 = createTestApprovalRequest();
|
||||
|
||||
useApprovalStore.getState().addApproval(request1);
|
||||
useApprovalStore.getState().addApproval(request2);
|
||||
useApprovalStore.getState().addApproval(request3);
|
||||
|
||||
expect(useApprovalStore.getState().getPendingCount()).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getPendingForSession', () => {
|
||||
it('should return empty array for session with no approvals', () => {
|
||||
const result = useApprovalStore.getState().getPendingForSession('session-1');
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should return only approvals for specified session', () => {
|
||||
const request1 = createTestApprovalRequest({ sessionId: 'session-1' });
|
||||
const request2 = createTestApprovalRequest({ sessionId: 'session-2' });
|
||||
const request3 = createTestApprovalRequest({ sessionId: 'session-1' });
|
||||
|
||||
useApprovalStore.getState().addApproval(request1);
|
||||
useApprovalStore.getState().addApproval(request2);
|
||||
useApprovalStore.getState().addApproval(request3);
|
||||
|
||||
const result = useApprovalStore.getState().getPendingForSession('session-1');
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0]).toEqual(request1);
|
||||
expect(result[1]).toEqual(request3);
|
||||
});
|
||||
|
||||
it('should include both pending and queued for session', () => {
|
||||
const request1 = createTestApprovalRequest({ sessionId: 'session-1' });
|
||||
const request2 = createTestApprovalRequest({ sessionId: 'session-1' });
|
||||
|
||||
useApprovalStore.getState().addApproval(request1);
|
||||
useApprovalStore.getState().addApproval(request2);
|
||||
|
||||
const result = useApprovalStore.getState().getPendingForSession('session-1');
|
||||
expect(result).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasPendingApproval', () => {
|
||||
it('should return false when nothing is pending', () => {
|
||||
expect(useApprovalStore.getState().hasPendingApproval()).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true when approval is pending', () => {
|
||||
const request = createTestApprovalRequest();
|
||||
useApprovalStore.getState().addApproval(request);
|
||||
|
||||
expect(useApprovalStore.getState().hasPendingApproval()).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false after clearing', () => {
|
||||
const request = createTestApprovalRequest();
|
||||
useApprovalStore.getState().addApproval(request);
|
||||
useApprovalStore.getState().clearApproval();
|
||||
|
||||
expect(useApprovalStore.getState().hasPendingApproval()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('session isolation', () => {
|
||||
it('should keep different sessions separate in queue', () => {
|
||||
const session1Request1 = createTestApprovalRequest({ sessionId: 'session-1' });
|
||||
const session2Request1 = createTestApprovalRequest({ sessionId: 'session-2' });
|
||||
const session1Request2 = createTestApprovalRequest({ sessionId: 'session-1' });
|
||||
|
||||
useApprovalStore.getState().addApproval(session1Request1);
|
||||
useApprovalStore.getState().addApproval(session2Request1);
|
||||
useApprovalStore.getState().addApproval(session1Request2);
|
||||
|
||||
const session1Pending = useApprovalStore.getState().getPendingForSession('session-1');
|
||||
const session2Pending = useApprovalStore.getState().getPendingForSession('session-2');
|
||||
|
||||
expect(session1Pending).toHaveLength(2);
|
||||
expect(session2Pending).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user