agentacta 1.3.3 → 1.4.0
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/README.md +102 -80
- package/db.js +1 -0
- package/index.js +57 -3
- package/indexer.js +281 -20
- package/package.json +1 -1
- package/public/app.js +231 -37
- package/public/index.html +4 -0
- package/public/style.css +144 -4
package/README.md
CHANGED
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/agentacta)
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
|
|
7
|
-
**Your
|
|
7
|
+
**Your agent did 1000s of things today. Can you find the 1 that broke prod?**
|
|
8
8
|
|
|
9
|
-
AgentActa is
|
|
9
|
+
AgentActa is a local audit trail and search engine for AI agent sessions.
|
|
10
|
+
|
|
11
|
+
It indexes messages, tool calls, file edits, searches, and decisions into a fast UI you can query in seconds.
|
|
10
12
|
|
|
11
13
|
One command. Zero config. Full visibility.
|
|
12
14
|
|
|
@@ -18,24 +20,25 @@ npx agentacta
|
|
|
18
20
|
<img src="screenshots/demo.gif" alt="AgentActa demo" width="800">
|
|
19
21
|
</p>
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
## Why this exists
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
Agents move fast. Your memory of what happened doesn’t.
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
When you need to answer “what changed, when, and why,” you’re usually scraping logs, scrolling transcripts, or asking the same assistant that forgot 20 minutes ago.
|
|
26
28
|
|
|
27
|
-
AgentActa gives you
|
|
29
|
+
AgentActa gives you one place to inspect the full trail.
|
|
28
30
|
|
|
29
|
-
## What
|
|
31
|
+
## What you get
|
|
30
32
|
|
|
31
|
-
🔍
|
|
32
|
-
📋
|
|
33
|
-
📅
|
|
34
|
-
📁
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
- 🔍 Full-text search across messages, tool calls, and results
|
|
34
|
+
- 📋 Session browser with summaries, token breakdowns, and model info
|
|
35
|
+
- 📅 Timeline view with live updates for today
|
|
36
|
+
- 📁 File activity across all indexed sessions
|
|
37
|
+
- 🌗 Light and dark themes
|
|
38
|
+
- 📊 Stats for sessions, messages, tools, and tokens
|
|
39
|
+
- ⚡ Live indexing via file watching
|
|
40
|
+
- 📱 Mobile-friendly UI
|
|
41
|
+
- 💡 Search suggestions based on real data
|
|
39
42
|
|
|
40
43
|
## Demo
|
|
41
44
|
|
|
@@ -51,61 +54,81 @@ https://github.com/mirajchokshi/agentacta/raw/main/screenshots/demo-final.mp4
|
|
|
51
54
|

|
|
52
55
|

