feat: Add transcript engine API and connect ConversationHistory to real data
- Add transcript-engine service that parses Claude Code JSONL transcripts with session listing, message extraction, token/stats analysis, and caching - Add transcript REST routes (sessions list, latest, by session ID, section filtering) - Rewrite ConversationHistory to fetch from /api/transcript/* instead of mock data - Add session pills for switching between conversation sessions - Add stats bar footer with model, duration, tokens, and tool count - Add TranscriptSession/TranscriptMessage types, ChatInput, InputSettings, PromptBar updates, TranscriptCard, and useVoiceCapture composable
This commit is contained in:
@@ -20,6 +20,7 @@ import {
|
||||
handleAgentsPlugins, handleAgentsMcpJson,
|
||||
handleAgentsConfigPermissions, handleAgentsConfigHooks, handleAgentsConfigMcp
|
||||
} from './agents'
|
||||
import { handleTranscript, handleTranscriptSessions } from './transcript'
|
||||
|
||||
export async function handleRequest(req: Request): Promise<Response> {
|
||||
const url = new URL(req.url)
|
||||
@@ -277,6 +278,20 @@ export async function handleRequest(req: Request): Promise<Response> {
|
||||
return handleGitFile(url)
|
||||
}
|
||||
|
||||
// Transcript
|
||||
if (path === '/api/transcript/sessions' && req.method === 'GET') {
|
||||
return handleTranscriptSessions()
|
||||
}
|
||||
|
||||
if (path === '/api/transcript/latest' && req.method === 'GET') {
|
||||
return handleTranscript(req, url, 'latest')
|
||||
}
|
||||
|
||||
const transcriptMatch = path.match(/^\/api\/transcript\/([a-f0-9-]+)$/)
|
||||
if (transcriptMatch && req.method === 'GET') {
|
||||
return handleTranscript(req, url, transcriptMatch[1])
|
||||
}
|
||||
|
||||
// Agents
|
||||
if (path === '/api/agents' && req.method === 'GET') {
|
||||
return handleAgents(req)
|
||||
|
||||
81
server/routes/transcript.ts
Normal file
81
server/routes/transcript.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { jsonResponse, errorResponse } from '../utils/cors'
|
||||
import { getTranscriptAnalysis, listSessions } from '../services/transcript-engine'
|
||||
import type { TranscriptAnalysis } from '../services/transcript-engine'
|
||||
|
||||
export function handleTranscriptSessions(): Response {
|
||||
const sessions = listSessions()
|
||||
return jsonResponse(sessions)
|
||||
}
|
||||
|
||||
export function handleTranscript(req: Request, url: URL, sessionId: string): Response {
|
||||
if (req.method !== 'GET') return errorResponse('Method not allowed', 405)
|
||||
|
||||
const resolvedId = sessionId === 'latest' ? undefined : sessionId
|
||||
const analysis = getTranscriptAnalysis(resolvedId)
|
||||
|
||||
if (!analysis) {
|
||||
return errorResponse('Transcript not found', 404)
|
||||
}
|
||||
|
||||
// Section filtering
|
||||
const section = url.searchParams.get('section')
|
||||
if (section) {
|
||||
return handleSection(analysis, section)
|
||||
}
|
||||
|
||||
// Full response (exclude thinking by default)
|
||||
const includeThinking = url.searchParams.get('includeThinking') === 'true'
|
||||
if (!includeThinking) {
|
||||
return jsonResponse({
|
||||
...analysis,
|
||||
messages: analysis.messages.filter(m => !m.isMeta)
|
||||
})
|
||||
}
|
||||
|
||||
return jsonResponse({
|
||||
...analysis,
|
||||
messages: analysis.messages.filter(m => !m.isMeta)
|
||||
})
|
||||
}
|
||||
|
||||
function handleSection(analysis: TranscriptAnalysis, section: string): Response {
|
||||
switch (section) {
|
||||
case 'messages':
|
||||
return jsonResponse({
|
||||
sessionId: analysis.sessionId,
|
||||
messages: analysis.messages.filter(m => !m.isMeta)
|
||||
})
|
||||
case 'tokens':
|
||||
return jsonResponse({
|
||||
sessionId: analysis.sessionId,
|
||||
tokens: analysis.tokens
|
||||
})
|
||||
case 'tools':
|
||||
return jsonResponse({
|
||||
sessionId: analysis.sessionId,
|
||||
tools: analysis.tools
|
||||
})
|
||||
case 'stats':
|
||||
return jsonResponse({
|
||||
sessionId: analysis.sessionId,
|
||||
stats: analysis.stats,
|
||||
model: analysis.model,
|
||||
version: analysis.version,
|
||||
duration: analysis.duration,
|
||||
startTime: analysis.startTime,
|
||||
endTime: analysis.endTime
|
||||
})
|
||||
case 'files':
|
||||
return jsonResponse({
|
||||
sessionId: analysis.sessionId,
|
||||
filesModified: analysis.filesModified
|
||||
})
|
||||
case 'subagents':
|
||||
return jsonResponse({
|
||||
sessionId: analysis.sessionId,
|
||||
subagents: analysis.subagents
|
||||
})
|
||||
default:
|
||||
return errorResponse(`Unknown section: ${section}. Valid: messages, tokens, tools, stats, files, subagents`, 400)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user