feat: add Help page with search, streamline copilot, misc UI fixes
Adds searchable Help/User Guide page, trims copilot tool bloat, adds OTHER department option, and various form/layout improvements. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
-- AlterEnum
|
||||
ALTER TYPE "Department" ADD VALUE 'OTHER';
|
||||
@@ -139,6 +139,10 @@ async function main() {
|
||||
airportPickup: true,
|
||||
venueTransport: true,
|
||||
partySize: 3, // Roger + 2 handlers
|
||||
phone: '+1 (202) 555-0140',
|
||||
email: 'roger.mosby@scouting.org',
|
||||
emergencyContactName: 'Linda Mosby',
|
||||
emergencyContactPhone: '+1 (202) 555-0141',
|
||||
notes: 'Chief Scout Executive. Travels with 2 staff handlers. Requires accessible vehicle.',
|
||||
flights: {
|
||||
create: [
|
||||
@@ -167,6 +171,10 @@ async function main() {
|
||||
airportPickup: true,
|
||||
venueTransport: true,
|
||||
partySize: 2, // Patricia + spouse
|
||||
phone: '+1 (404) 555-0230',
|
||||
email: 'patricia.hawkins@bsaboard.org',
|
||||
emergencyContactName: 'Richard Hawkins',
|
||||
emergencyContactPhone: '+1 (404) 555-0231',
|
||||
notes: 'National Board Chair. Traveling with husband (Richard). Both attend all events.',
|
||||
flights: {
|
||||
create: [
|
||||
@@ -195,6 +203,10 @@ async function main() {
|
||||
airportPickup: true,
|
||||
venueTransport: true,
|
||||
partySize: 1, // Solo
|
||||
phone: '+1 (214) 555-0375',
|
||||
email: 'jwhitfield@whitfieldfoundation.org',
|
||||
emergencyContactName: 'Catherine Whitfield',
|
||||
emergencyContactPhone: '+1 (214) 555-0376',
|
||||
notes: 'Major donor ($2M+). Eagle Scout class of 1978. Very punctual — do not be late.',
|
||||
flights: {
|
||||
create: [
|
||||
@@ -223,6 +235,10 @@ async function main() {
|
||||
airportPickup: true,
|
||||
venueTransport: true,
|
||||
partySize: 2, // Dr. Baker + assistant
|
||||
phone: '+1 (301) 555-0488',
|
||||
email: 'abaker@natgeo.com',
|
||||
emergencyContactName: 'Marcus Webb',
|
||||
emergencyContactPhone: '+1 (301) 555-0489',
|
||||
notes: 'Keynote speaker, Day 1. Traveling with assistant (Marcus). Needs quiet space before keynote.',
|
||||
flights: {
|
||||
create: [
|
||||
@@ -252,6 +268,10 @@ async function main() {
|
||||
airportPickup: false,
|
||||
venueTransport: true,
|
||||
partySize: 4, // Governor + security officer + aide + driver (their own driver stays)
|
||||
phone: '+1 (303) 555-0100',
|
||||
email: 'gov.martinez@state.co.us',
|
||||
emergencyContactName: 'Elena Martinez',
|
||||
emergencyContactPhone: '+1 (303) 555-0101',
|
||||
notes: 'Governor arriving by motorcade. Party of 4: Gov, 1 state trooper, 1 aide, 1 advance staff. Their driver does NOT need a seat.',
|
||||
},
|
||||
});
|
||||
@@ -267,6 +287,10 @@ async function main() {
|
||||
airportPickup: false,
|
||||
venueTransport: true,
|
||||
partySize: 1,
|
||||
phone: '+1 (720) 555-0550',
|
||||
email: 'somalley@denvercouncil.org',
|
||||
emergencyContactName: 'Patrick O\'Malley',
|
||||
emergencyContactPhone: '+1 (720) 555-0551',
|
||||
notes: 'Local council president. Knows the venue well. Can help with directions if needed.',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -31,7 +31,7 @@ export class CopilotService {
|
||||
properties: {
|
||||
name: { type: 'string', description: 'VIP name to search for (partial match)' },
|
||||
organization: { type: 'string', description: 'Organization name to filter by' },
|
||||
department: { type: 'string', enum: ['OFFICE_OF_DEVELOPMENT', 'ADMIN'], description: 'Department to filter by' },
|
||||
department: { type: 'string', enum: ['OFFICE_OF_DEVELOPMENT', 'ADMIN', 'OTHER'], description: 'Department to filter by' },
|
||||
arrivalMode: { type: 'string', enum: ['FLIGHT', 'SELF_DRIVING'], description: 'Arrival mode to filter by' },
|
||||
},
|
||||
required: [],
|
||||
@@ -55,7 +55,7 @@ export class CopilotService {
|
||||
type: 'object' as const,
|
||||
properties: {
|
||||
name: { type: 'string', description: 'Driver name to search for' },
|
||||
department: { type: 'string', enum: ['OFFICE_OF_DEVELOPMENT', 'ADMIN'], description: 'Department to filter by' },
|
||||
department: { type: 'string', enum: ['OFFICE_OF_DEVELOPMENT', 'ADMIN', 'OTHER'], description: 'Department to filter by' },
|
||||
availableOnly: { type: 'boolean', description: 'Only return available drivers' },
|
||||
},
|
||||
required: [],
|
||||
@@ -240,31 +240,43 @@ export class CopilotService {
|
||||
properties: {
|
||||
name: { type: 'string', description: 'VIP full name' },
|
||||
organization: { type: 'string', description: 'Organization/company name' },
|
||||
department: { type: 'string', enum: ['OFFICE_OF_DEVELOPMENT', 'ADMIN'], description: 'Department' },
|
||||
department: { type: 'string', enum: ['OFFICE_OF_DEVELOPMENT', 'ADMIN', 'OTHER'], description: 'Department' },
|
||||
arrivalMode: { type: 'string', enum: ['FLIGHT', 'SELF_DRIVING'], description: 'How VIP will arrive' },
|
||||
expectedArrival: { type: 'string', description: 'Expected arrival time for self-driving (ISO format)' },
|
||||
airportPickup: { type: 'boolean', description: 'Whether VIP needs airport pickup' },
|
||||
venueTransport: { type: 'boolean', description: 'Whether VIP needs venue transport' },
|
||||
partySize: { type: 'number', description: 'Total party size including VIP plus companions/entourage (default 1)' },
|
||||
notes: { type: 'string', description: 'Additional notes about the VIP' },
|
||||
isRosterOnly: { type: 'boolean', description: 'True if VIP is roster-only (accountability tracking, no active transport coordination)' },
|
||||
phone: { type: 'string', description: 'VIP phone number' },
|
||||
email: { type: 'string', description: 'VIP email address' },
|
||||
emergencyContactName: { type: 'string', description: 'Emergency contact name' },
|
||||
emergencyContactPhone: { type: 'string', description: 'Emergency contact phone' },
|
||||
},
|
||||
required: ['name', 'department', 'arrivalMode'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'update_vip',
|
||||
description: 'Update VIP information.',
|
||||
description: 'Update VIP information including party size, contact info, and roster status.',
|
||||
input_schema: {
|
||||
type: 'object' as const,
|
||||
properties: {
|
||||
vipId: { type: 'string', description: 'The VIP ID to update' },
|
||||
name: { type: 'string', description: 'New name' },
|
||||
organization: { type: 'string', description: 'New organization' },
|
||||
department: { type: 'string', enum: ['OFFICE_OF_DEVELOPMENT', 'ADMIN'], description: 'New department' },
|
||||
department: { type: 'string', enum: ['OFFICE_OF_DEVELOPMENT', 'ADMIN', 'OTHER'], description: 'New department' },
|
||||
arrivalMode: { type: 'string', enum: ['FLIGHT', 'SELF_DRIVING'], description: 'New arrival mode' },
|
||||
expectedArrival: { type: 'string', description: 'New expected arrival time' },
|
||||
airportPickup: { type: 'boolean', description: 'Whether VIP needs airport pickup' },
|
||||
venueTransport: { type: 'boolean', description: 'Whether VIP needs venue transport' },
|
||||
partySize: { type: 'number', description: 'Total party size including VIP plus companions/entourage' },
|
||||
notes: { type: 'string', description: 'New notes' },
|
||||
isRosterOnly: { type: 'boolean', description: 'True if VIP is roster-only (no active transport coordination)' },
|
||||
phone: { type: 'string', description: 'VIP phone number' },
|
||||
email: { type: 'string', description: 'VIP email address' },
|
||||
emergencyContactName: { type: 'string', description: 'Emergency contact name' },
|
||||
emergencyContactPhone: { type: 'string', description: 'Emergency contact phone' },
|
||||
},
|
||||
required: ['vipId'],
|
||||
},
|
||||
@@ -290,7 +302,7 @@ export class CopilotService {
|
||||
driverId: { type: 'string', description: 'The driver ID to update' },
|
||||
name: { type: 'string', description: 'New name' },
|
||||
phone: { type: 'string', description: 'New phone number' },
|
||||
department: { type: 'string', enum: ['OFFICE_OF_DEVELOPMENT', 'ADMIN'], description: 'New department' },
|
||||
department: { type: 'string', enum: ['OFFICE_OF_DEVELOPMENT', 'ADMIN', 'OTHER'], description: 'New department' },
|
||||
isAvailable: { type: 'boolean', description: 'Whether driver is available' },
|
||||
shiftStartTime: { type: 'string', description: 'Shift start time (HH:MM format)' },
|
||||
shiftEndTime: { type: 'string', description: 'Shift end time (HH:MM format)' },
|
||||
@@ -358,7 +370,7 @@ export class CopilotService {
|
||||
properties: {
|
||||
startTime: { type: 'string', description: 'Start time of the time range (ISO format)' },
|
||||
endTime: { type: 'string', description: 'End time of the time range (ISO format)' },
|
||||
preferredDepartment: { type: 'string', enum: ['OFFICE_OF_DEVELOPMENT', 'ADMIN'], description: 'Optional: filter by department' },
|
||||
preferredDepartment: { type: 'string', enum: ['OFFICE_OF_DEVELOPMENT', 'ADMIN', 'OTHER'], description: 'Optional: filter by department' },
|
||||
},
|
||||
required: ['startTime', 'endTime'],
|
||||
},
|
||||
@@ -491,71 +503,16 @@ export class CopilotService {
|
||||
required: ['startDate', 'endDate'],
|
||||
},
|
||||
},
|
||||
// ==================== SELF-AWARENESS / HELP TOOLS ====================
|
||||
{
|
||||
name: 'get_my_capabilities',
|
||||
description: 'Get a comprehensive list of all available tools and capabilities. Use this when the user asks "what can you do?" or when you need to understand your own capabilities. Returns tools organized by category with usage examples.',
|
||||
input_schema: {
|
||||
type: 'object' as const,
|
||||
properties: {
|
||||
category: {
|
||||
type: 'string',
|
||||
enum: ['all', 'search', 'create', 'update', 'delete', 'communication', 'analytics', 'scheduling'],
|
||||
description: 'Filter by category (optional, defaults to all)'
|
||||
},
|
||||
},
|
||||
required: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'get_workflow_guide',
|
||||
description: 'Get step-by-step guidance for common VIP coordination tasks. Use this when you need to understand how to accomplish a complex task or when the user asks "how do I...?" questions.',
|
||||
input_schema: {
|
||||
type: 'object' as const,
|
||||
properties: {
|
||||
task: {
|
||||
type: 'string',
|
||||
enum: [
|
||||
'schedule_airport_pickup',
|
||||
'reassign_driver',
|
||||
'handle_flight_delay',
|
||||
'create_vip_itinerary',
|
||||
'morning_briefing',
|
||||
'send_driver_notifications',
|
||||
'check_schedule_problems',
|
||||
'vehicle_assignment',
|
||||
'bulk_schedule_update'
|
||||
],
|
||||
description: 'The task to get guidance for'
|
||||
},
|
||||
},
|
||||
required: ['task'],
|
||||
},
|
||||
},
|
||||
// ==================== SYSTEM STATUS ====================
|
||||
{
|
||||
name: 'get_current_system_status',
|
||||
description: 'Get a quick status overview of the entire VIP Coordinator system - counts of VIPs, drivers, vehicles, upcoming events, and any immediate issues. Use this to understand the current state of operations.',
|
||||
description: 'Get system overview: VIP/driver/vehicle counts, today\'s events, alerts.',
|
||||
input_schema: {
|
||||
type: 'object' as const,
|
||||
properties: {},
|
||||
required: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'get_api_documentation',
|
||||
description: 'Get documentation for the VIP Coordinator REST API endpoints. Use this when the user asks about API capabilities, how to integrate, or what endpoints are available. Shows all HTTP endpoints with methods, paths, and descriptions.',
|
||||
input_schema: {
|
||||
type: 'object' as const,
|
||||
properties: {
|
||||
resource: {
|
||||
type: 'string',
|
||||
enum: ['all', 'auth', 'users', 'vips', 'drivers', 'events', 'vehicles', 'flights', 'signal'],
|
||||
description: 'Filter by resource type (optional, defaults to all)'
|
||||
},
|
||||
},
|
||||
required: [],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
constructor(
|
||||
@@ -662,37 +619,31 @@ User role: ${userRole}
|
||||
## Your capabilities:
|
||||
- Search and retrieve information about VIPs, drivers, vehicles, events, and flights
|
||||
- CREATE new VIPs, events, and flights
|
||||
- UPDATE existing events, flights, VIP info, and driver assignments
|
||||
- UPDATE existing events, flights, VIP info (including party size, contact info, roster status), and driver assignments
|
||||
- DELETE events and flights that are no longer needed
|
||||
- Assign/reassign drivers and vehicles to events
|
||||
- Check for scheduling conflicts and identify gaps
|
||||
- Get VIP itineraries and driver manifests
|
||||
- **BULK REASSIGN** events from one driver to another by NAME
|
||||
- **SEND MESSAGES** to drivers via Signal
|
||||
- **SEND SCHEDULES** to drivers (PDF/ICS via Signal)
|
||||
- **FIND AVAILABLE DRIVERS** for specific time ranges
|
||||
- **SUGGEST VEHICLES** based on capacity and availability
|
||||
- **AUDIT SCHEDULES** for conflicts, unassigned events, and capacity issues
|
||||
- **WORKLOAD ANALYSIS** for driver utilization
|
||||
- Bulk reassign events, send Signal messages/schedules, find available drivers
|
||||
- Suggest vehicles, audit schedules, analyze workloads
|
||||
|
||||
## IMPORTANT: Use the right tool for the job
|
||||
- To MODIFY an existing event → use update_event (don't create a new one)
|
||||
- To MODIFY an existing event → use update_event
|
||||
- To REMOVE an event → use delete_event
|
||||
- To CHANGE a flight → use update_flight
|
||||
- To ADD a flight → use create_flight
|
||||
- To ASSIGN a driver → use assign_driver_to_event
|
||||
- To ASSIGN a vehicle → use assign_vehicle_to_event
|
||||
- **To REASSIGN all events from one driver to another** → use reassign_driver_events (works by NAME, no IDs needed!)
|
||||
- **To SEND a message to a driver** → use send_driver_notification_via_signal
|
||||
- **To SEND schedules to drivers** → use bulk_send_driver_schedules
|
||||
- **To FIND available drivers** → use find_available_drivers_for_timerange
|
||||
- **To GET a driver's daily schedule** → use get_daily_driver_manifest
|
||||
- **To FIND unassigned events** → use find_unassigned_events
|
||||
- **To CHECK for VIP conflicts** → use check_vip_conflicts
|
||||
- **To AUDIT the schedule** → use identify_scheduling_gaps
|
||||
- **To SUGGEST vehicles** → use suggest_vehicle_for_event
|
||||
- **To GET vehicle schedule** → use get_vehicle_schedule
|
||||
- **To ANALYZE workload** → use get_driver_workload_summary
|
||||
- To CHANGE a flight → use update_flight; ADD a flight → create_flight
|
||||
- To ASSIGN a driver → assign_driver_to_event; vehicle → assign_vehicle_to_event
|
||||
- To REASSIGN events between drivers → reassign_driver_events (by NAME)
|
||||
- To UPDATE VIP party size, contacts, or roster status → use update_vip
|
||||
- To SEND a message to a driver → send_driver_notification_via_signal
|
||||
- To SEND schedules → bulk_send_driver_schedules
|
||||
- To FIND available drivers → find_available_drivers_for_timerange
|
||||
- To AUDIT schedule → identify_scheduling_gaps
|
||||
- To SUGGEST vehicles → suggest_vehicle_for_event
|
||||
|
||||
## Party size and companions:
|
||||
- partySize = the VIP + all companions/handlers/entourage
|
||||
- If user says "add 20 companions" → set partySize to 21 (VIP + 20)
|
||||
- Always use update_vip with partySize to change this, not notes
|
||||
|
||||
## CRITICAL: Never ask for IDs - use names!
|
||||
- You can search for drivers, VIPs, vehicles, and events by NAME
|
||||
@@ -703,8 +654,12 @@ User role: ${userRole}
|
||||
## For actions that MODIFY data (create, update, delete):
|
||||
1. First, search to find the relevant records (use names, not IDs)
|
||||
2. Clearly state what changes you're proposing
|
||||
3. Ask for confirmation before executing
|
||||
4. After execution, show a summary of what was changed
|
||||
3. For BULK operations (updating many records, reassigning multiple events, etc.):
|
||||
- Tell the user upfront: "This is a larger task - I'll be updating X records. Give me a moment to work through them all."
|
||||
- Then proceed immediately with all the tool calls WITHOUT waiting for confirmation
|
||||
- Summarize all changes at the end
|
||||
4. For single-record changes, ask for confirmation before executing
|
||||
5. After execution, show a summary of what was changed
|
||||
|
||||
## When reassigning a driver's events (driver sick, swapping schedules, etc.):
|
||||
1. Use reassign_driver_events with the FROM and TO driver names
|
||||
@@ -801,14 +756,8 @@ User role: ${userRole}
|
||||
return await this.getVehicleSchedule(input);
|
||||
case 'get_driver_workload_summary':
|
||||
return await this.getDriverWorkloadSummary(input);
|
||||
case 'get_my_capabilities':
|
||||
return await this.getMyCapabilities(input);
|
||||
case 'get_workflow_guide':
|
||||
return await this.getWorkflowGuide(input);
|
||||
case 'get_current_system_status':
|
||||
return await this.getCurrentSystemStatus();
|
||||
case 'get_api_documentation':
|
||||
return await this.getApiDocumentation(input);
|
||||
default:
|
||||
return { success: false, error: `Unknown tool: ${name}` };
|
||||
}
|
||||
@@ -1344,7 +1293,13 @@ User role: ${userRole}
|
||||
expectedArrival: input.expectedArrival ? new Date(input.expectedArrival) : null,
|
||||
airportPickup: input.airportPickup ?? false,
|
||||
venueTransport: input.venueTransport ?? false,
|
||||
partySize: input.partySize ?? 1,
|
||||
notes: input.notes,
|
||||
isRosterOnly: input.isRosterOnly ?? false,
|
||||
phone: input.phone || null,
|
||||
email: input.email || null,
|
||||
emergencyContactName: input.emergencyContactName || null,
|
||||
emergencyContactPhone: input.emergencyContactPhone || null,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1362,7 +1317,13 @@ User role: ${userRole}
|
||||
if (updateData.expectedArrival !== undefined) data.expectedArrival = updateData.expectedArrival ? new Date(updateData.expectedArrival) : null;
|
||||
if (updateData.airportPickup !== undefined) data.airportPickup = updateData.airportPickup;
|
||||
if (updateData.venueTransport !== undefined) data.venueTransport = updateData.venueTransport;
|
||||
if (updateData.partySize !== undefined) data.partySize = updateData.partySize;
|
||||
if (updateData.notes !== undefined) data.notes = updateData.notes;
|
||||
if (updateData.isRosterOnly !== undefined) data.isRosterOnly = updateData.isRosterOnly;
|
||||
if (updateData.phone !== undefined) data.phone = updateData.phone || null;
|
||||
if (updateData.email !== undefined) data.email = updateData.email || null;
|
||||
if (updateData.emergencyContactName !== undefined) data.emergencyContactName = updateData.emergencyContactName || null;
|
||||
if (updateData.emergencyContactPhone !== undefined) data.emergencyContactPhone = updateData.emergencyContactPhone || null;
|
||||
|
||||
const vip = await this.prisma.vIP.update({
|
||||
where: { id: vipId },
|
||||
@@ -2711,223 +2672,6 @@ User role: ${userRole}
|
||||
};
|
||||
}
|
||||
|
||||
// ==================== SELF-AWARENESS / HELP TOOLS ====================
|
||||
|
||||
private async getMyCapabilities(input: Record<string, any>): Promise<ToolResult> {
|
||||
const category = input.category || 'all';
|
||||
|
||||
const capabilities = {
|
||||
search: {
|
||||
description: 'Find and lookup information',
|
||||
tools: [
|
||||
{ name: 'search_vips', description: 'Search VIPs by name, organization, department', example: 'Find VIP named John' },
|
||||
{ name: 'search_drivers', description: 'Search drivers by name, department, availability', example: 'Find available drivers' },
|
||||
{ name: 'search_events', description: 'Search events by VIP, driver, date, status', example: 'Events for tomorrow' },
|
||||
{ name: 'get_vip_details', description: 'Get full VIP info with flights and events', example: 'Details for VIP John Smith' },
|
||||
{ name: 'get_vip_itinerary', description: 'Get complete VIP itinerary', example: 'Itinerary for Dr. Martinez' },
|
||||
{ name: 'list_all_drivers', description: 'List all drivers in system', example: 'Show all drivers' },
|
||||
{ name: 'get_available_vehicles', description: 'Find available vehicles by type/capacity', example: 'Available SUVs' },
|
||||
{ name: 'find_available_drivers_for_timerange', description: 'Find drivers free during specific time', example: 'Who is free 2-4pm tomorrow?' },
|
||||
{ name: 'get_daily_driver_manifest', description: 'Full daily schedule for a driver', example: "What's Mike's schedule today?" },
|
||||
{ name: 'get_vehicle_schedule', description: 'Get vehicle assignments for date range', example: 'Blue van schedule this week' },
|
||||
],
|
||||
},
|
||||
create: {
|
||||
description: 'Create new records',
|
||||
tools: [
|
||||
{ name: 'create_vip', description: 'Create a new VIP profile', example: 'Add VIP Jane Doe from Acme Corp' },
|
||||
{ name: 'create_event', description: 'Schedule a new event/transport', example: 'Schedule pickup at 3pm' },
|
||||
{ name: 'create_flight', description: 'Add flight for a VIP', example: 'Add flight AA1234 for John' },
|
||||
],
|
||||
},
|
||||
update: {
|
||||
description: 'Modify existing records',
|
||||
tools: [
|
||||
{ name: 'update_vip', description: 'Update VIP information', example: 'Change VIP phone number' },
|
||||
{ name: 'update_event', description: 'Modify event details, time, location', example: 'Move pickup to 4pm' },
|
||||
{ name: 'update_flight', description: 'Update flight times/status', example: 'Flight delayed to 5pm' },
|
||||
{ name: 'update_driver', description: 'Update driver info, availability', example: 'Mark driver as unavailable' },
|
||||
{ name: 'assign_driver_to_event', description: 'Assign/change driver for event', example: 'Assign Mike to this pickup' },
|
||||
{ name: 'assign_vehicle_to_event', description: 'Assign/change vehicle for event', example: 'Use the SUV for this trip' },
|
||||
{ name: 'reassign_driver_events', description: 'Bulk reassign from one driver to another', example: 'Move all of John\'s events to Mike' },
|
||||
],
|
||||
},
|
||||
delete: {
|
||||
description: 'Remove records (soft delete)',
|
||||
tools: [
|
||||
{ name: 'delete_event', description: 'Cancel/delete an event', example: 'Cancel the 3pm pickup' },
|
||||
{ name: 'delete_flight', description: 'Remove a flight record', example: 'Delete cancelled flight' },
|
||||
],
|
||||
},
|
||||
communication: {
|
||||
description: 'Driver messaging via Signal',
|
||||
tools: [
|
||||
{ name: 'send_driver_notification_via_signal', description: 'Send message to driver', example: 'Tell Mike the pickup is delayed' },
|
||||
{ name: 'bulk_send_driver_schedules', description: 'Send schedules to all drivers', example: 'Send tomorrow\'s schedules to everyone' },
|
||||
],
|
||||
},
|
||||
analytics: {
|
||||
description: 'Reports, summaries, and audits',
|
||||
tools: [
|
||||
{ name: 'get_todays_summary', description: 'Today\'s events, arrivals, stats', example: "What's happening today?" },
|
||||
{ name: 'get_weekly_lookahead', description: 'Week-by-week event summary', example: 'What does next week look like?' },
|
||||
{ name: 'get_driver_workload_summary', description: 'Driver utilization statistics', example: 'Who is overworked this week?' },
|
||||
{ name: 'identify_scheduling_gaps', description: 'Find problems in schedule', example: 'Any issues with the schedule?' },
|
||||
{ name: 'find_unassigned_events', description: 'Events without driver/vehicle', example: 'What needs assignment?' },
|
||||
],
|
||||
},
|
||||
scheduling: {
|
||||
description: 'Conflict detection and scheduling',
|
||||
tools: [
|
||||
{ name: 'check_driver_conflicts', description: 'Check driver for time conflicts', example: 'Can Mike do 2-4pm?' },
|
||||
{ name: 'check_vip_conflicts', description: 'Check VIP for double-booking', example: 'Is VIP free at 3pm?' },
|
||||
{ name: 'suggest_vehicle_for_event', description: 'Recommend best vehicle', example: 'What vehicle for 6 passengers?' },
|
||||
{ name: 'get_driver_schedule', description: 'Driver events for date range', example: 'Mike\'s schedule this week' },
|
||||
],
|
||||
},
|
||||
help: {
|
||||
description: 'Self-help and system info',
|
||||
tools: [
|
||||
{ name: 'get_my_capabilities', description: 'List all available tools (this tool)', example: 'What can you do?' },
|
||||
{ name: 'get_workflow_guide', description: 'Step-by-step task guides', example: 'How do I handle a flight delay?' },
|
||||
{ name: 'get_current_system_status', description: 'System overview and stats', example: 'System status' },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const validCategory = category as keyof typeof capabilities;
|
||||
if (category !== 'all' && capabilities[validCategory]) {
|
||||
return {
|
||||
success: true,
|
||||
data: { [category]: capabilities[validCategory] },
|
||||
message: `Showing ${category} tools. I have ${capabilities[validCategory].tools.length} tools in this category.`,
|
||||
};
|
||||
}
|
||||
|
||||
const totalTools = Object.values(capabilities).reduce((sum, cat) => sum + cat.tools.length, 0);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: capabilities,
|
||||
message: `I have ${totalTools} tools available across ${Object.keys(capabilities).length} categories. Ask me about any specific capability!`,
|
||||
};
|
||||
}
|
||||
|
||||
private async getWorkflowGuide(input: Record<string, any>): Promise<ToolResult> {
|
||||
const workflows = {
|
||||
schedule_airport_pickup: {
|
||||
title: 'Schedule an Airport Pickup',
|
||||
steps: [
|
||||
'1. Find the VIP: Use search_vips with the VIP name',
|
||||
'2. Get flight info: Use get_flights_for_vip or check if flight already exists',
|
||||
'3. Find available driver: Use find_available_drivers_for_timerange for the arrival time',
|
||||
'4. Find suitable vehicle: Use suggest_vehicle_for_event or get_available_vehicles',
|
||||
'5. Create the event: Use create_event with type TRANSPORT, pickup at airport, dropoff at destination',
|
||||
'6. Notify driver: Use send_driver_notification_via_signal to inform them',
|
||||
],
|
||||
tips: ['Add 30 min buffer after flight arrival', 'Check flight status before the day'],
|
||||
},
|
||||
reassign_driver: {
|
||||
title: 'Reassign a Driver\'s Events (Driver Sick/Unavailable)',
|
||||
steps: [
|
||||
'1. Use reassign_driver_events with fromDriverName and toDriverName',
|
||||
'2. Optionally specify a date to only reassign that day\'s events',
|
||||
'3. Review the returned list of reassigned events',
|
||||
'4. Use send_driver_notification_via_signal to notify both drivers',
|
||||
],
|
||||
tips: ['Check the new driver has no conflicts first', 'Update driver availability with update_driver'],
|
||||
},
|
||||
handle_flight_delay: {
|
||||
title: 'Handle a Flight Delay',
|
||||
steps: [
|
||||
'1. Find the VIP: Use search_vips to get VIP info',
|
||||
'2. Update flight: Use update_flight with new arrival time',
|
||||
'3. Find affected events: Use search_events filtered by VIP and date',
|
||||
'4. Update pickup event: Use update_event to adjust start/end times',
|
||||
'5. Check driver conflicts: Use check_driver_conflicts for new time',
|
||||
'6. Notify driver: Use send_driver_notification_via_signal about the change',
|
||||
],
|
||||
tips: ['Check if other VIPs share the same flight', 'Consider ripple effects on later events'],
|
||||
},
|
||||
create_vip_itinerary: {
|
||||
title: 'Create a Complete VIP Itinerary',
|
||||
steps: [
|
||||
'1. Create VIP: Use create_vip with all details',
|
||||
'2. Add flights: Use create_flight for each flight segment',
|
||||
'3. Create events: Use create_event for airport pickup, meetings, dinners, etc.',
|
||||
'4. Assign resources: Use assign_driver_to_event and assign_vehicle_to_event',
|
||||
'5. Review: Use get_vip_itinerary to see the complete schedule',
|
||||
],
|
||||
tips: ['Schedule in chronological order', 'Add buffer time between events'],
|
||||
},
|
||||
morning_briefing: {
|
||||
title: 'Get Morning Briefing',
|
||||
steps: [
|
||||
'1. Get today summary: Use get_todays_summary for overview',
|
||||
'2. Check for problems: Use identify_scheduling_gaps',
|
||||
'3. Review unassigned: Use find_unassigned_events',
|
||||
'4. Check workload: Use get_driver_workload_summary for balance',
|
||||
'5. Send schedules: Use bulk_send_driver_schedules to notify all drivers',
|
||||
],
|
||||
tips: ['Do this 30 min before operations start', 'Address gaps before drivers arrive'],
|
||||
},
|
||||
send_driver_notifications: {
|
||||
title: 'Send Notifications to Drivers',
|
||||
steps: [
|
||||
'1. For single driver: Use send_driver_notification_via_signal with message',
|
||||
'2. For all drivers (schedules): Use bulk_send_driver_schedules with date',
|
||||
'3. Reference specific event: Include relatedEventId for context',
|
||||
],
|
||||
tips: ['Keep messages concise', 'Include pickup time and location'],
|
||||
},
|
||||
check_schedule_problems: {
|
||||
title: 'Audit Schedule for Problems',
|
||||
steps: [
|
||||
'1. Run full audit: Use identify_scheduling_gaps with lookahead days',
|
||||
'2. Review driver conflicts: Check the conflicts list',
|
||||
'3. Review VIP conflicts: Check for double-bookings',
|
||||
'4. Review unassigned: Check events missing drivers/vehicles',
|
||||
'5. Fix issues: Use update_event, assign_driver_to_event as needed',
|
||||
],
|
||||
tips: ['Run this daily', 'Prioritize same-day issues'],
|
||||
},
|
||||
vehicle_assignment: {
|
||||
title: 'Choose and Assign the Right Vehicle',
|
||||
steps: [
|
||||
'1. Get recommendation: Use suggest_vehicle_for_event with eventId',
|
||||
'2. Or search manually: Use get_available_vehicles with type/capacity filters',
|
||||
'3. Check availability: Use get_vehicle_schedule to see existing assignments',
|
||||
'4. Assign: Use assign_vehicle_to_event',
|
||||
],
|
||||
tips: ['Consider VIP count for capacity', 'Check vehicle location if multiple stops'],
|
||||
},
|
||||
bulk_schedule_update: {
|
||||
title: 'Handle Bulk Schedule Changes',
|
||||
steps: [
|
||||
'1. Search affected events: Use search_events with date/driver/VIP filter',
|
||||
'2. Update each event: Use update_event for each (or reassign_driver_events for driver swap)',
|
||||
'3. Notify drivers: Use send_driver_notification_via_signal or bulk_send_driver_schedules',
|
||||
],
|
||||
tips: ['Work chronologically', 'Verify no new conflicts after changes'],
|
||||
},
|
||||
};
|
||||
|
||||
const task = input.task as keyof typeof workflows;
|
||||
if (!workflows[task]) {
|
||||
return {
|
||||
success: false,
|
||||
error: `Unknown workflow: ${task}. Available: ${Object.keys(workflows).join(', ')}`,
|
||||
};
|
||||
}
|
||||
|
||||
const workflow = workflows[task];
|
||||
return {
|
||||
success: true,
|
||||
data: workflow,
|
||||
message: `Here's the step-by-step guide for: ${workflow.title}`,
|
||||
};
|
||||
}
|
||||
|
||||
private async getCurrentSystemStatus(): Promise<ToolResult> {
|
||||
const now = new Date();
|
||||
const today = new Date(now);
|
||||
@@ -3011,142 +2755,4 @@ User role: ${userRole}
|
||||
};
|
||||
}
|
||||
|
||||
private async getApiDocumentation(input: Record<string, any>): Promise<ToolResult> {
|
||||
const resource = input.resource || 'all';
|
||||
|
||||
const apiDocs = {
|
||||
auth: {
|
||||
description: 'Authentication endpoints (Auth0 JWT-based)',
|
||||
baseUrl: '/api/v1/auth',
|
||||
endpoints: [
|
||||
{ method: 'GET', path: '/profile', description: 'Get current user profile and role', auth: 'Required' },
|
||||
],
|
||||
notes: 'Uses Auth0 for authentication. Token must be included as Bearer token in Authorization header.',
|
||||
},
|
||||
users: {
|
||||
description: 'User management (Admin only)',
|
||||
baseUrl: '/api/v1/users',
|
||||
endpoints: [
|
||||
{ method: 'GET', path: '/', description: 'List all users', auth: 'Admin only' },
|
||||
{ method: 'GET', path: '/pending', description: 'List users pending approval', auth: 'Admin only' },
|
||||
{ method: 'GET', path: '/:id', description: 'Get specific user by ID', auth: 'Admin only' },
|
||||
{ method: 'PATCH', path: '/:id', description: 'Update user details', auth: 'Admin only' },
|
||||
{ method: 'PATCH', path: '/:id/approve', description: 'Approve a pending user', auth: 'Admin only' },
|
||||
{ method: 'DELETE', path: '/:id', description: 'Delete user (soft delete)', auth: 'Admin only' },
|
||||
],
|
||||
notes: 'First user to register automatically becomes Admin. Other users need approval.',
|
||||
},
|
||||
vips: {
|
||||
description: 'VIP profile management',
|
||||
baseUrl: '/api/v1/vips',
|
||||
endpoints: [
|
||||
{ method: 'GET', path: '/', description: 'List all VIPs', auth: 'All roles' },
|
||||
{ method: 'GET', path: '/:id', description: 'Get VIP details with flights and events', auth: 'All roles' },
|
||||
{ method: 'POST', path: '/', description: 'Create new VIP', auth: 'Admin, Coordinator' },
|
||||
{ method: 'PATCH', path: '/:id', description: 'Update VIP information', auth: 'Admin, Coordinator' },
|
||||
{ method: 'DELETE', path: '/:id', description: 'Soft delete VIP', auth: 'Admin, Coordinator' },
|
||||
],
|
||||
fields: ['name', 'organization', 'department (OFFICE_OF_DEVELOPMENT | ADMIN)', 'arrivalMode (FLIGHT | SELF_DRIVING)', 'expectedArrival', 'airportPickup', 'venueTransport', 'notes'],
|
||||
},
|
||||
drivers: {
|
||||
description: 'Driver resource management',
|
||||
baseUrl: '/api/v1/drivers',
|
||||
endpoints: [
|
||||
{ method: 'GET', path: '/', description: 'List all drivers', auth: 'All roles' },
|
||||
{ method: 'GET', path: '/me', description: 'Get current user\'s driver profile', auth: 'Driver role' },
|
||||
{ method: 'GET', path: '/:id', description: 'Get driver details', auth: 'All roles' },
|
||||
{ method: 'GET', path: '/:id/schedule', description: 'Get driver\'s schedule', auth: 'All roles' },
|
||||
{ method: 'POST', path: '/', description: 'Create new driver', auth: 'Admin, Coordinator' },
|
||||
{ method: 'POST', path: '/:id/send-schedule', description: 'Send schedule to driver via Signal (ICS + PDF)', auth: 'Admin, Coordinator' },
|
||||
{ method: 'POST', path: '/send-all-schedules', description: 'Send schedules to all drivers with events', auth: 'Admin, Coordinator' },
|
||||
{ method: 'PATCH', path: '/:id', description: 'Update driver', auth: 'Admin, Coordinator' },
|
||||
{ method: 'PATCH', path: '/me', description: 'Update own profile', auth: 'Driver role' },
|
||||
{ method: 'DELETE', path: '/:id', description: 'Soft delete driver', auth: 'Admin, Coordinator' },
|
||||
],
|
||||
fields: ['name', 'phone', 'department', 'isAvailable', 'shiftStartTime', 'shiftEndTime', 'userId (link to User)'],
|
||||
},
|
||||
events: {
|
||||
description: 'Schedule event management (transports, meetings, etc.)',
|
||||
baseUrl: '/api/v1/events',
|
||||
endpoints: [
|
||||
{ method: 'GET', path: '/', description: 'List events (supports filters: date, driverId, status)', auth: 'All roles' },
|
||||
{ method: 'GET', path: '/:id', description: 'Get event details', auth: 'All roles' },
|
||||
{ method: 'POST', path: '/', description: 'Create new event (checks for conflicts)', auth: 'Admin, Coordinator' },
|
||||
{ method: 'PATCH', path: '/:id', description: 'Update event', auth: 'Admin, Coordinator' },
|
||||
{ method: 'PATCH', path: '/:id/status', description: 'Update event status only', auth: 'All roles (Drivers can update their events)' },
|
||||
{ method: 'DELETE', path: '/:id', description: 'Cancel/delete event', auth: 'Admin, Coordinator' },
|
||||
],
|
||||
fields: ['vipIds[]', 'title', 'type (TRANSPORT | MEETING | EVENT | MEAL | ACCOMMODATION)', 'status (SCHEDULED | IN_PROGRESS | COMPLETED | CANCELLED)', 'startTime', 'endTime', 'pickupLocation', 'dropoffLocation', 'location', 'driverId', 'vehicleId', 'description', 'notes'],
|
||||
notes: 'Events support multiple VIPs via vipIds array. Conflict detection runs on create/update.',
|
||||
},
|
||||
vehicles: {
|
||||
description: 'Vehicle fleet management',
|
||||
baseUrl: '/api/v1/vehicles',
|
||||
endpoints: [
|
||||
{ method: 'GET', path: '/', description: 'List all vehicles (supports filters: type, status)', auth: 'All roles' },
|
||||
{ method: 'GET', path: '/:id', description: 'Get vehicle details', auth: 'All roles' },
|
||||
{ method: 'POST', path: '/', description: 'Create new vehicle', auth: 'Admin, Coordinator' },
|
||||
{ method: 'PATCH', path: '/:id', description: 'Update vehicle', auth: 'Admin, Coordinator' },
|
||||
{ method: 'DELETE', path: '/:id', description: 'Soft delete vehicle', auth: 'Admin, Coordinator' },
|
||||
],
|
||||
fields: ['name', 'type (VAN | SUV | SEDAN | BUS | GOLF_CART | TRUCK)', 'licensePlate', 'seatCapacity', 'status (AVAILABLE | IN_USE | MAINTENANCE)', 'notes'],
|
||||
},
|
||||
flights: {
|
||||
description: 'Flight tracking and management',
|
||||
baseUrl: '/api/v1/flights',
|
||||
endpoints: [
|
||||
{ method: 'GET', path: '/', description: 'List all flights', auth: 'All roles' },
|
||||
{ method: 'GET', path: '/:id', description: 'Get flight details', auth: 'All roles' },
|
||||
{ method: 'GET', path: '/vip/:vipId', description: 'Get all flights for a VIP', auth: 'All roles' },
|
||||
{ method: 'POST', path: '/', description: 'Create flight record', auth: 'Admin, Coordinator' },
|
||||
{ method: 'POST', path: '/track/:flightNumber', description: 'Fetch live flight status from AviationStack', auth: 'Admin, Coordinator' },
|
||||
{ method: 'PATCH', path: '/:id', description: 'Update flight info', auth: 'Admin, Coordinator' },
|
||||
{ method: 'DELETE', path: '/:id', description: 'Delete flight', auth: 'Admin, Coordinator' },
|
||||
],
|
||||
fields: ['vipId', 'flightNumber', 'flightDate', 'segment', 'departureAirport (IATA code)', 'arrivalAirport', 'scheduledDeparture', 'scheduledArrival', 'actualDeparture', 'actualArrival', 'status'],
|
||||
},
|
||||
signal: {
|
||||
description: 'Signal messaging integration for driver communication',
|
||||
baseUrl: '/api/v1/signal',
|
||||
endpoints: [
|
||||
{ method: 'GET', path: '/status', description: 'Get Signal service status and linked number', auth: 'Admin, Coordinator' },
|
||||
{ method: 'GET', path: '/messages', description: 'Get message history (supports driverId filter)', auth: 'Admin, Coordinator' },
|
||||
{ method: 'GET', path: '/messages/unread-counts', description: 'Get unread message counts per driver', auth: 'Admin, Coordinator' },
|
||||
{ method: 'GET', path: '/messages/driver/:driverId', description: 'Get messages for specific driver', auth: 'Admin, Coordinator' },
|
||||
{ method: 'POST', path: '/messages/send', description: 'Send message to driver via Signal', auth: 'Admin, Coordinator' },
|
||||
{ method: 'POST', path: '/messages/mark-read/:driverId', description: 'Mark driver messages as read', auth: 'Admin, Coordinator' },
|
||||
{ method: 'POST', path: '/messages/check-responses', description: 'Check if drivers responded since event start times', auth: 'Admin, Coordinator' },
|
||||
],
|
||||
notes: 'Requires Signal CLI to be running and linked. Messages are stored in database for history.',
|
||||
},
|
||||
};
|
||||
|
||||
const validResource = resource as keyof typeof apiDocs;
|
||||
if (resource !== 'all' && apiDocs[validResource]) {
|
||||
const doc = apiDocs[validResource];
|
||||
return {
|
||||
success: true,
|
||||
data: { [resource]: doc },
|
||||
message: `API documentation for ${resource} endpoints. Base URL: ${doc.baseUrl}`,
|
||||
};
|
||||
}
|
||||
|
||||
const totalEndpoints = Object.values(apiDocs).reduce(
|
||||
(sum, r) => sum + r.endpoints.length,
|
||||
0
|
||||
);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
overview: {
|
||||
baseUrl: '/api/v1',
|
||||
authentication: 'Auth0 JWT Bearer token required on all endpoints',
|
||||
roles: ['ADMINISTRATOR (full access)', 'COORDINATOR (manage VIPs, drivers, events)', 'DRIVER (view + update own events)'],
|
||||
},
|
||||
resources: apiDocs,
|
||||
},
|
||||
message: `VIP Coordinator API has ${totalEndpoints} endpoints across ${Object.keys(apiDocs).length} resources. All endpoints require authentication.`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user