diff --git a/src/lib/integrations.ts b/src/lib/integrations.ts index 06fe493..861774f 100644 --- a/src/lib/integrations.ts +++ b/src/lib/integrations.ts @@ -6,6 +6,13 @@ import type { Env } from '../index'; * All calls are fire-and-forget safe — failures are logged but don't break the caller. */ +/** Distinguishes "no data" from "service error" for array-returning methods */ +export interface ServiceArrayResult { + data: T[]; + error?: string; + status?: number; +} + // ── ChittyLedger ───────────────────────────────────────────── // Audit trail: entries, chain verification, custody queries // NOTE: Evidence/case operations live on ChittyEvidence, not ChittyLedger. @@ -54,17 +61,22 @@ export function ledgerClient(env: Env) { addEntry: (entry: LedgerEntryPayload) => post<{ id: string; sequenceNumber: number; hash: string }>('/entries', entry), - /** Search ledger entries */ - searchEntries: async (params: { entityType?: string; entityId?: string; actor?: string; status?: string; limit?: number }): Promise[]> => { + /** Search ledger entries — returns { data, error? } to distinguish empty from failure */ + searchEntries: async (params: { entityType?: string; entityId?: string; actor?: string; status?: string; limit?: number }): Promise>> => { const qs = new URLSearchParams( Object.entries(params).filter(([, v]) => v !== undefined).map(([k, v]) => [k, String(v)] as [string, string]) ).toString(); - return await get[]>(`/entries${qs ? `?${qs}` : ''}`) ?? []; + const result = await get[]>(`/entries${qs ? `?${qs}` : ''}`); + if (result === null) return { data: [], error: 'ChittyLedger unreachable or returned error' }; + return { data: result }; }, - /** Get chain of custody for an entity */ - getChainOfCustody: async (entityId: string) => - await get[]>(`/custody/${encodeURIComponent(entityId)}`) ?? [], + /** Get chain of custody for an entity — returns { data, error? } to distinguish empty from failure */ + getChainOfCustody: async (entityId: string): Promise>> => { + const result = await get[]>(`/custody/${encodeURIComponent(entityId)}`); + if (result === null) return { data: [], error: 'ChittyLedger unreachable or returned error' }; + return { data: result }; + }, /** Verify ledger chain integrity */ verifyChain: () => get<{ valid: boolean; errors: string[] }>('/verify'), diff --git a/src/routes/mcp.ts b/src/routes/mcp.ts index 0258e30..31b3bee 100644 --- a/src/routes/mcp.ts +++ b/src/routes/mcp.ts @@ -1225,16 +1225,21 @@ async function executeTool(env: Env, sql: NeonQueryFunction, toolN // Facts from ChittyEvidence const evidence = evidenceClient(env); if (evidence) { - const facts = startDate && endDate - ? await evidence.getFactsByDateRange(caseId, startDate, endDate) - : await evidence.getEnrichedFacts(caseId); - if (facts) { - for (const f of facts) { - if (!f.fact_date) continue; - events.push({ id: `fact:${f.id}`, date: f.fact_date, type: 'fact', title: f.fact_text.slice(0, 120), factType: f.fact_type, confidence: f.confidence, status: f.verification_status }); + try { + const facts = startDate && endDate + ? await evidence.getFactsByDateRange(caseId, startDate, endDate) + : await evidence.getEnrichedFacts(caseId); + if (facts) { + for (const f of facts) { + if (!f.fact_date) continue; + events.push({ id: `fact:${f.id}`, date: f.fact_date, type: 'fact', title: f.fact_text.slice(0, 120), factType: f.fact_type, confidence: f.confidence, status: f.verification_status }); + } + } else { + warnings.push('Failed to fetch facts from ChittyEvidence'); } - } else { - warnings.push('Failed to fetch facts from ChittyEvidence'); + } catch (err) { + console.error('[mcp/get_case_timeline] evidence facts error:', err); + warnings.push('Evidence facts unavailable: service error'); } } else { warnings.push('ChittyEvidence not configured'); diff --git a/src/routes/timeline.ts b/src/routes/timeline.ts index b4d59c1..c418df2 100644 --- a/src/routes/timeline.ts +++ b/src/routes/timeline.ts @@ -65,14 +65,16 @@ timelineRoutes.get('/cases/:caseId/timeline', async (c) => { // 2. Fetch legal deadlines from ChittyCommand DB const sql = getDb(c.env); try { - const deadlines = await sql` - SELECT id, title, description, deadline_date, deadline_type, status, urgency_score - FROM cc_legal_deadlines - WHERE case_ref = ${caseId} - ${startDate ? sql`AND deadline_date >= ${startDate}` : sql``} - ${endDate ? sql`AND deadline_date <= ${endDate}` : sql``} - ORDER BY deadline_date ASC - `; + let deadlines; + if (startDate && endDate) { + deadlines = await sql`SELECT id, title, description, deadline_date, deadline_type, status, urgency_score FROM cc_legal_deadlines WHERE case_ref = ${caseId} AND deadline_date >= ${startDate} AND deadline_date <= ${endDate} ORDER BY deadline_date ASC`; + } else if (startDate) { + deadlines = await sql`SELECT id, title, description, deadline_date, deadline_type, status, urgency_score FROM cc_legal_deadlines WHERE case_ref = ${caseId} AND deadline_date >= ${startDate} ORDER BY deadline_date ASC`; + } else if (endDate) { + deadlines = await sql`SELECT id, title, description, deadline_date, deadline_type, status, urgency_score FROM cc_legal_deadlines WHERE case_ref = ${caseId} AND deadline_date <= ${endDate} ORDER BY deadline_date ASC`; + } else { + deadlines = await sql`SELECT id, title, description, deadline_date, deadline_type, status, urgency_score FROM cc_legal_deadlines WHERE case_ref = ${caseId} ORDER BY deadline_date ASC`; + } for (const d of deadlines) { events.push({ id: `deadline:${d.id}`,