agentgui 1.0.745 → 1.0.747

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/database.js CHANGED
@@ -422,7 +422,8 @@ try {
422
422
  isStreaming: 'INTEGER DEFAULT 0',
423
423
  model: 'TEXT',
424
424
  subAgent: 'TEXT',
425
- pinned: 'INTEGER DEFAULT 0'
425
+ pinned: 'INTEGER DEFAULT 0',
426
+ tags: 'TEXT'
426
427
  };
427
428
 
428
429
  let addedColumns = false;
@@ -595,6 +596,21 @@ try {
595
596
  console.error('[Migration] Backfill error:', err.message);
596
597
  }
597
598
 
599
+ try {
600
+ const hasFts = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='messages_fts'").get();
601
+ if (!hasFts) {
602
+ db.exec("CREATE VIRTUAL TABLE messages_fts USING fts5(content, conversationId UNINDEXED, role UNINDEXED, content_rowid='rowid')");
603
+ const msgs = db.prepare("SELECT rowid, content, conversationId, role FROM messages").all();
604
+ if (msgs.length > 0) {
605
+ const ins = db.prepare("INSERT INTO messages_fts(rowid, content, conversationId, role) VALUES (?, ?, ?, ?)");
606
+ const tx = db.transaction(() => { for (const m of msgs) ins.run(m.rowid, m.content, m.conversationId, m.role); });
607
+ tx();
608
+ console.log(`[Migration] FTS5 index created with ${msgs.length} messages`);
609
+ }
610
+ }
611
+ } catch (err) {
612
+ console.error('[Migration] FTS5 error:', err.message);
613
+ }
598
614
 
599
615
  const stmtCache = new Map();
