agentgui 1.0.743 → 1.0.745

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
@@ -421,7 +421,8 @@ try {
421
421
  claudeSessionId: 'TEXT',
422
422
  isStreaming: 'INTEGER DEFAULT 0',
423
423
  model: 'TEXT',
424
- subAgent: 'TEXT'
424
+ subAgent: 'TEXT',
425
+ pinned: 'INTEGER DEFAULT 0'
425
426
  };
426
427
 
427
428
  let addedColumns = false;
@@ -644,13 +645,13 @@ export const queries = {
644
645
 
645
646
  getConversationsList() {
646
647
  const stmt = prep(
647
- 'SELECT id, agentId, title, agentType, created_at, updated_at, messageCount, workingDirectory, isStreaming, model, subAgent FROM conversations WHERE status != ? ORDER BY updated_at DESC'
648
+ 'SELECT id, agentId, title, agentType, created_at, updated_at, messageCount, workingDirectory, isStreaming, model, subAgent, pinned FROM conversations WHERE status != ? ORDER BY pinned DESC, updated_at DESC'
648
649
  );
649
650
  return stmt.all('deleted');
650
651
  },
651
652
 
652
653
  getConversations() {
653
- const stmt = prep('SELECT * FROM conversations WHERE status != ? ORDER BY updated_at DESC');
654
+ const stmt = prep('SELECT * FROM conversations WHERE status != ? ORDER BY pinned DESC, updated_at DESC');
654
655
  return stmt.all('deleted');
655
656
  },
656
657
 
@@ -665,11 +666,12 @@ export const queries = {
665
666
  const agentType = data.agentType !== undefined ? data.agentType : conv.agentType;
666
667
  const model = data.model !== undefined ? data.model : conv.model;
667
668
  const subAgent = data.subAgent !== undefined ? data.subAgent : conv.subAgent;
669
+ const pinned = data.pinned !== undefined ? (data.pinned ? 1 : 0) : (conv.pinned || 0);
668
670
 
669
671
  const stmt = prep(
670
- `UPDATE conversations SET title = ?, status = ?, agentId = ?, agentType = ?, model = ?, subAgent = ?, updated_at = ? WHERE id = ?`
672
+ `UPDATE conversations SET title = ?, status = ?, agentId = ?, agentType = ?, model = ?, subAgent = ?, pinned = ?, updated_at = ? WHERE id = ?`
671
673
  );
672
- stmt.run(title, status, agentId, agentType, model, subAgent, now, id);
674
+ stmt.run(title, status, agentId, agentType, model, subAgent, pinned, now, id);
673
675
 
674
676
  return {
675
677
  ...conv,
@@ -679,6 +681,7 @@ export const queries = {
679
681
  agentType,
680
682
  model,
681
683
  subAgent,
684
+ pinned,
682
685
  updated_at: now
683
686
  };
684
687
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.743",
3
+ "version": "1.0.745",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "electron/main.js",
package/server.js CHANGED
@@ -2107,6 +2107,37 @@ const server = http.createServer(async (req, res) => {
2107
2107
  return;
2108
2108
  }
2109
2109
 
2110
+ if (pathOnly === '/api/backup' && req.method === 'GET') {
2111
+ const dbPath = path.join(os.homedir(), '.gmgui', 'data.db');
2112
+ if (!fs.existsSync(dbPath)) { sendJSON(req, res, 404, { error: 'Database not found' }); return; }
2113
+ const stat = fs.statSync(dbPath);
2114
+ res.writeHead(200, { 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename="agentgui-backup.db"', 'Content-Length': stat.size });
2115
+ fs.createReadStream(dbPath).pipe(res);
2116
+ return;
2117
+ }
2118
+
2119
+ if (pathOnly === '/api/restore' && req.method === 'POST') {
2120
+ const dbPath = path.join(os.homedir(), '.gmgui', 'data.db');
2121
+ const backupPath = dbPath + '.bak-' + Date.now();
2122
+ const chunks = [];
2123
+ req.on('data', (chunk) => chunks.push(chunk));
2124
+ req.on('end', () => {
2125
+ try {
2126
+ const body = Buffer.concat(chunks);
2127
+ if (body.length < 100 || body.slice(0, 16).toString() !== 'SQLite format 3\0') {
2128
+ sendJSON(req, res, 400, { error: 'Invalid SQLite database file' });
2129
+ return;
2130
+ }
2131
+ fs.copyFileSync(dbPath, backupPath);
2132
+ fs.writeFileSync(dbPath, body);
2133
+ sendJSON(req, res, 200, { success: true, backupPath, size: body.length });
2134
+ } catch (e) {
2135
+ sendJSON(req, res, 500, { error: e.message });
2136
+ }
2137
+ });
2138
+ return;
2139
+ }
2140
+
2110
2141
  if (pathOnly === '/api/debug/machines' && req.method === 'GET' && process.env.DEBUG) {
2111
2142
  const toolSnap = {};
2112
2143
  for (const [id, actor] of toolInstallMachine.getMachineActors()) {
@@ -914,6 +914,18 @@ class AgentGUIClient {
914
914
  this.scrollToBottom(true);
915
915
  }
916
916
 
917
+ this._streamStartedAt = Date.now();
918
+ this._sessionCost = 0;
919
+ if (this._elapsedTimer) clearInterval(this._elapsedTimer);
920
+ this._elapsedTimer = setInterval(() => {
921
+ const label = streamingDiv?.querySelector('.streaming-indicator-label');
922
+ if (label) {
923
+ const sec = ((Date.now() - this._streamStartedAt) / 1000) | 0;
924
+ const time = sec < 60 ? sec + 's' : Math.floor(sec / 60) + 'm ' + (sec % 60) + 's';
925
+ label.textContent = this._sessionCost > 0 ? time + ' · $' + this._sessionCost.toFixed(4) : time;
926
+ }
927
+ }, 1000);
928
+
917
929
  // Reset rendered block seq tracker for this session
918
930
  this._renderedSeqs[data.sessionId] = new Set();
919
931
 
@@ -1030,6 +1042,10 @@ class AgentGUIClient {
1030
1042
  return;
1031
1043
  }
1032
1044
 
1045
+ if (block.type === 'result' && block.total_cost_usd) {
1046
+ this._sessionCost = (this._sessionCost || 0) + block.total_cost_usd;
1047
+ }
1048
+
1033
1049
  const el = this.renderer.renderBlock(block, data, blocksEl);
1034
1050
  if (el) {
1035
1051
  blocksEl.appendChild(el);
@@ -1116,6 +1132,7 @@ class AgentGUIClient {
1116
1132
  console.error('Streaming error:', data);
1117
1133
  if (window.promptMachineAPI) window.promptMachineAPI.send({ type: 'READY' });
1118
1134
  this._clearThinkingCountdown();
1135
+ if (this._elapsedTimer) { clearInterval(this._elapsedTimer); this._elapsedTimer = null; }
1119
1136
 
1120
1137
  // Hide stop and inject buttons on error
1121
1138
  if (this.ui.stopButton) this.ui.stopButton.classList.remove('visible');
@@ -1194,6 +1211,7 @@ class AgentGUIClient {
1194
1211
  this._dbg('Streaming completed:', data);
1195
1212
  if (window.promptMachineAPI) window.promptMachineAPI.send({ type: 'READY' });
1196
1213
  this._clearThinkingCountdown();
1214
+ if (this._elapsedTimer) { clearInterval(this._elapsedTimer); this._elapsedTimer = null; }
1197
1215
 
1198
1216
  const conversationId = data.conversationId || this.state.currentSession?.conversationId;
1199
1217
  if (conversationId) this.invalidateCache(conversationId);
package/static/theme.js CHANGED
@@ -53,15 +53,18 @@ class ThemeManager {
53
53
  }
54
54
 
55
55
  toggleTheme() {
56
- const currentTheme = document.documentElement.getAttribute('data-theme') || 'light';
57
- const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
58
- this.setTheme(newTheme);
56
+ const saved = localStorage.getItem(this.THEME_KEY);
57
+ const current = document.documentElement.getAttribute('data-theme') || 'light';
58
+ if (saved === 'dark') { this.setTheme('light'); }
59
+ else if (saved === 'light') { localStorage.removeItem(this.THEME_KEY); this.setTheme(this.SYSTEM_DARK_MODE.matches ? 'dark' : 'light'); this.updateThemeIcon('auto'); return; }
60
+ else { this.setTheme('dark'); }
59
61
  }
60
62
 
61
63
  updateThemeIcon(theme) {
62
64
  const icon = document.querySelector('.theme-icon');
63
65
  if (icon) {
64
- icon.textContent = theme === 'dark' ? '☀️' : '🌙';
66
+ icon.textContent = theme === 'auto' ? '⚙️' : theme === 'dark' ? '☀️' : '🌙';
67
+ icon.title = theme === 'auto' ? 'Theme: Auto (follows OS)' : theme === 'dark' ? 'Theme: Dark' : 'Theme: Light';
65
68
  }
66
69
  }
67
70