agentgui 1.0.833 → 1.0.834
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -0
- package/lib/db-queries-chunks.js +195 -0
- package/lib/db-queries-chunks2.js +82 -0
- package/lib/db-queries-cleanup.js +85 -0
- package/lib/db-queries-del.js +142 -0
- package/lib/db-queries-events.js +68 -0
- package/lib/db-queries-import.js +133 -0
- package/lib/db-queries-messages.js +102 -0
- package/lib/db-queries-sessions.js +112 -0
- package/lib/db-queries-streams.js +100 -0
- package/lib/db-queries-tools.js +127 -0
- package/lib/db-queries-voice.js +85 -0
- package/lib/db-queries.js +92 -1412
- package/package.json +1 -1
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
|
|
5
|
+
export function addImportQueries(q, db, prep, generateId) {
|
|
6
|
+
q.discoverClaudeCodeConversations = function() {
|
|
7
|
+
const projectsDir = path.join(os.homedir(), '.claude', 'projects');
|
|
8
|
+
if (!fs.existsSync(projectsDir)) return [];
|
|
9
|
+
|
|
10
|
+
const discovered = [];
|
|
11
|
+
try {
|
|
12
|
+
const dirs = fs.readdirSync(projectsDir, { withFileTypes: true });
|
|
13
|
+
for (const dir of dirs) {
|
|
14
|
+
if (!dir.isDirectory()) continue;
|
|
15
|
+
const dirPath = path.join(projectsDir, dir.name);
|
|
16
|
+
const indexPath = path.join(dirPath, 'sessions-index.json');
|
|
17
|
+
if (!fs.existsSync(indexPath)) continue;
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const index = JSON.parse(fs.readFileSync(indexPath, 'utf-8'));
|
|
21
|
+
const projectPath = index.originalPath || dir.name.replace(/^-/, '/').replace(/-/g, '/');
|
|
22
|
+
for (const entry of (index.entries || [])) {
|
|
23
|
+
if (!entry.sessionId || entry.messageCount === 0) continue;
|
|
24
|
+
discovered.push({
|
|
25
|
+
id: entry.sessionId,
|
|
26
|
+
jsonlPath: entry.fullPath || path.join(dirPath, `${entry.sessionId}.jsonl`),
|
|
27
|
+
title: entry.summary || entry.firstPrompt || 'Claude Code Session',
|
|
28
|
+
projectPath,
|
|
29
|
+
created: entry.created ? new Date(entry.created).getTime() : entry.fileMtime,
|
|
30
|
+
modified: entry.modified ? new Date(entry.modified).getTime() : entry.fileMtime,
|
|
31
|
+
messageCount: entry.messageCount,
|
|
32
|
+
gitBranch: entry.gitBranch,
|
|
33
|
+
source: 'claude-code'
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
} catch (e) {
|
|
37
|
+
console.error(`Error reading index ${indexPath}:`, e.message);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
} catch (e) {
|
|
41
|
+
console.error('Error discovering Claude Code conversations:', e.message);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return discovered;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
q.parseJsonlMessages = function(jsonlPath) {
|
|
48
|
+
if (!fs.existsSync(jsonlPath)) return [];
|
|
49
|
+
const messages = [];
|
|
50
|
+
try {
|
|
51
|
+
const lines = fs.readFileSync(jsonlPath, 'utf-8').split('\n');
|
|
52
|
+
for (const line of lines) {
|
|
53
|
+
if (!line.trim()) continue;
|
|
54
|
+
try {
|
|
55
|
+
const obj = JSON.parse(line);
|
|
56
|
+
if (obj.type === 'user' && obj.message?.content) {
|
|
57
|
+
const content = typeof obj.message.content === 'string'
|
|
58
|
+
? obj.message.content
|
|
59
|
+
: Array.isArray(obj.message.content)
|
|
60
|
+
? obj.message.content.filter(c => c.type === 'text').map(c => c.text).join('\n')
|
|
61
|
+
: JSON.stringify(obj.message.content);
|
|
62
|
+
if (content && !content.startsWith('[{"tool_use_id"')) {
|
|
63
|
+
messages.push({ id: obj.uuid || generateId('msg'), role: 'user', content, created_at: new Date(obj.timestamp).getTime() });
|
|
64
|
+
}
|
|
65
|
+
} else if (obj.type === 'assistant' && obj.message?.content) {
|
|
66
|
+
let text = '';
|
|
67
|
+
const content = obj.message.content;
|
|
68
|
+
if (Array.isArray(content)) {
|
|
69
|
+
const textBlocks = [];
|
|
70
|
+
for (const c of content) {
|
|
71
|
+
if (c.type === 'text' && c.text) {
|
|
72
|
+
textBlocks.push(c.text);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
text = textBlocks.join('\n\n');
|
|
76
|
+
} else if (typeof content === 'string') {
|
|
77
|
+
text = content;
|
|
78
|
+
}
|
|
79
|
+
if (text) {
|
|
80
|
+
messages.push({ id: obj.uuid || generateId('msg'), role: 'assistant', content: text, created_at: new Date(obj.timestamp).getTime() });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
} catch (_) {}
|
|
84
|
+
}
|
|
85
|
+
} catch (e) {
|
|
86
|
+
console.error(`Error parsing JSONL ${jsonlPath}:`, e.message);
|
|
87
|
+
}
|
|
88
|
+
return messages;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
q.importClaudeCodeConversations = function() {
|
|
92
|
+
const discovered = this.discoverClaudeCodeConversations();
|
|
93
|
+
const imported = [];
|
|
94
|
+
|
|
95
|
+
for (const conv of discovered) {
|
|
96
|
+
try {
|
|
97
|
+
const existingConv = prep('SELECT id, status FROM conversations WHERE id = ? OR externalId = ?').get(conv.id, conv.id);
|
|
98
|
+
if (existingConv) {
|
|
99
|
+
imported.push({ id: conv.id, status: 'skipped', reason: existingConv.status === 'deleted' ? 'deleted' : 'exists' });
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const projectName = conv.projectPath ? path.basename(conv.projectPath) : '';
|
|
104
|
+
const title = conv.title || 'Claude Code Session';
|
|
105
|
+
const displayTitle = projectName ? `[${projectName}] ${title}` : title;
|
|
106
|
+
|
|
107
|
+
const messages = this.parseJsonlMessages(conv.jsonlPath);
|
|
108
|
+
|
|
109
|
+
const importStmt = db.transaction(() => {
|
|
110
|
+
prep(
|
|
111
|
+
`INSERT INTO conversations (id, agentId, title, created_at, updated_at, status, claudeSessionId) VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
112
|
+
).run(conv.id, 'claude-code', displayTitle, conv.created, conv.modified, 'active', conv.id);
|
|
113
|
+
|
|
114
|
+
for (const msg of messages) {
|
|
115
|
+
try {
|
|
116
|
+
prep(
|
|
117
|
+
`INSERT INTO messages (id, conversationId, role, content, created_at) VALUES (?, ?, ?, ?, ?)`
|
|
118
|
+
).run(msg.id, conv.id, msg.role, msg.content, msg.created_at);
|
|
119
|
+
} catch (_) {}
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
importStmt();
|
|
124
|
+
imported.push({ id: conv.id, status: 'imported', title: displayTitle, messages: messages.length });
|
|
125
|
+
} catch (e) {
|
|
126
|
+
imported.push({ id: conv.id, status: 'error', error: e.message });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return imported;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
export function addMessageQueries(q, db, prep, generateId) {
|
|
2
|
+
q.createMessage = function(conversationId, role, content, idempotencyKey = null) {
|
|
3
|
+
if (idempotencyKey) {
|
|
4
|
+
const cached = this.getIdempotencyKey(idempotencyKey);
|
|
5
|
+
if (cached) return JSON.parse(cached);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const id = generateId('msg');
|
|
9
|
+
const now = Date.now();
|
|
10
|
+
const storedContent = typeof content === 'string' ? content : JSON.stringify(content);
|
|
11
|
+
|
|
12
|
+
const stmt = prep(
|
|
13
|
+
`INSERT INTO messages (id, conversationId, role, content, created_at) VALUES (?, ?, ?, ?, ?)`
|
|
14
|
+
);
|
|
15
|
+
stmt.run(id, conversationId, role, storedContent, now);
|
|
16
|
+
|
|
17
|
+
try { prep('INSERT INTO messages_fts(rowid, content, conversationId, role) VALUES ((SELECT rowid FROM messages WHERE id = ?), ?, ?, ?)').run(id, storedContent, conversationId, role); } catch (_) {}
|
|
18
|
+
|
|
19
|
+
const updateConvStmt = prep('UPDATE conversations SET updated_at = ? WHERE id = ?');
|
|
20
|
+
updateConvStmt.run(now, conversationId);
|
|
21
|
+
|
|
22
|
+
const message = {
|
|
23
|
+
id,
|
|
24
|
+
conversationId,
|
|
25
|
+
role,
|
|
26
|
+
content,
|
|
27
|
+
created_at: now
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
if (idempotencyKey) {
|
|
31
|
+
this.setIdempotencyKey(idempotencyKey, message);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return message;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
q.getMessage = function(id) {
|
|
38
|
+
const stmt = prep('SELECT * FROM messages WHERE id = ?');
|
|
39
|
+
const msg = stmt.get(id);
|
|
40
|
+
if (msg && typeof msg.content === 'string') {
|
|
41
|
+
try {
|
|
42
|
+
msg.content = JSON.parse(msg.content);
|
|
43
|
+
} catch (_) {
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return msg;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
q.getConversationMessages = function(conversationId) {
|
|
50
|
+
const stmt = prep(
|
|
51
|
+
'SELECT * FROM messages WHERE conversationId = ? ORDER BY created_at ASC'
|
|
52
|
+
);
|
|
53
|
+
const messages = stmt.all(conversationId);
|
|
54
|
+
return messages.map(msg => {
|
|
55
|
+
if (typeof msg.content === 'string') {
|
|
56
|
+
try {
|
|
57
|
+
msg.content = JSON.parse(msg.content);
|
|
58
|
+
} catch (_) {
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return msg;
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
q.getLastUserMessage = function(conversationId) {
|
|
66
|
+
const stmt = prep(
|
|
67
|
+
"SELECT * FROM messages WHERE conversationId = ? AND role = 'user' ORDER BY created_at DESC LIMIT 1"
|
|
68
|
+
);
|
|
69
|
+
const msg = stmt.get(conversationId);
|
|
70
|
+
if (msg && typeof msg.content === 'string') {
|
|
71
|
+
try { msg.content = JSON.parse(msg.content); } catch (_) {}
|
|
72
|
+
}
|
|
73
|
+
return msg || null;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
q.getPaginatedMessages = function(conversationId, limit = 50, offset = 0) {
|
|
77
|
+
const countStmt = prep('SELECT COUNT(*) as count FROM messages WHERE conversationId = ?');
|
|
78
|
+
const total = countStmt.get(conversationId).count;
|
|
79
|
+
|
|
80
|
+
const stmt = prep(
|
|
81
|
+
'SELECT * FROM messages WHERE conversationId = ? ORDER BY created_at ASC LIMIT ? OFFSET ?'
|
|
82
|
+
);
|
|
83
|
+
const messages = stmt.all(conversationId, limit, offset);
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
messages: messages.map(msg => {
|
|
87
|
+
if (typeof msg.content === 'string') {
|
|
88
|
+
try {
|
|
89
|
+
msg.content = JSON.parse(msg.content);
|
|
90
|
+
} catch (_) {
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return msg;
|
|
94
|
+
}),
|
|
95
|
+
total,
|
|
96
|
+
limit,
|
|
97
|
+
offset,
|
|
98
|
+
hasMore: offset + limit < total
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
export function addSessionQueries(q, db, prep, generateId) {
|
|
2
|
+
q.createSession = function(conversationId) {
|
|
3
|
+
const id = generateId('sess');
|
|
4
|
+
const now = Date.now();
|
|
5
|
+
|
|
6
|
+
const stmt = prep(
|
|
7
|
+
`INSERT INTO sessions (id, conversationId, status, started_at, completed_at, response, error) VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
8
|
+
);
|
|
9
|
+
stmt.run(id, conversationId, 'pending', now, null, null, null);
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
id,
|
|
13
|
+
conversationId,
|
|
14
|
+
status: 'pending',
|
|
15
|
+
started_at: now,
|
|
16
|
+
completed_at: null,
|
|
17
|
+
response: null,
|
|
18
|
+
error: null
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
q.getSession = function(id) {
|
|
23
|
+
const stmt = prep('SELECT * FROM sessions WHERE id = ?');
|
|
24
|
+
return stmt.get(id);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
q.getConversationSessions = function(conversationId) {
|
|
28
|
+
const stmt = prep(
|
|
29
|
+
'SELECT * FROM sessions WHERE conversationId = ? ORDER BY started_at DESC'
|
|
30
|
+
);
|
|
31
|
+
return stmt.all(conversationId);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
q.updateSession = function(id, data) {
|
|
35
|
+
const session = this.getSession(id);
|
|
36
|
+
if (!session) return null;
|
|
37
|
+
|
|
38
|
+
const status = data.status !== undefined ? data.status : session.status;
|
|
39
|
+
const rawResponse = data.response !== undefined ? data.response : session.response;
|
|
40
|
+
const response = rawResponse && typeof rawResponse === 'object' ? JSON.stringify(rawResponse) : rawResponse;
|
|
41
|
+
const error = data.error !== undefined ? data.error : session.error;
|
|
42
|
+
const completed_at = data.completed_at !== undefined ? data.completed_at : session.completed_at;
|
|
43
|
+
|
|
44
|
+
const stmt = prep(
|
|
45
|
+
`UPDATE sessions SET status = ?, response = ?, error = ?, completed_at = ? WHERE id = ?`
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
stmt.run(status, response, error, completed_at, id);
|
|
50
|
+
return {
|
|
51
|
+
...session,
|
|
52
|
+
status,
|
|
53
|
+
response,
|
|
54
|
+
error,
|
|
55
|
+
completed_at
|
|
56
|
+
};
|
|
57
|
+
} catch (e) {
|
|
58
|
+
throw e;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
q.getLatestSession = function(conversationId) {
|
|
63
|
+
const stmt = prep(
|
|
64
|
+
'SELECT * FROM sessions WHERE conversationId = ? ORDER BY started_at DESC LIMIT 1'
|
|
65
|
+
);
|
|
66
|
+
return stmt.get(conversationId) || null;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
q.getSessionsByStatus = function(conversationId, status) {
|
|
70
|
+
const stmt = prep(
|
|
71
|
+
'SELECT * FROM sessions WHERE conversationId = ? AND status = ? ORDER BY started_at DESC'
|
|
72
|
+
);
|
|
73
|
+
return stmt.all(conversationId, status);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
q.getActiveSessions = function() {
|
|
77
|
+
const stmt = prep(
|
|
78
|
+
"SELECT * FROM sessions WHERE status IN ('active', 'pending') ORDER BY started_at DESC"
|
|
79
|
+
);
|
|
80
|
+
return stmt.all();
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
q.getActiveSessionConversationIds = function() {
|
|
84
|
+
const stmt = prep(
|
|
85
|
+
"SELECT DISTINCT conversationId FROM sessions WHERE status IN ('active', 'pending')"
|
|
86
|
+
);
|
|
87
|
+
return stmt.all().map(r => r.conversationId);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
q.getSessionsByConversation = function(conversationId, limit = 10, offset = 0) {
|
|
91
|
+
const stmt = prep(
|
|
92
|
+
'SELECT * FROM sessions WHERE conversationId = ? ORDER BY started_at DESC LIMIT ? OFFSET ?'
|
|
93
|
+
);
|
|
94
|
+
return stmt.all(conversationId, limit, offset);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
q.getAllSessions = function(limit = 100) {
|
|
98
|
+
const stmt = prep(
|
|
99
|
+
'SELECT * FROM sessions ORDER BY started_at DESC LIMIT ?'
|
|
100
|
+
);
|
|
101
|
+
return stmt.all(limit);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
q.deleteSession = function(id) {
|
|
105
|
+
const stmt = prep('DELETE FROM sessions WHERE id = ?');
|
|
106
|
+
const result = stmt.run(id);
|
|
107
|
+
prep('DELETE FROM chunks WHERE sessionId = ?').run(id);
|
|
108
|
+
prep('DELETE FROM events WHERE sessionId = ?').run(id);
|
|
109
|
+
return result.changes || 0;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
export function addStreamQueries(q, db, prep, generateId) {
|
|
2
|
+
q.createStreamUpdate = function(sessionId, conversationId, updateType, content) {
|
|
3
|
+
const id = generateId('upd');
|
|
4
|
+
const now = Date.now();
|
|
5
|
+
|
|
6
|
+
const transaction = db.transaction(() => {
|
|
7
|
+
const maxSequence = prep(
|
|
8
|
+
'SELECT MAX(sequence) as max FROM stream_updates WHERE sessionId = ?'
|
|
9
|
+
).get(sessionId);
|
|
10
|
+
const sequence = (maxSequence?.max || -1) + 1;
|
|
11
|
+
|
|
12
|
+
prep(
|
|
13
|
+
`INSERT INTO stream_updates (id, sessionId, conversationId, updateType, content, sequence, created_at)
|
|
14
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
15
|
+
).run(id, sessionId, conversationId, updateType, JSON.stringify(content), sequence, now);
|
|
16
|
+
|
|
17
|
+
return sequence;
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const sequence = transaction();
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
id,
|
|
24
|
+
sessionId,
|
|
25
|
+
conversationId,
|
|
26
|
+
updateType,
|
|
27
|
+
content,
|
|
28
|
+
sequence,
|
|
29
|
+
created_at: now
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
q.getSessionStreamUpdates = function(sessionId) {
|
|
34
|
+
const stmt = prep(
|
|
35
|
+
`SELECT id, sessionId, conversationId, updateType, content, sequence, created_at
|
|
36
|
+
FROM stream_updates WHERE sessionId = ? ORDER BY sequence ASC`
|
|
37
|
+
);
|
|
38
|
+
const rows = stmt.all(sessionId);
|
|
39
|
+
return rows.map(row => ({
|
|
40
|
+
...row,
|
|
41
|
+
content: JSON.parse(row.content)
|
|
42
|
+
}));
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
q.clearSessionStreamUpdates = function(sessionId) {
|
|
46
|
+
const stmt = prep('DELETE FROM stream_updates WHERE sessionId = ?');
|
|
47
|
+
stmt.run(sessionId);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
q.createImportedConversation = function(data) {
|
|
51
|
+
const id = generateId('conv');
|
|
52
|
+
const now = Date.now();
|
|
53
|
+
const stmt = prep(
|
|
54
|
+
`INSERT INTO conversations (
|
|
55
|
+
id, agentId, title, created_at, updated_at, status,
|
|
56
|
+
agentType, source, externalId, firstPrompt, messageCount,
|
|
57
|
+
projectPath, gitBranch, sourcePath, lastSyncedAt
|
|
58
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
59
|
+
);
|
|
60
|
+
stmt.run(
|
|
61
|
+
id,
|
|
62
|
+
data.externalId || id,
|
|
63
|
+
data.title,
|
|
64
|
+
data.created || now,
|
|
65
|
+
data.modified || now,
|
|
66
|
+
'active',
|
|
67
|
+
data.agentType || 'claude-code',
|
|
68
|
+
data.source || 'imported',
|
|
69
|
+
data.externalId,
|
|
70
|
+
data.firstPrompt,
|
|
71
|
+
data.messageCount || 0,
|
|
72
|
+
data.projectPath,
|
|
73
|
+
data.gitBranch,
|
|
74
|
+
data.sourcePath,
|
|
75
|
+
now
|
|
76
|
+
);
|
|
77
|
+
return { id, ...data };
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
q.getConversationByExternalId = function(agentType, externalId) {
|
|
81
|
+
const stmt = prep(
|
|
82
|
+
'SELECT * FROM conversations WHERE agentType = ? AND externalId = ?'
|
|
83
|
+
);
|
|
84
|
+
return stmt.get(agentType, externalId);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
q.getConversationsByAgentType = function(agentType) {
|
|
88
|
+
const stmt = prep(
|
|
89
|
+
'SELECT * FROM conversations WHERE agentType = ? AND status != ? ORDER BY updated_at DESC'
|
|
90
|
+
);
|
|
91
|
+
return stmt.all(agentType, 'deleted');
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
q.getImportedConversations = function() {
|
|
95
|
+
const stmt = prep(
|
|
96
|
+
'SELECT * FROM conversations WHERE source = ? AND status != ? ORDER BY updated_at DESC'
|
|
97
|
+
);
|
|
98
|
+
return stmt.all('imported', 'deleted');
|
|
99
|
+
};
|
|
100
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
export function addToolQueries(q, db, prep, generateId) {
|
|
2
|
+
q.initializeToolInstallations = function(tools) {
|
|
3
|
+
const now = Date.now();
|
|
4
|
+
for (const tool of tools) {
|
|
5
|
+
const stmt = prep(`
|
|
6
|
+
INSERT OR IGNORE INTO tool_installations
|
|
7
|
+
(id, tool_id, status, created_at, updated_at)
|
|
8
|
+
VALUES (?, ?, ?, ?, ?)
|
|
9
|
+
`);
|
|
10
|
+
stmt.run(generateId('tinst'), tool.id, 'not_installed', now, now);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
q.getToolStatus = function(toolId) {
|
|
15
|
+
const stmt = prep(`
|
|
16
|
+
SELECT id, tool_id, version, installed_at, status, last_check_at,
|
|
17
|
+
error_message, update_available, latest_version, created_at, updated_at
|
|
18
|
+
FROM tool_installations
|
|
19
|
+
WHERE tool_id = ?
|
|
20
|
+
`);
|
|
21
|
+
return stmt.get(toolId) || null;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
q.getAllToolStatuses = function() {
|
|
25
|
+
const stmt = prep(`
|
|
26
|
+
SELECT id, tool_id, version, installed_at, status, last_check_at,
|
|
27
|
+
error_message, update_available, latest_version, created_at, updated_at
|
|
28
|
+
FROM tool_installations
|
|
29
|
+
ORDER BY tool_id
|
|
30
|
+
`);
|
|
31
|
+
return stmt.all();
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
q.insertToolInstallation = function(toolId, data) {
|
|
35
|
+
const now = Date.now();
|
|
36
|
+
const stmt = prep(`
|
|
37
|
+
INSERT OR IGNORE INTO tool_installations
|
|
38
|
+
(id, tool_id, version, installed_at, status, last_check_at, error_message, update_available, latest_version, created_at, updated_at)
|
|
39
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
40
|
+
`);
|
|
41
|
+
stmt.run(
|
|
42
|
+
generateId('ti'),
|
|
43
|
+
toolId,
|
|
44
|
+
data.version || null,
|
|
45
|
+
data.installed_at || null,
|
|
46
|
+
data.status || 'not_installed',
|
|
47
|
+
now,
|
|
48
|
+
data.error_message || null,
|
|
49
|
+
0,
|
|
50
|
+
null,
|
|
51
|
+
now,
|
|
52
|
+
now
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
q.updateToolStatus = function(toolId, data) {
|
|
57
|
+
const now = Date.now();
|
|
58
|
+
const stmt = prep(`
|
|
59
|
+
UPDATE tool_installations
|
|
60
|
+
SET version = ?, installed_at = ?, status = ?, last_check_at = ?,
|
|
61
|
+
error_message = ?, update_available = ?, latest_version = ?, updated_at = ?
|
|
62
|
+
WHERE tool_id = ?
|
|
63
|
+
`);
|
|
64
|
+
stmt.run(
|
|
65
|
+
data.version || null,
|
|
66
|
+
data.installed_at || null,
|
|
67
|
+
data.status || 'not_installed',
|
|
68
|
+
data.last_check_at || now,
|
|
69
|
+
data.error_message || null,
|
|
70
|
+
data.update_available ? 1 : 0,
|
|
71
|
+
data.latest_version || null,
|
|
72
|
+
now,
|
|
73
|
+
toolId
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
q.addToolInstallHistory = function(toolId, action, status, error) {
|
|
78
|
+
const now = Date.now();
|
|
79
|
+
const stmt = prep(`
|
|
80
|
+
INSERT INTO tool_install_history
|
|
81
|
+
(id, tool_id, action, started_at, completed_at, status, error_message, created_at)
|
|
82
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
83
|
+
`);
|
|
84
|
+
stmt.run(generateId('thist'), toolId, action, now, now, status, error || null, now);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
q.getToolInstallHistory = function(toolId, limit = 20, offset = 0) {
|
|
88
|
+
const stmt = prep(`
|
|
89
|
+
SELECT id, tool_id, action, started_at, completed_at, status, error_message, created_at
|
|
90
|
+
FROM tool_install_history
|
|
91
|
+
WHERE tool_id = ?
|
|
92
|
+
ORDER BY created_at DESC
|
|
93
|
+
LIMIT ? OFFSET ?
|
|
94
|
+
`);
|
|
95
|
+
return stmt.all(toolId, limit, offset);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
q.pruneToolInstallHistory = function(toolId, keepCount = 100) {
|
|
99
|
+
const stmt = prep(`
|
|
100
|
+
DELETE FROM tool_install_history
|
|
101
|
+
WHERE tool_id = ? AND id NOT IN (
|
|
102
|
+
SELECT id FROM tool_install_history
|
|
103
|
+
WHERE tool_id = ?
|
|
104
|
+
ORDER BY created_at DESC
|
|
105
|
+
LIMIT ?
|
|
106
|
+
)
|
|
107
|
+
`);
|
|
108
|
+
return stmt.run(toolId, toolId, keepCount).changes;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
q.searchMessages = function(query, limit = 50) {
|
|
112
|
+
try {
|
|
113
|
+
const sanitized = '"' + query.replace(/"/g, '""') + '"';
|
|
114
|
+
const stmt = prep(`
|
|
115
|
+
SELECT m.id, m.conversationId, m.role, m.content, m.created_at,
|
|
116
|
+
c.title as conversationTitle, c.agentType
|
|
117
|
+
FROM messages_fts fts
|
|
118
|
+
JOIN messages m ON m.rowid = fts.rowid
|
|
119
|
+
JOIN conversations c ON c.id = m.conversationId
|
|
120
|
+
WHERE messages_fts MATCH ?
|
|
121
|
+
ORDER BY m.created_at DESC LIMIT ?
|
|
122
|
+
`);
|
|
123
|
+
return stmt.all(sanitized, limit);
|
|
124
|
+
} catch (_) { return []; }
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export function addVoiceQueries(q, db, prep, generateId) {
|
|
2
|
+
q.getDownloadsByStatus = function(status) {
|
|
3
|
+
const stmt = prep('SELECT * FROM WHERE status = ? ORDER BY started_at DESC');
|
|
4
|
+
return stmt.all(status);
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
q.updateDownloadResume = function(downloadId, currentSize, attempts, lastAttempt, status) {
|
|
8
|
+
const stmt = prep(`
|
|
9
|
+
UPDATE
|
|
10
|
+
SET downloaded_bytes = ?, attempts = ?, lastAttempt = ?, status = ?
|
|
11
|
+
WHERE id = ?
|
|
12
|
+
`);
|
|
13
|
+
stmt.run(currentSize, attempts, lastAttempt, status, downloadId);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
q.updateDownloadHash = function(downloadId, hash) {
|
|
17
|
+
const stmt = prep('UPDATE SET hash = ? WHERE id = ?');
|
|
18
|
+
stmt.run(hash, downloadId);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
q.markDownloadResuming = function(downloadId) {
|
|
22
|
+
const stmt = prep('UPDATE SET status = ?, lastAttempt = ? WHERE id = ?');
|
|
23
|
+
stmt.run('resuming', Date.now(), downloadId);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
q.markDownloadPaused = function(downloadId, errorMessage) {
|
|
27
|
+
const stmt = prep('UPDATE SET status = ?, error_message = ?, lastAttempt = ? WHERE id = ?');
|
|
28
|
+
stmt.run('paused', errorMessage, Date.now(), downloadId);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
q.saveVoiceCache = function(conversationId, text, audioBlob, ttlMs = 3600000) {
|
|
32
|
+
const id = generateId('vcache');
|
|
33
|
+
const now = Date.now();
|
|
34
|
+
const expiresAt = now + ttlMs;
|
|
35
|
+
const byteSize = audioBlob ? Buffer.byteLength(audioBlob) : 0;
|
|
36
|
+
const stmt = prep(`
|
|
37
|
+
INSERT INTO voice_cache (id, conversationId, text, audioBlob, byteSize, created_at, expires_at)
|
|
38
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
39
|
+
`);
|
|
40
|
+
stmt.run(id, conversationId, text, audioBlob || null, byteSize, now, expiresAt);
|
|
41
|
+
return { id, conversationId, text, byteSize, created_at: now, expires_at: expiresAt };
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
q.getVoiceCache = function(conversationId, text) {
|
|
45
|
+
const now = Date.now();
|
|
46
|
+
const stmt = prep(`
|
|
47
|
+
SELECT id, conversationId, text, audioBlob, byteSize, created_at, expires_at
|
|
48
|
+
FROM voice_cache
|
|
49
|
+
WHERE conversationId = ? AND text = ? AND expires_at > ?
|
|
50
|
+
LIMIT 1
|
|
51
|
+
`);
|
|
52
|
+
return stmt.get(conversationId, text, now) || null;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
q.cleanExpiredVoiceCache = function() {
|
|
56
|
+
const now = Date.now();
|
|
57
|
+
const stmt = prep('DELETE FROM voice_cache WHERE expires_at <= ?');
|
|
58
|
+
return stmt.run(now).changes;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
q.getVoiceCacheSize = function(conversationId) {
|
|
62
|
+
const now = Date.now();
|
|
63
|
+
const stmt = prep(`
|
|
64
|
+
SELECT COALESCE(SUM(byteSize), 0) as totalSize
|
|
65
|
+
FROM voice_cache
|
|
66
|
+
WHERE conversationId = ? AND expires_at > ?
|
|
67
|
+
`);
|
|
68
|
+
return stmt.get(conversationId, now).totalSize || 0;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
q.deleteOldestVoiceCache = function(conversationId, neededBytes) {
|
|
72
|
+
const stmt = prep(`
|
|
73
|
+
SELECT id FROM voice_cache
|
|
74
|
+
WHERE conversationId = ?
|
|
75
|
+
ORDER BY created_at ASC
|
|
76
|
+
LIMIT (SELECT COUNT(*) FROM voice_cache WHERE conversationId = ? AND byteSize > ?)
|
|
77
|
+
`);
|
|
78
|
+
const oldest = stmt.all(conversationId, conversationId, neededBytes);
|
|
79
|
+
const deleteStmt = prep('DELETE FROM voice_cache WHERE id = ?');
|
|
80
|
+
for (const row of oldest) {
|
|
81
|
+
deleteStmt.run(row.id);
|
|
82
|
+
}
|
|
83
|
+
return oldest.length;
|
|
84
|
+
};
|
|
85
|
+
}
|