600
616
  function prep(sql) {
@@ -667,11 +683,12 @@ export const queries = {
667
683
  const model = data.model !== undefined ? data.model : conv.model;
668
684
  const subAgent = data.subAgent !== undefined ? data.subAgent : conv.subAgent;
669
685
  const pinned = data.pinned !== undefined ? (data.pinned ? 1 : 0) : (conv.pinned || 0);
686
+ const tags = data.tags !== undefined ? (Array.isArray(data.tags) ? data.tags.join(',') : data.tags) : (conv.tags || null);
670
687
 
671
688
  const stmt = prep(
672
- `UPDATE conversations SET title = ?, status = ?, agentId = ?, agentType = ?, model = ?, subAgent = ?, pinned = ?, updated_at = ? WHERE id = ?`
689
+ `UPDATE conversations SET title = ?, status = ?, agentId = ?, agentType = ?, model = ?, subAgent = ?, pinned = ?, tags = ?, updated_at = ? WHERE id = ?`
673
690
  );
674
- stmt.run(title, status, agentId, agentType, model, subAgent, pinned, now, id);
691
+ stmt.run(title, status, agentId, agentType, model, subAgent, pinned, tags, now, id);
675
692
 
676
693
  return {
677
694
  ...conv,
@@ -682,6 +699,7 @@ export const queries = {
682
699
  model,
683
700
  subAgent,
684
701
  pinned,
702
+ tags,
685
703
  updated_at: now
686
704
  };
687
705
  },
@@ -774,6 +792,8 @@ export const queries = {
774
792
  );
775
793
  stmt.run(id, conversationId, role, storedContent, now);
776
794
 
795
+ try { prep('INSERT INTO messages_fts(rowid, content, conversationId, role) VALUES ((SELECT rowid FROM messages WHERE id = ?), ?, ?, ?)').run(id, storedContent, conversationId, role); } catch (_) {}
796
+
777
797
  const updateConvStmt = prep('UPDATE conversations SET updated_at = ? WHERE id = ?');
778
798
  updateConvStmt.run(now, conversationId);
779
799
 
@@ -1924,6 +1944,21 @@ export const queries = {
1924
1944
  return stmt.run(toolId, toolId, keepCount).changes;
1925
1945
  },
1926
1946
 
1947
+ searchMessages(query, limit = 50) {
1948
+ try {
1949
+ const stmt = prep(`
1950
+ SELECT m.id, m.conversationId, m.role, m.content, m.created_at,
1951
+ c.title as conversationTitle, c.agentType
1952
+ FROM messages_fts fts
1953
+ JOIN messages m ON m.rowid = fts.rowid
1954
+ JOIN conversations c ON c.id = m.conversationId
1955
+ WHERE messages_fts MATCH ?
1956
+ ORDER BY m.created_at DESC LIMIT ?
1957
+ `);
1958
+ return stmt.all(query, limit);
1959
+ } catch (_) { return []; }
1960
+ },
1961
+
1927
1962
  // ============ ACP-COMPATIBLE QUERIES ============
1928
1963
  ...createACPQueries(db, prep)
1929
1964
  };
@@ -22,11 +22,19 @@ export function register(router, deps) {
22
22
  getJsonlWatcher = () => null } = deps;
23
23
 
24
24
  router.handle('conv.ls', () => {
25
- const conversations = queries.getConversationsList();
25
+ let conversations = queries.getConversationsList();
26
26
  for (const c of conversations) { if (c.isStreaming && !execMachine.isActive(c.id)) c.isStreaming = 0; }
27
+ if (p.tag) conversations = conversations.filter(c => c.tags && c.tags.split(',').includes(p.tag));
27
28
  return { conversations };
28
29
  });
29
30
 
31
+ router.handle('conv.tags', () => {
32
+ const convs = queries.getConversationsList();
33
+ const tagSet = new Set();
34
+ for (const c of convs) { if (c.tags) c.tags.split(',').forEach(t => { if (t.trim()) tagSet.add(t.trim()); }); }
35
+ return { tags: [...tagSet].sort() };
36
+ });
37
+
30
38
  router.handle('conv.new', (p) => {
31
39
  p = validate(ConvNewSchema, p);
32
40
  const wd = p.workingDirectory ? path.resolve(expandTilde(p.workingDirectory)) : null;
@@ -135,6 +143,53 @@ export function register(router, deps) {
135
143
  return { markdown: md, title: conv.title };
136
144
  });
137
145
 
146
+ router.handle('conv.sync', (p) => {
147
+ const conv = queries.getConversation(p.id);
148
+ if (!conv) notFound();
149
+ const machineSnap = execMachine.snapshot(p.id);
150
+ const executionState = machineSnap?.value || 'idle';
151
+ const latestSession = queries.getLatestSession(p.id);
152
+ const sinceSeq = parseInt(p.sinceSeq || '-1');
153
+ const since = parseInt(p.since || '0');
154
+ let missedChunks = [];
155
+ if (latestSession && sinceSeq >= 0) {
156
+ missedChunks = queries.getChunksSinceSeq(latestSession.id, sinceSeq);
157
+ } else if (latestSession && since > 0) {
158
+ missedChunks = queries.getConversationChunksSince(p.id, since);
159
+ }
160
+ return {
161
+ conversation: conv,
162
+ executionState,
163
+ isActivelyStreaming: execMachine.isActive(p.id),
164
+ latestSession,
165
+ missedChunks,
166
+ missedCount: missedChunks.length,
167
+ queueLength: machineSnap?.context?.queue?.length || 0
168
+ };
169
+ });
170
+
171
+ router.handle('conv.search', (p) => {
172
+ if (!p.query || typeof p.query !== 'string' || p.query.trim().length < 2) return { results: [] };
173
+ const limit = Math.min(parseInt(p.limit || '50'), 200);
174
+ return { results: queries.searchMessages(p.query.trim(), limit) };
175
+ });
176
+
177
+ router.handle('conv.prune', (p) => {
178
+ const conv = queries.getConversation(p.id);
179
+ if (!conv) notFound();
180
+ const keep = Math.max(parseInt(p.keep || '500'), 100);
181
+ const sessions = queries.getConversationSessions(p.id);
182
+ if (!sessions || sessions.length === 0) return { pruned: 0 };
183
+ const latestSessionId = sessions[0]?.id;
184
+ let pruned = 0;
185
+ for (const sess of sessions) {
186
+ if (sess.id === latestSessionId) continue;
187
+ const count = queries.getSessionChunks(sess.id)?.length || 0;
188
+ if (count > 0) { queries.deleteSessionChunks(sess.id); pruned += count; }
189
+ }
190
+ return { pruned, kept: latestSessionId, sessionsProcessed: sessions.length };
191
+ });
192
+
138
193
  router.handle('conv.cancel', (p) => {
139
194
  if (!execMachine.isActive(p.id)) notFound('No active execution to cancel');
140
195
  const ctx = execMachine.getContext(p.id);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.745",
3
+ "version": "1.0.747",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "electron/main.js",