/**
* Meeting Chief Lite - Status Tool
* System health, sync status, and statistics
*/
import { z } from 'zod';
import {
getStats,
getLastSyncRun,
getPendingEmbeddingJobs,
getSetting
} from '../lib/database.js';
import { processEmbeddingJobs } from '../lib/embeddings.js';
import { runOtterSync } from '../lib/otter.js';
export const statusToolSchema = z.object({
operation: z.enum(['health', 'sync', 'stats', 'generate_embeddings', 'run_sync']).describe(
'Operation: health (system check), sync (last sync info), stats (database stats), generate_embeddings (process pending), run_sync (sync from Otter.ai)'
),
sync_days: z.number().optional().default(90).describe('Days of history to sync (for run_sync)'),
});
export type StatusToolInput = z.infer<typeof statusToolSchema>;
export async function statusTool(input: StatusToolInput): Promise<string> {
const { operation, sync_days } = input;
switch (operation) {
case 'health': {
const stats = getStats();
const lastSync = getLastSyncRun();
const pendingJobs = getPendingEmbeddingJobs(1);
const hasApiKey = !!process.env.OPENROUTER_API_KEY;
const hasOtterCreds = !!process.env.OTTER_EMAIL && !!process.env.OTTER_PASSWORD;
return JSON.stringify({
status: 'ok',
database: {
meetings: stats.total_meetings,
chunks: stats.total_chunks,
vectors: stats.total_vectors,
embedding_coverage: stats.total_chunks > 0
? `${Math.round((stats.total_vectors / stats.total_chunks) * 100)}%`
: 'N/A',
},
sync: {
last_run: lastSync?.started_at,
last_status: lastSync?.status,
items_synced: lastSync?.items_synced,
},
pending: {
embedding_jobs: pendingJobs.length > 0,
},
config: {
openrouter_api_key: hasApiKey ? 'configured' : 'missing',
otter_credentials: hasOtterCreds ? 'configured' : 'missing',
},
});
}
case 'sync': {
const lastSync = getLastSyncRun();
if (!lastSync) {
return JSON.stringify({
message: 'No sync runs found. Run "run_sync" to sync from Otter.ai.',
last_sync: null,
});
}
return JSON.stringify({
id: lastSync.id,
source: lastSync.source,
status: lastSync.status,
items_synced: lastSync.items_synced,
started_at: lastSync.started_at,
completed_at: lastSync.completed_at,
error: lastSync.error,
});
}
case 'stats': {
const stats = getStats();
return JSON.stringify({
meetings: stats.total_meetings,
transcript_chunks: stats.total_chunks,
embeddings: stats.total_vectors,
embedding_coverage: stats.total_chunks > 0
? `${Math.round((stats.total_vectors / stats.total_chunks) * 100)}%`
: '0%',
date_range: {
oldest: stats.oldest_meeting?.slice(0, 10) || null,
newest: stats.newest_meeting?.slice(0, 10) || null,
},
});
}
case 'generate_embeddings': {
try {
const result = await processEmbeddingJobs();
return JSON.stringify({
message: 'Embedding generation complete',
processed: result.processed,
failed: result.failed,
});
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
return JSON.stringify({
error: `Embedding generation failed: ${message}`,
});
}
}
case 'run_sync': {
try {
const result = await runOtterSync(sync_days);
if (!result.success) {
return JSON.stringify({
error: result.error || 'Sync failed',
synced: result.synced,
failed: result.failed,
});
}
return JSON.stringify({
message: 'Sync complete',
synced: result.synced,
failed: result.failed,
});
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
return JSON.stringify({
error: `Sync failed: ${message}`,
});
}
}
default:
return JSON.stringify({ error: `Unknown operation: ${operation}` });
}
}
export const statusToolDefinition = {
name: 'status',
description: 'System health, sync status, and management operations. Use health to check system state, sync to see last sync info, stats for database statistics, generate_embeddings to process pending jobs, run_sync to sync from Otter.ai.',
inputSchema: {
type: 'object' as const,
properties: {
operation: {
type: 'string',
enum: ['health', 'sync', 'stats', 'generate_embeddings', 'run_sync'],
description: 'Operation to perform',
},
sync_days: {
type: 'number',
description: 'Days of history to sync (default: 90)',
},
},
required: ['operation'],
},
};