|
|
53
56
|
|
|
54
|
-
## Quick
|
|
57
|
+
## Quick start
|
|
55
58
|
|
|
56
59
|
```bash
|
|
57
|
-
#
|
|
60
|
+
# run directly
|
|
58
61
|
npx agentacta
|
|
59
62
|
|
|
60
|
-
#
|
|
63
|
+
# or install globally
|
|
61
64
|
npm install -g agentacta
|
|
62
65
|
agentacta
|
|
63
66
|
```
|
|
64
67
|
|
|
65
|
-
Open `http://localhost:4003`
|
|
68
|
+
Open: `http://localhost:4003`
|
|
66
69
|
|
|
67
|
-
|
|
70
|
+
Auto-detected session paths:
|
|
68
71
|
- `~/.openclaw/agents/*/sessions/` (OpenClaw)
|
|
69
72
|
- `~/.claude/projects/*/` (Claude Code)
|
|
73
|
+
- `~/.codex/sessions/` (Codex CLI)
|
|
70
74
|
|
|
71
|
-
|
|
75
|
+
Custom path:
|
|
72
76
|
|
|
73
77
|
```bash
|
|
74
78
|
AGENTACTA_SESSIONS_PATH=/path/to/sessions agentacta
|
|
75
79
|
```
|
|
76
80
|
|
|
77
|
-
##
|
|
81
|
+
## Core features
|
|
78
82
|
|
|
79
83
|
### Search
|
|
80
|
-
|
|
84
|
+
|
|
85
|
+
SQLite FTS5 full-text search with filters for message type (messages, tool calls, results) and role (user, assistant).
|
|
86
|
+
|
|
87
|
+
Suggestions come from your own dataset: top tools, common topics, frequently touched files.
|
|
81
88
|
|
|
82
89
|
### Sessions
|
|
83
|
-
|
|
90
|
+
|
|
91
|
+
Browse indexed sessions with auto-generated summaries, token splits (input/output), and model details. Click into any session to see the full event history.
|
|
92
|
+
|
|
93
|
+
Session types get tagged so noisy categories are easier to spot (cron, sub-agent, heartbeat).
|
|
84
94
|
|
|
85
95
|
### Timeline
|
|
86
|
-
|
|
96
|
+
|
|
97
|
+
Pick a date, see everything that happened, newest first. Today's view updates live as new events come in.
|
|
87
98
|
|
|
88
99
|
### File Activity
|
|
89
|
-
|
|
100
|
+
|
|
101
|
+
See what files were touched, how often, and by which sessions.
|
|
102
|
+
|
|
103
|
+
Sort by recency, frequency, or session count. Filter by extension. Group by directory. Click any file to see which sessions touched it.
|
|
90
104
|
|
|
91
105
|
### Export
|
|
92
|
-
Download any session or search results as Markdown or JSON. Great for sharing, auditing, or archiving.
|
|
93
106
|
|
|
94
|
-
|
|
107
|
+
Export sessions or search results as Markdown or JSON.
|
|
95
108
|
|
|
96
|
-
|
|
109
|
+
Useful for handoffs, incident writeups, and audit archives.
|
|
97
110
|
|
|
98
|
-
|
|
111
|
+
## How it works
|
|
99
112
|
|
|
100
|
-
|
|
101
|
-
|
|
113
|
+
AgentActa parses JSONL session files (OpenClaw, Claude Code, Codex CLI), then indexes events into local SQLite with FTS5.
|
|
114
|
+
|
|
115
|
+
The UI is a single-page app served by a lightweight Node HTTP server.
|
|
116
|
+
|
|
117
|
+
No framework build pipeline. Minimal moving parts.
|
|
118
|
+
|
|
119
|
+
```text
|
|
120
|
+
Session JSONL files -> SQLite + FTS5 index -> HTTP API -> Web UI
|
|
102
121
|
```
|
|
103
122
|
|
|
104
|
-
|
|
123
|
+
Everything stays on your machine.
|
|
105
124
|
|
|
106
125
|
## Configuration
|
|
107
126
|
|
|
108
|
-
On first run, AgentActa creates
|
|
127
|
+
On first run, AgentActa creates:
|
|
128
|
+
- `~/.config/agentacta/config.json`
|
|
129
|
+
- or `agentacta.config.json` in current directory (if present)
|
|
130
|
+
|
|
131
|
+
Default config:
|
|
109
132
|
|
|
110
133
|
```json
|
|
111
134
|
{
|
|
@@ -117,102 +140,101 @@ On first run, AgentActa creates a config file with sensible defaults at `~/.conf
|
|
|
117
140
|
}
|
|
118
141
|
```
|
|
119
142
|
|
|
120
|
-
### Storage
|
|
143
|
+
### Storage modes
|
|
121
144
|
|
|
122
|
-
-
|
|
123
|
-
-
|
|
145
|
+
- `reference` (default): index parsed events in SQLite, keep source JSONL on disk. Lightweight.
|
|
146
|
+
- `archive`: store full JSONL in SQLite. Sessions survive even if original files are deleted. Uses more disk.
|
|
124
147
|
|
|
125
|
-
### Environment
|
|
148
|
+
### Environment variables
|
|
126
149
|
|
|
127
150
|
| Variable | Default | Description |
|
|
128
151
|
|---|---|---|
|
|
129
152
|
| `PORT` | `4003` | Server port |
|
|
130
|
-
| `AGENTACTA_HOST` | `127.0.0.1` | Bind address
|
|
131
|
-
| `AGENTACTA_SESSIONS_PATH` |
|
|
132
|
-
| `AGENTACTA_DB_PATH` | `./agentacta.db` | Database
|
|
133
|
-
| `AGENTACTA_STORAGE` | `reference` |
|
|
134
|
-
| `AGENTACTA_PROJECT_ALIASES_JSON` | unset |
|
|
153
|
+
| `AGENTACTA_HOST` | `127.0.0.1` | Bind address |
|
|
154
|
+
| `AGENTACTA_SESSIONS_PATH` | auto-detected | Custom sessions directory |
|
|
155
|
+
| `AGENTACTA_DB_PATH` | `./agentacta.db` | Database path |
|
|
156
|
+
| `AGENTACTA_STORAGE` | `reference` | `reference` or `archive` |
|
|
157
|
+
| `AGENTACTA_PROJECT_ALIASES_JSON` | unset | Rename inferred project labels |
|
|
135
158
|
|
|
136
159
|
## API
|
|
137
160
|
|
|
138
|
-
AgentActa exposes a JSON API for programmatic access — useful for integrating search into your agent's workflow.
|
|
139
|
-
|
|
140
161
|
| Endpoint | Description |
|
|
141
162
|
|---|---|
|
|
142
|
-
| `GET /api/stats` |
|
|
143
|
-
| `GET /api/sessions` |
|
|
144
|
-
| `GET /api/sessions/:id` | Full session
|
|
145
|
-
| `GET /api/search?q=<query>` | Full-text search
|
|
146
|
-
| `GET /api/suggestions` |
|
|
147
|
-
| `GET /api/timeline?date=YYYY-MM-DD` |
|
|
148
|
-
| `GET /api/files` |
|
|
149
|
-
| `GET /api/export/session/:id?format=md` | Export session
|
|
163
|
+
| `GET /api/stats` | Session/message/tool/token totals |
|
|
164
|
+
| `GET /api/sessions` | Session list with metadata |
|
|
165
|
+
| `GET /api/sessions/:id` | Full session events |
|
|
166
|
+
| `GET /api/search?q=<query>` | Full-text search + filters |
|
|
167
|
+
| `GET /api/suggestions` | Search suggestions |
|
|
168
|
+
| `GET /api/timeline?date=YYYY-MM-DD` | Events for one day |
|
|
169
|
+
| `GET /api/files` | Touched-file inventory |
|
|
170
|
+
| `GET /api/export/session/:id?format=md` | Export one session |
|
|
171
|
+
| `GET /api/timeline/stream?after=<ts>` | SSE stream for live timeline updates |
|
|
172
|
+
| `POST /api/maintenance` | VACUUM + WAL checkpoint (returns size before/after) |
|
|
150
173
|
| `GET /api/export/search?q=<query>&format=md` | Export search results |
|
|
151
174
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
Your AI agent can query AgentActa for better recall:
|
|
175
|
+
Agent integration example:
|
|
155
176
|
|
|
156
177
|
```javascript
|
|
157
|
-
const
|
|
158
|
-
const data = await
|
|
159
|
-
// Agent now has context from past sessions
|
|
178
|
+
const res = await fetch('http://localhost:4003/api/search?q=deployment+issue&limit=5');
|
|
179
|
+
const data = await res.json();
|
|
160
180
|
```
|
|
161
181
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
Want to see what AgentActa looks like with data? Run with demo sessions:
|
|
182
|
+
## Demo mode
|
|
165
183
|
|
|
166
184
|
```bash
|
|
167
|
-
#
|
|
185
|
+
# seed demo data + run
|
|
168
186
|
npm run demo
|
|
169
187
|
|
|
170
|
-
#
|
|
188
|
+
# or split steps
|
|
171
189
|
node scripts/seed-demo.js
|
|
172
190
|
node index.js --demo
|
|
173
191
|
```
|
|
174
192
|
|
|
175
|
-
|
|
193
|
+
Demo mode creates 7 realistic sessions (weather app build path: scaffolding, API, frontend, debugging, deployment, tests, sub-agent task).
|
|
176
194
|
|
|
177
195
|
## Security
|
|
178
196
|
|
|
179
|
-
|
|
197
|
+
AgentActa binds to `127.0.0.1` by default.
|
|
180
198
|
|
|
181
|
-
|
|
199
|
+
If you expose it on a network, do it intentionally:
|
|
182
200
|
|
|
183
201
|
```bash
|
|
184
202
|
AGENTACTA_HOST=0.0.0.0 agentacta
|
|
185
203
|
```
|
|
186
204
|
|
|
187
|
-
|
|
205
|
+
**Important:** Session data can contain sensitive content (file snippets, API payloads, personal messages, tool args). There is no built-in auth yet, so only expose on trusted networks.
|
|
188
206
|
|
|
189
|
-
## Tech
|
|
207
|
+
## Tech stack
|
|
190
208
|
|
|
191
|
-
-
|
|
192
|
-
-
|
|
193
|
-
-
|
|
194
|
-
-
|
|
209
|
+
- Node.js (built-in `http`)
|
|
210
|
+
- `better-sqlite3` + SQLite FTS5
|
|
211
|
+
- Vanilla HTML/CSS/JS
|
|
212
|
+
- PWA support
|
|
195
213
|
|
|
196
214
|
## Privacy
|
|
197
215
|
|
|
198
|
-
|
|
216
|
+
No telemetry. No cloud sync. No external indexing service.
|
|
217
|
+
|
|
218
|
+
Your session history stays local.
|
|
199
219
|
|
|
200
220
|
## Compatibility
|
|
201
221
|
|
|
202
222
|
- ✅ [OpenClaw](https://github.com/openclaw/openclaw)
|
|
203
223
|
- ✅ Claude Code
|
|
204
|
-
-
|
|
224
|
+
- ✅ Codex CLI
|
|
205
225
|
- 🔜 Custom JSONL formats
|
|
206
226
|
|
|
207
227
|
## Contributing
|
|
208
228
|
|
|
209
|
-
PRs welcome
|
|
229
|
+
PRs welcome.
|
|
230
|
+
|
|
231
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). If you’re adding a new agent format, start in `indexer.js`.
|
|
210
232
|
|
|
211
|
-
##
|
|
233
|
+
## Name
|
|
212
234
|
|
|
213
|
-
*Acta*
|
|
235
|
+
*Acta* is Latin for “things done.”
|
|
214
236
|
|
|
215
|
-
|
|
237
|
+
That’s the job here: keep a readable record of what your agents actually did.
|
|
216
238
|
|
|
217
239
|
## License
|
|
218
240
|
|
|
@@ -220,4 +242,4 @@ MIT
|
|
|
220
242
|
|
|
221
243
|
---
|
|
222
244
|
|
|
223
|
-
Built in Chicago by humans and agents
|
|
245
|
+
Built in Chicago by humans and agents.
|
package/db.js
CHANGED
|
@@ -54,6 +54,7 @@ function init(dbPath) {
|
|
|
54
54
|
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
55
55
|
);
|
|
56
56
|
|
|
57
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_start_time ON sessions(start_time DESC);
|
|
57
58
|
CREATE INDEX IF NOT EXISTS idx_events_session ON events(session_id);
|
|
58
59
|
CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
|
|
59
60
|
CREATE INDEX IF NOT EXISTS idx_events_type ON events(type);
|
package/index.js
CHANGED
|
@@ -428,13 +428,67 @@ const server = http.createServer((req, res) => {
|
|
|
428
428
|
const date = query.date || (() => { const n = new Date(); return `${n.getFullYear()}-${String(n.getMonth()+1).padStart(2,'0')}-${String(n.getDate()).padStart(2,'0')}`; })();
|
|
429
429
|
const from = new Date(date + 'T00:00:00').toISOString();
|
|
430
430
|
const to = new Date(date + 'T23:59:59.999').toISOString();
|
|
431
|
+
const limit = Math.min(parseInt(query.limit || '100', 10) || 100, 500);
|
|
432
|
+
const offset = Math.max(parseInt(query.offset || '0', 10) || 0, 0);
|
|
431
433
|
const events = db.prepare(
|
|
432
434
|
`SELECT e.*, s.summary as session_summary FROM events e
|
|
433
435
|
JOIN sessions s ON s.id = e.session_id
|
|
434
436
|
WHERE e.timestamp >= ? AND e.timestamp <= ?
|
|
435
|
-
ORDER BY e.timestamp DESC
|
|
436
|
-
|
|
437
|
-
|
|
437
|
+
ORDER BY e.timestamp DESC
|
|
438
|
+
LIMIT ? OFFSET ?`
|
|
439
|
+
).all(from, to, limit, offset);
|
|
440
|
+
const total = db.prepare(
|
|
441
|
+
`SELECT COUNT(*) as c FROM events e
|
|
442
|
+
WHERE e.timestamp >= ? AND e.timestamp <= ?`
|
|
443
|
+
).get(from, to).c;
|
|
444
|
+
json(res, { date, events, total, limit, offset, hasMore: offset + events.length < total });
|
|
445
|
+
}
|
|
446
|
+
else if (pathname === '/api/timeline/stream') {
|
|
447
|
+
res.writeHead(200, {
|
|
448
|
+
'Content-Type': 'text/event-stream',
|
|
449
|
+
'Cache-Control': 'no-cache',
|
|
450
|
+
'Connection': 'keep-alive',
|
|
451
|
+
'X-Accel-Buffering': 'no'
|
|
452
|
+
});
|
|
453
|
+
res.write(': connected\n\n');
|
|
454
|
+
|
|
455
|
+
let lastTs = query.after || new Date().toISOString();
|
|
456
|
+
|
|
457
|
+
const onUpdate = () => {
|
|
458
|
+
try {
|
|
459
|
+
const rows = db.prepare(
|
|
460
|
+
`SELECT e.*, s.summary as session_summary FROM events e
|
|
461
|
+
JOIN sessions s ON s.id = e.session_id
|
|
462
|
+
WHERE e.timestamp > ?
|
|
463
|
+
ORDER BY e.timestamp ASC`
|
|
464
|
+
).all(lastTs);
|
|
465
|
+
if (rows.length) {
|
|
466
|
+
lastTs = rows[rows.length - 1].timestamp;
|
|
467
|
+
res.write(`id: ${lastTs}\ndata: ${JSON.stringify(rows)}\n\n`);
|
|
468
|
+
}
|
|
469
|
+
} catch (err) {
|
|
470
|
+
console.error('Timeline SSE error:', err.message);
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
sseEmitter.on('session-update', onUpdate);
|
|
475
|
+
|
|
476
|
+
const ping = setInterval(() => {
|
|
477
|
+
try { res.write(': ping\n\n'); } catch {}
|
|
478
|
+
}, 30000);
|
|
479
|
+
|
|
480
|
+
req.on('close', () => {
|
|
481
|
+
sseEmitter.off('session-update', onUpdate);
|
|
482
|
+
clearInterval(ping);
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
else if (pathname === '/api/maintenance') {
|
|
486
|
+
if (req.method !== 'POST') return json(res, { error: 'Method not allowed' }, 405);
|
|
487
|
+
const sizeBefore = getDbSize();
|
|
488
|
+
db.pragma('wal_checkpoint(TRUNCATE)');
|
|
489
|
+
db.exec('VACUUM');
|
|
490
|
+
const sizeAfter = getDbSize();
|
|
491
|
+
json(res, { ok: true, sizeBefore, sizeAfter });
|
|
438
492
|
}
|
|
439
493
|
else if (pathname === '/api/files') {
|
|
440
494
|
const limit = parseInt(query.limit) || 100;
|