-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtimeline.ts
More file actions
79 lines (68 loc) · 2.91 KB
/
Copy pathtimeline.ts
File metadata and controls
79 lines (68 loc) · 2.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import { EventStore } from "./event-store.js";
export interface TimelineEntry {
type: "prompt" | "commit" | "log" | "execution";
id: string;
timestamp: string;
summary: string;
author?: string;
event_type?: string;
severity?: string;
details?: any;
}
export class TimelineProvider {
private eventStore: EventStore;
constructor() {
this.eventStore = EventStore.getInstance();
}
getUnifiedTimeline(limit = 50, repoId?: string): TimelineEntry[] {
const db = (this.eventStore as any).db;
const prompts = db.prepare(`
SELECT 'prompt' as type, p.id, p.timestamp, p.raw_prompt as summary, p.agent_name as author, p.intent as event_type, p.normalized_prompt as details
FROM prompts p
${repoId ? 'WHERE (p.repo_id = ? OR p.repo_id IS NULL)' : ''}
ORDER BY p.timestamp DESC
LIMIT ?
`).all(...(repoId ? [repoId, limit] : [limit]));
const commits = db.prepare(`
SELECT 'commit' as type, c.id, c.committed_at as timestamp, c.message as summary, c.author, c.changed_files_json as details
FROM commits c
${repoId ? 'WHERE (c.repo_id = ? OR c.repo_id IS NULL)' : ''}
ORDER BY c.committed_at DESC
LIMIT ?
`).all(...(repoId ? [repoId, limit] : [limit]));
// Filter out prompt_recorded events because we already have the prompt record itself
const events = db.prepare(`
SELECT 'log' as type, id, timestamp, summary, event_type as author, event_type, severity, details_json as details
FROM events
WHERE event_type NOT IN ('prompt_recorded', 'prompt_processed', 'prompt_received')
${repoId ? 'AND (repo_id = ? OR repo_id IS NULL)' : ''}
ORDER BY timestamp DESC
LIMIT ?
`).all(...(repoId ? [repoId, limit] : [limit]));
const executions = db.prepare(`
SELECT 'execution' as type, e.id, e.started_at as timestamp, e.result_summary as summary, e.executor_name as author, e.status as event_type, e.artifacts_json as details
FROM executions e
JOIN prompts p ON e.prompt_id = p.id
${repoId ? 'WHERE (p.repo_id = ? OR p.repo_id IS NULL)' : ''}
ORDER BY e.started_at DESC
LIMIT ?
`).all(...(repoId ? [repoId, limit] : [limit]));
const unified: TimelineEntry[] = [
...prompts.map((p: any) => ({ ...p, details: { intent: p.event_type, normalized_prompt: p.details } })),
...commits.map((c: any) => ({ ...c, details: { files: safeJsonParse(c.details, []) } })),
...events.map((e: any) => ({ ...e, details: safeJsonParse(e.details, {}) })),
...executions.map((x: any) => ({ ...x, details: safeJsonParse(x.details, {}) }))
];
return unified.sort((a, b) => b.timestamp.localeCompare(a.timestamp)).slice(0, limit);
}
}
function safeJsonParse<T>(value: unknown, fallback: T): T {
if (typeof value !== "string" || value.length === 0) {
return fallback;
}
try {
return JSON.parse(value) as T;
} catch {
return fallback;
}
}