agentacta 1.1.2 → 1.1.4

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/config.js CHANGED
@@ -53,3 +53,4 @@ function loadConfig() {
53
53
  }
54
54
 
55
55
  module.exports = { loadConfig, CONFIG_FILE };
56
+ // v1.1.3
package/db.js CHANGED
@@ -115,6 +115,7 @@ function init(dbPath) {
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
117
  if (!cols.includes('models')) db.exec("ALTER TABLE sessions ADD COLUMN models TEXT");
118
+ if (!cols.includes('projects')) db.exec("ALTER TABLE sessions ADD COLUMN projects TEXT");
118
119
 
119
120
  db.close();
120
121
  }
@@ -127,7 +128,7 @@ function createStmts(db) {
127
128
  deleteSession: db.prepare('DELETE FROM sessions WHERE id = ?'),
128
129
  deleteFileActivity: db.prepare('DELETE FROM file_activity WHERE session_id = ?'),
129
130
  insertEvent: db.prepare(`INSERT OR REPLACE INTO events (id, session_id, timestamp, type, role, content, tool_name, tool_args, tool_result) 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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
131
+ 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, projects) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
131
132
  upsertState: db.prepare(`INSERT OR REPLACE INTO index_state (file_path, last_offset, last_modified) VALUES (?, ?, ?)`),
132
133
  insertFileActivity: db.prepare(`INSERT INTO file_activity (session_id, file_path, operation, timestamp) VALUES (?, ?, ?, ?)`),
133
134
  deleteArchive: db.prepare('DELETE FROM archive WHERE session_id = ?'),
package/indexer.js CHANGED
@@ -107,6 +107,38 @@ function extractFilePaths(toolName, toolArgs) {
107
107
  return paths;
108
108
  }
109
109
 
110
+ function extractProjectFromPath(filePath) {
111
+ if (!filePath || typeof filePath !== 'string') return null;
112
+
113
+ const normalized = filePath.replace(/\\/g, '/');
114
+
115
+ // Relative paths are usually from workspace cwd -> treat as workspace activity
116
+ if (!normalized.startsWith('/') && !normalized.startsWith('~')) return 'workspace';
117
+
118
+ let rel = normalized
119
+ .replace(/^\/home\/[^/]+\//, '')
120
+ .replace(/^\/Users\/[^/]+\//, '')
121
+ .replace(/^~\//, '');
122
+
123
+ const parts = rel.split('/').filter(Boolean);
124
+ if (!parts.length) return null;
125
+
126
+ // Common repo location: ~/Developer/<repo>/...
127
+ if (parts[0] === 'Developer' && parts[1]) return parts[1];
128
+
129
+ // OpenClaw workspace and agent stores
130
+ if (parts[0] === '.openclaw' && parts[1] === 'workspace') return 'workspace';
131
+ if (parts[0] === '.openclaw' && parts[1] === 'agents' && parts[2]) return `agent:${parts[2]}`;
132
+
133
+ // Claude Code projects
134
+ if (parts[0] === '.claude' && parts[1] === 'projects' && parts[2]) return `claude:${parts[2]}`;
135
+
136
+ // Shared files area
137
+ if (parts[0] === 'Shared') return 'shared';
138
+
139
+ return null;
140
+ }
141
+
110
142
  function indexFile(db, filePath, agentName, stmts, archiveMode) {
111
143
  const stat = fs.statSync(filePath);
112
144
  const mtime = stat.mtime.toISOString();
@@ -177,6 +209,13 @@ function indexFile(db, filePath, agentName, stmts, archiveMode) {
177
209
 
178
210
  const pendingEvents = [];
179
211
  const fileActivities = [];
212
+ const projectCounts = new Map();
213
+
214
+ // Seed project from session cwd when available (helps chat-only sessions)
215
+ if (firstLine && firstLine.cwd) {
216
+ const p = extractProjectFromPath(firstLine.cwd);
217
+ if (p) projectCounts.set(p, 1);
218
+ }
180
219
 
181
220
  for (const line of lines) {
182
221
  let obj;
@@ -272,6 +311,9 @@ function indexFile(db, filePath, agentName, stmts, archiveMode) {
272
311
  : tool.name.includes('edit') || tool.name === 'Edit' ? 'edit'
273
312
  : 'read';
274
313
  fileActivities.push([sessionId, fp, op, ts]);
314
+
315
+ const project = extractProjectFromPath(fp);
316
+ if (project) projectCounts.set(project, (projectCounts.get(project) || 0) + 1);
275
317
  }
276
318
  }
277
319
  }
@@ -290,15 +332,23 @@ function indexFile(db, filePath, agentName, stmts, archiveMode) {
290
332
  }
291
333
  if (!sessionType && !initialPrompt) sessionType = 'heartbeat';
292
334
  // Detect subagent: task-style prompts injected by sessions_spawn
293
- // These typically start with a date/time stamp and contain a detailed task
335
+ // These typically start with a date/time stamp (e.g. "[Wed 2026-...")
336
+ // But exclude System Messages (cron announcements injected into main session)
294
337
  if (!sessionType && initialPrompt) {
295
338
  const p = initialPrompt.trim();
296
- // Sub-agent prompts start with "[Wed 2026-..." or "You are working on..."
297
- if (/^\[(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s+\d{4}-/.test(p)) sessionType = 'subagent';
339
+ // Sub-agent prompts start with "[Wed 2026-..." but NOT "[... [System Message]"
340
+ if (/^\[(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s+\d{4}-/.test(p) && !p.includes('[System Message]')) {
341
+ sessionType = 'subagent';
342
+ }
298
343
  }
299
344
 
300
345
  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);
346
+ const projects = [...projectCounts.entries()]
347
+ .sort((a, b) => (b[1] - a[1]) || a[0].localeCompare(b[0]))
348
+ .map(([name]) => name);
349
+ const projectsJson = projects.length > 0 ? JSON.stringify(projects) : null;
350
+
351
+ stmts.upsertSession.run(sessionId, sessionStart, sessionEnd, msgCount, toolCount, model, summary, agent, sessionType, totalCost, totalTokens, totalInputTokens, totalOutputTokens, totalCacheReadTokens, totalCacheWriteTokens, initialPrompt, firstMessageId, firstMessageTimestamp, modelsJson, projectsJson);
302
352
  for (const ev of pendingEvents) stmts.insertEvent.run(...ev);
303
353
  for (const fa of fileActivities) stmts.insertFileActivity.run(...fa);
304
354
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentacta",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "Audit trail and search engine for AI agent sessions",
5
5
  "main": "index.js",
6
6
  "bin": {
package/public/app.js CHANGED
@@ -113,6 +113,14 @@ 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 renderProjectTags(s) {
117
+ let projects = [];
118
+ if (s.projects) {
119
+ try { projects = JSON.parse(s.projects); } catch {}
120
+ }
121
+ return projects.map(p => `<span class="session-project">${escHtml(p)}</span>`).join('');
122
+ }
123
+
116
124
  function renderModelTags(s) {
117
125
  // Prefer models array if present, fall back to single model
118
126
  let models = [];
@@ -132,6 +140,7 @@ function renderSessionItem(s) {
132
140
  <div class="session-header">
133
141
  <span class="session-time">${timeRange} · ${duration}</span>
134
142
  <span style="display:flex;gap:6px;align-items:center;flex-wrap:wrap">
143
+ ${renderProjectTags(s)}
135
144
  ${s.agent && s.agent !== 'main' ? `<span class="session-agent">${escHtml(s.agent)}</span>` : ''}
136
145
  ${s.session_type ? `<span class="session-type">${escHtml(s.session_type)}</span>` : ''}
137
146
  ${renderModelTags(s)}
@@ -318,6 +327,7 @@ async function viewSession(id) {
318
327
  <div class="session-header">
319
328
  <span class="session-time">${fmtDate(s.start_time)} · ${fmtTimeShort(s.start_time)} – ${fmtTimeShort(s.end_time)}</span>
320
329
  <span style="display:flex;gap:6px;align-items:center;flex-wrap:wrap">
330
+ ${renderProjectTags(s)}
321
331
  ${s.agent && s.agent !== 'main' ? `<span class="session-agent">${escHtml(s.agent)}</span>` : ''}
322
332
  ${s.session_type ? `<span class="session-type">${escHtml(s.session_type)}</span>` : ''}
323
333
  ${renderModelTags(s)}
package/public/style.css CHANGED
@@ -180,6 +180,7 @@ body {
180
180
  }
181
181
 
182
182
  .session-time { font-size: 13px; color: var(--text2); font-family: var(--mono); }
183
+ .session-project { font-size: 11px; color: var(--accent2); background: rgba(63,185,80,0.14); padding: 2px 8px; border-radius: 10px; }
183
184
  .session-model { font-size: 11px; color: var(--purple); background: rgba(188,140,255,0.1); padding: 2px 8px; border-radius: 10px; }
184
185
  .session-summary { font-size: 14px; color: var(--text); line-height: 1.4; }
185
186
  .session-meta { display: flex; gap: 16px; margin-top: 8px; font-size: 12px; color: var(--text2); }