agentacta 1.1.0 → 1.1.2
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/db.js +2 -1
- package/indexer.js +11 -5
- package/package.json +1 -1
- package/public/app.js +75 -16
package/db.js
CHANGED
|
@@ -114,6 +114,7 @@ function init(dbPath) {
|
|
|
114
114
|
if (!cols.includes('output_tokens')) db.exec("ALTER TABLE sessions ADD COLUMN output_tokens INTEGER DEFAULT 0");
|
|
115
115
|
if (!cols.includes('cache_read_tokens')) db.exec("ALTER TABLE sessions ADD COLUMN cache_read_tokens INTEGER DEFAULT 0");
|
|
116
116
|
if (!cols.includes('cache_write_tokens')) db.exec("ALTER TABLE sessions ADD COLUMN cache_write_tokens INTEGER DEFAULT 0");
|
|
117
|
+
if (!cols.includes('models')) db.exec("ALTER TABLE sessions ADD COLUMN models TEXT");
|
|
117
118
|
|
|
118
119
|
db.close();
|
|
119
120
|
}
|
|
@@ -126,7 +127,7 @@ function createStmts(db) {
|
|
|
126
127
|
deleteSession: db.prepare('DELETE FROM sessions WHERE id = ?'),
|
|
127
128
|
deleteFileActivity: db.prepare('DELETE FROM file_activity WHERE session_id = ?'),
|
|
128
129
|
insertEvent: db.prepare(`INSERT OR REPLACE INTO events (id, session_id, timestamp, type, role, content, tool_name, tool_args, tool_result) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`),
|
|
129
|
-
upsertSession: db.prepare(`INSERT OR REPLACE INTO sessions (id, start_time, end_time, message_count, tool_count, model, summary, agent, session_type, total_cost, total_tokens, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, initial_prompt, first_message_id, first_message_timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
|
|
130
|
+
upsertSession: db.prepare(`INSERT OR REPLACE INTO sessions (id, start_time, end_time, message_count, tool_count, model, summary, agent, session_type, total_cost, total_tokens, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, initial_prompt, first_message_id, first_message_timestamp, models) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
|
|
130
131
|
upsertState: db.prepare(`INSERT OR REPLACE INTO index_state (file_path, last_offset, last_modified) VALUES (?, ?, ?)`),
|
|
131
132
|
insertFileActivity: db.prepare(`INSERT INTO file_activity (session_id, file_path, operation, timestamp) VALUES (?, ?, ?, ?)`),
|
|
132
133
|
deleteArchive: db.prepare('DELETE FROM archive WHERE session_id = ?'),
|
package/indexer.js
CHANGED
|
@@ -126,6 +126,7 @@ function indexFile(db, filePath, agentName, stmts, archiveMode) {
|
|
|
126
126
|
let msgCount = 0;
|
|
127
127
|
let toolCount = 0;
|
|
128
128
|
let model = null;
|
|
129
|
+
const modelsSet = new Set();
|
|
129
130
|
let summary = '';
|
|
130
131
|
let sessionType = null;
|
|
131
132
|
let agent = agentName;
|
|
@@ -182,7 +183,10 @@ function indexFile(db, filePath, agentName, stmts, archiveMode) {
|
|
|
182
183
|
try { obj = JSON.parse(line); } catch { continue; }
|
|
183
184
|
|
|
184
185
|
if (obj.type === 'session' || obj.type === 'model_change' || obj.type === 'thinking_level_change' || obj.type === 'custom' || obj.type === 'file-history-snapshot') {
|
|
185
|
-
if (obj.type === 'model_change'
|
|
186
|
+
if (obj.type === 'model_change' && obj.modelId) {
|
|
187
|
+
if (!model) model = obj.modelId; // First model for backwards compat
|
|
188
|
+
modelsSet.add(obj.modelId); // Collect all unique models
|
|
189
|
+
}
|
|
186
190
|
continue;
|
|
187
191
|
}
|
|
188
192
|
|
|
@@ -204,9 +208,10 @@ function indexFile(db, filePath, agentName, stmts, archiveMode) {
|
|
|
204
208
|
if (msg) {
|
|
205
209
|
sessionEnd = ts;
|
|
206
210
|
|
|
207
|
-
// Extract model from assistant messages
|
|
208
|
-
if (
|
|
209
|
-
model = msg.model;
|
|
211
|
+
// Extract model from assistant messages
|
|
212
|
+
if (msg.role === 'assistant' && msg.model && msg.model !== 'delivery-mirror' && !msg.model.startsWith('<')) {
|
|
213
|
+
if (!model) model = msg.model; // Keep first model for backwards compat
|
|
214
|
+
modelsSet.add(msg.model); // Collect all unique models
|
|
210
215
|
}
|
|
211
216
|
|
|
212
217
|
// Cost tracking
|
|
@@ -292,7 +297,8 @@ function indexFile(db, filePath, agentName, stmts, archiveMode) {
|
|
|
292
297
|
if (/^\[(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s+\d{4}-/.test(p)) sessionType = 'subagent';
|
|
293
298
|
}
|
|
294
299
|
|
|
295
|
-
|
|
300
|
+
const modelsJson = modelsSet.size > 0 ? JSON.stringify([...modelsSet]) : null;
|
|
301
|
+
stmts.upsertSession.run(sessionId, sessionStart, sessionEnd, msgCount, toolCount, model, summary, agent, sessionType, totalCost, totalTokens, totalInputTokens, totalOutputTokens, totalCacheReadTokens, totalCacheWriteTokens, initialPrompt, firstMessageId, firstMessageTimestamp, modelsJson);
|
|
296
302
|
for (const ev of pendingEvents) stmts.insertEvent.run(...ev);
|
|
297
303
|
for (const fa of fileActivities) stmts.insertFileActivity.run(...fa);
|
|
298
304
|
|
package/package.json
CHANGED
package/public/app.js
CHANGED
|
@@ -113,6 +113,16 @@ function fmtTimeOnly(ts) {
|
|
|
113
113
|
return new Date(ts).toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true });
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
function renderModelTags(s) {
|
|
117
|
+
// Prefer models array if present, fall back to single model
|
|
118
|
+
let models = [];
|
|
119
|
+
if (s.models) {
|
|
120
|
+
try { models = JSON.parse(s.models); } catch {}
|
|
121
|
+
}
|
|
122
|
+
if (!models.length && s.model) models = [s.model];
|
|
123
|
+
return models.map(m => `<span class="session-model">${escHtml(m)}</span>`).join('');
|
|
124
|
+
}
|
|
125
|
+
|
|
116
126
|
function renderSessionItem(s) {
|
|
117
127
|
const duration = fmtDuration(s.start_time, s.end_time);
|
|
118
128
|
const timeRange = `${fmtTime(s.start_time)} → ${s.end_time ? fmtTimeOnly(s.end_time) : 'now'}`;
|
|
@@ -121,10 +131,10 @@ function renderSessionItem(s) {
|
|
|
121
131
|
<div class="session-item" data-id="${s.id}">
|
|
122
132
|
<div class="session-header">
|
|
123
133
|
<span class="session-time">${timeRange} · ${duration}</span>
|
|
124
|
-
<span style="display:flex;gap:6px;align-items:center">
|
|
134
|
+
<span style="display:flex;gap:6px;align-items:center;flex-wrap:wrap">
|
|
125
135
|
${s.agent && s.agent !== 'main' ? `<span class="session-agent">${escHtml(s.agent)}</span>` : ''}
|
|
126
136
|
${s.session_type ? `<span class="session-type">${escHtml(s.session_type)}</span>` : ''}
|
|
127
|
-
${
|
|
137
|
+
${renderModelTags(s)}
|
|
128
138
|
</span>
|
|
129
139
|
</div>
|
|
130
140
|
<div class="session-summary">${escHtml(truncate(s.summary || 'No summary', 120))}</div>
|
|
@@ -218,7 +228,11 @@ async function showSearchHome() {
|
|
|
218
228
|
});
|
|
219
229
|
|
|
220
230
|
$$('.session-item', el).forEach(item => {
|
|
221
|
-
item.addEventListener('click', () =>
|
|
231
|
+
item.addEventListener('click', () => {
|
|
232
|
+
window._lastView = 'search';
|
|
233
|
+
window._lastSearchQuery = $('#searchInput')?.value || '';
|
|
234
|
+
viewSession(item.dataset.id);
|
|
235
|
+
});
|
|
222
236
|
});
|
|
223
237
|
}
|
|
224
238
|
|
|
@@ -260,11 +274,16 @@ async function doSearch(q) {
|
|
|
260
274
|
`).join('');
|
|
261
275
|
|
|
262
276
|
$$('.session-link', el).forEach(link => {
|
|
263
|
-
link.addEventListener('click', () =>
|
|
277
|
+
link.addEventListener('click', () => {
|
|
278
|
+
window._lastView = 'search';
|
|
279
|
+
window._lastSearchQuery = q;
|
|
280
|
+
viewSession(link.dataset.session);
|
|
281
|
+
});
|
|
264
282
|
});
|
|
265
283
|
}
|
|
266
284
|
|
|
267
285
|
async function viewSessions() {
|
|
286
|
+
window._currentSessionId = null;
|
|
268
287
|
content.innerHTML = '<div class="loading">Loading…</div>';
|
|
269
288
|
const data = await api('/sessions?limit=200');
|
|
270
289
|
|
|
@@ -278,6 +297,7 @@ async function viewSessions() {
|
|
|
278
297
|
}
|
|
279
298
|
|
|
280
299
|
async function viewSession(id) {
|
|
300
|
+
window._currentSessionId = id;
|
|
281
301
|
content.innerHTML = '<div class="loading">Loading…</div>';
|
|
282
302
|
const data = await api(`/sessions/${id}`);
|
|
283
303
|
|
|
@@ -287,22 +307,20 @@ async function viewSession(id) {
|
|
|
287
307
|
const cost = fmtCost(s.total_cost);
|
|
288
308
|
let html = `
|
|
289
309
|
<div class="back-btn" id="backBtn">← Back</div>
|
|
290
|
-
<div
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
<a class="export-btn" href="#" onclick="dlExport('/api/export/session/${id}?format=json','session.json');return false">📋 JSON</a>
|
|
297
|
-
</div>
|
|
310
|
+
<div class="page-title">Session</div>
|
|
311
|
+
<div style="display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin-bottom:8px">
|
|
312
|
+
${s.first_message_id ? `<button class="jump-to-start-btn" id="jumpToStartBtn" title="Jump to initial prompt">↗️ Initial Prompt</button>` : ''}
|
|
313
|
+
${data.hasArchive ? `<a class="export-btn" href="#" onclick="dlExport('/api/archive/export/${id}','session.jsonl');return false">📦 JSONL</a>` : ''}
|
|
314
|
+
<a class="export-btn" href="#" onclick="dlExport('/api/export/session/${id}?format=md','session.md');return false">📄 MD</a>
|
|
315
|
+
<a class="export-btn" href="#" onclick="dlExport('/api/export/session/${id}?format=json','session.json');return false">📋 JSON</a>
|
|
298
316
|
</div>
|
|
299
317
|
<div class="session-item" style="cursor:default">
|
|
300
318
|
<div class="session-header">
|
|
301
319
|
<span class="session-time">${fmtDate(s.start_time)} · ${fmtTimeShort(s.start_time)} – ${fmtTimeShort(s.end_time)}</span>
|
|
302
|
-
<span style="display:flex;gap:6px;align-items:center">
|
|
320
|
+
<span style="display:flex;gap:6px;align-items:center;flex-wrap:wrap">
|
|
303
321
|
${s.agent && s.agent !== 'main' ? `<span class="session-agent">${escHtml(s.agent)}</span>` : ''}
|
|
304
322
|
${s.session_type ? `<span class="session-type">${escHtml(s.session_type)}</span>` : ''}
|
|
305
|
-
${
|
|
323
|
+
${renderModelTags(s)}
|
|
306
324
|
</span>
|
|
307
325
|
</div>
|
|
308
326
|
<div class="session-meta" style="display:grid;grid-template-columns:repeat(2,1fr);gap:6px 16px">
|
|
@@ -320,6 +338,7 @@ async function viewSession(id) {
|
|
|
320
338
|
$('#backBtn').addEventListener('click', () => {
|
|
321
339
|
if (window._lastView === 'timeline') viewTimeline();
|
|
322
340
|
else if (window._lastView === 'files') viewFiles();
|
|
341
|
+
else if (window._lastView === 'search') viewSearch(window._lastSearchQuery || '');
|
|
323
342
|
else viewSessions();
|
|
324
343
|
});
|
|
325
344
|
|
|
@@ -613,6 +632,40 @@ $$('.nav-item').forEach(item => {
|
|
|
613
632
|
|
|
614
633
|
viewSearch();
|
|
615
634
|
|
|
635
|
+
// Swipe right from left edge to go back
|
|
636
|
+
(function initSwipeBack() {
|
|
637
|
+
let startX = 0, startY = 0, swiping = false;
|
|
638
|
+
const edgeWidth = 30; // px from left edge
|
|
639
|
+
const threshold = 80;
|
|
640
|
+
|
|
641
|
+
document.addEventListener('touchstart', e => {
|
|
642
|
+
const x = e.touches[0].clientX;
|
|
643
|
+
if (x <= edgeWidth) {
|
|
644
|
+
startX = x;
|
|
645
|
+
startY = e.touches[0].clientY;
|
|
646
|
+
swiping = true;
|
|
647
|
+
}
|
|
648
|
+
}, { passive: true });
|
|
649
|
+
|
|
650
|
+
document.addEventListener('touchmove', e => {
|
|
651
|
+
if (!swiping) return;
|
|
652
|
+
const dx = e.touches[0].clientX - startX;
|
|
653
|
+
const dy = Math.abs(e.touches[0].clientY - startY);
|
|
654
|
+
// Cancel if vertical movement exceeds horizontal (it's a scroll)
|
|
655
|
+
if (dy > dx) { swiping = false; }
|
|
656
|
+
}, { passive: true });
|
|
657
|
+
|
|
658
|
+
document.addEventListener('touchend', e => {
|
|
659
|
+
if (!swiping) return;
|
|
660
|
+
swiping = false;
|
|
661
|
+
const dx = e.changedTouches[0].clientX - startX;
|
|
662
|
+
if (dx > threshold) {
|
|
663
|
+
const backBtn = $('#backBtn');
|
|
664
|
+
if (backBtn) backBtn.click();
|
|
665
|
+
}
|
|
666
|
+
});
|
|
667
|
+
})();
|
|
668
|
+
|
|
616
669
|
// Pull to refresh
|
|
617
670
|
(function initPTR() {
|
|
618
671
|
let startY = 0;
|
|
@@ -652,8 +705,14 @@ viewSearch();
|
|
|
652
705
|
indicator.classList.add('refreshing');
|
|
653
706
|
try {
|
|
654
707
|
await api('/reindex');
|
|
655
|
-
|
|
656
|
-
|
|
708
|
+
// If viewing a session detail, refresh it in place
|
|
709
|
+
const backBtn = $('#backBtn');
|
|
710
|
+
if (backBtn && window._currentSessionId) {
|
|
711
|
+
await viewSession(window._currentSessionId);
|
|
712
|
+
} else {
|
|
713
|
+
const active = $('.nav-item.active');
|
|
714
|
+
if (active) active.click();
|
|
715
|
+
}
|
|
657
716
|
} catch(err) {}
|
|
658
717
|
setTimeout(() => {
|
|
659
718
|
indicator.classList.remove('visible', 'refreshing');
|