agentacta 2026.4.8 → 2026.4.10

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/package.json CHANGED
@@ -1,29 +1,27 @@
1
1
  {
2
2
  "name": "agentacta",
3
- "version": "2026.4.8",
3
+ "version": "2026.4.10",
4
4
  "description": "Audit trail and search engine for AI agent sessions",
5
- "main": "index.js",
5
+ "main": "dist/index.js",
6
6
  "bin": {
7
7
  "agentacta": "index.js"
8
8
  },
9
9
  "files": [
10
10
  "index.js",
11
- "indexer.js",
12
- "db.js",
13
- "config.js",
14
- "project-attribution.js",
15
- "delta-attribution-context.js",
16
- "insights.js",
11
+ "dist/",
17
12
  "public/",
18
13
  "LICENSE",
19
14
  "README.md"
20
15
  ],
21
16
  "scripts": {
22
- "start": "node index.js",
23
- "index": "node indexer.js",
24
- "test": "node --test tests/*.test.js",
25
- "demo": "node scripts/seed-demo.js && node index.js --demo",
26
- "seed-demo": "node scripts/seed-demo.js"
17
+ "build": "tsc",
18
+ "start": "node dist/index.js",
19
+ "dev": "tsx src/index.ts",
20
+ "index": "node dist/indexer.js",
21
+ "test": "tsx --test tests/*.test.ts",
22
+ "demo": "node scripts/seed-demo.js && node dist/index.js --demo",
23
+ "seed-demo": "node scripts/seed-demo.js",
24
+ "prepublishOnly": "npm run build && npm test"
27
25
  },
28
26
  "keywords": [
29
27
  "ai",
@@ -47,6 +45,12 @@
47
45
  "dependencies": {
48
46
  "better-sqlite3": "^12.6.2"
49
47
  },
48
+ "devDependencies": {
49
+ "@types/better-sqlite3": "^7.6.12",
50
+ "@types/node": "^22.15.3",
51
+ "tsx": "^4.19.4",
52
+ "typescript": "^5.8.3"
53
+ },
50
54
  "engines": {
51
55
  "node": ">=18.0.0"
52
56
  },
@@ -54,4 +58,4 @@
54
58
  "tar-fs": "^3.0.6",
55
59
  "npmlog": "^7.0.1"
56
60
  }
57
- }
61
+ }
package/config.js DELETED
@@ -1,85 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const os = require('os');
4
-
5
- const CWD_CONFIG_FILE = path.join(process.cwd(), 'agentacta.config.json');
6
- const XDG_CONFIG_DIR = path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config'), 'agentacta');
7
- const XDG_CONFIG_FILE = path.join(XDG_CONFIG_DIR, 'config.json');
8
-
9
- // Resolve config file: CWD first (backward compat), then XDG default
10
- function resolveConfigFile() {
11
- if (fs.existsSync(CWD_CONFIG_FILE)) return CWD_CONFIG_FILE;
12
- return XDG_CONFIG_FILE;
13
- }
14
-
15
- const CONFIG_FILE = resolveConfigFile();
16
-
17
- const KNOWN_SESSION_DIRS = [
18
- path.join(os.homedir(), '.claude', 'projects'), // Claude Code
19
- path.join(os.homedir(), '.codex', 'sessions'), // Codex CLI
20
- path.join(os.homedir(), '.openclaw', 'sessions'), // OpenClaw
21
- ];
22
-
23
- const DEFAULTS = {
24
- port: 4003,
25
- storage: 'reference',
26
- sessionsPath: null,
27
- dbPath: './agentacta.db',
28
- projectAliases: {}
29
- };
30
-
31
- function detectSessionDirs() {
32
- const found = KNOWN_SESSION_DIRS.filter(d => fs.existsSync(d));
33
- return found.length > 0 ? found : null;
34
- }
35
-
36
- function loadConfig() {
37
- let fileConfig = {};
38
-
39
- if (fs.existsSync(CONFIG_FILE)) {
40
- try {
41
- fileConfig = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
42
- } catch (err) {
43
- console.error(`Warning: Could not parse ${CONFIG_FILE}:`, err.message);
44
- }
45
- } else {
46
- // First-run: create default config with auto-detected session dirs
47
- const detected = detectSessionDirs();
48
- const firstRunDefaults = { ...DEFAULTS, sessionsPath: detected };
49
- const dir = path.dirname(CONFIG_FILE);
50
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
51
- fs.writeFileSync(CONFIG_FILE, JSON.stringify(firstRunDefaults, null, 2) + '\n');
52
- // Apply to in-memory config so this run also benefits
53
- fileConfig = firstRunDefaults;
54
- console.log(`Created default config: ${CONFIG_FILE}`);
55
- if (detected) {
56
- console.log(`Auto-detected session directories:\n${detected.map(d => ` - ${d}`).join('\n')}`);
57
- }
58
- }
59
-
60
- // In demo mode, ignore file-based sessionsPath so live data doesn't bleed in
61
- if (process.env.AGENTACTA_DEMO_MODE) delete fileConfig.sessionsPath;
62
- const config = { ...DEFAULTS, ...fileConfig };
63
-
64
- // Env var overrides (highest priority)
65
- if (process.env.PORT) config.port = parseInt(process.env.PORT);
66
- if (process.env.AGENTACTA_STORAGE) config.storage = process.env.AGENTACTA_STORAGE;
67
- if (process.env.AGENTACTA_SESSIONS_PATH) config.sessionsPath = process.env.AGENTACTA_SESSIONS_PATH;
68
- if (process.env.AGENTACTA_DB_PATH) config.dbPath = process.env.AGENTACTA_DB_PATH;
69
- if (process.env.AGENTACTA_PROJECT_ALIASES_JSON) {
70
- try {
71
- config.projectAliases = JSON.parse(process.env.AGENTACTA_PROJECT_ALIASES_JSON);
72
- } catch (err) {
73
- console.error('Warning: Could not parse AGENTACTA_PROJECT_ALIASES_JSON:', err.message);
74
- }
75
- }
76
-
77
- // Resolve dbPath relative to cwd
78
- config.dbPath = path.resolve(config.dbPath);
79
- if (!config.projectAliases || typeof config.projectAliases !== 'object') config.projectAliases = {};
80
-
81
- return config;
82
- }
83
-
84
- module.exports = { loadConfig, CONFIG_FILE };
85
- // v1.1.3
package/db.js DELETED
@@ -1,152 +0,0 @@
1
- const Database = require('better-sqlite3');
2
- const path = require('path');
3
- const { loadConfig } = require('./config');
4
-
5
- let _config = null;
6
- function getConfig() {
7
- if (!_config) _config = loadConfig();
8
- return _config;
9
- }
10
-
11
- function open(dbPath) {
12
- const p = dbPath || getConfig().dbPath;
13
- const db = new Database(p);
14
- db.pragma('journal_mode = WAL');
15
- db.pragma('foreign_keys = ON');
16
- return db;
17
- }
18
-
19
- function init(dbPath) {
20
- const db = open(dbPath);
21
-
22
- db.exec(`
23
- CREATE TABLE IF NOT EXISTS sessions (
24
- id TEXT PRIMARY KEY,
25
- start_time TEXT NOT NULL,
26
- end_time TEXT,
27
- message_count INTEGER DEFAULT 0,
28
- tool_count INTEGER DEFAULT 0,
29
- model TEXT,
30
- summary TEXT,
31
- agent TEXT,
32
- session_type TEXT,
33
- total_cost REAL DEFAULT 0,
34
- total_tokens INTEGER DEFAULT 0,
35
- input_tokens INTEGER DEFAULT 0,
36
- output_tokens INTEGER DEFAULT 0,
37
- cache_read_tokens INTEGER DEFAULT 0,
38
- cache_write_tokens INTEGER DEFAULT 0,
39
- initial_prompt TEXT,
40
- first_message_id TEXT,
41
- first_message_timestamp TEXT
42
- );
43
-
44
- CREATE TABLE IF NOT EXISTS events (
45
- id TEXT PRIMARY KEY,
46
- session_id TEXT NOT NULL,
47
- timestamp TEXT NOT NULL,
48
- type TEXT NOT NULL,
49
- role TEXT,
50
- content TEXT,
51
- tool_name TEXT,
52
- tool_args TEXT,
53
- tool_result TEXT,
54
- FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
55
- );
56
-
57
- CREATE INDEX IF NOT EXISTS idx_sessions_start_time ON sessions(start_time DESC);
58
- CREATE INDEX IF NOT EXISTS idx_events_session ON events(session_id);
59
- CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
60
- CREATE INDEX IF NOT EXISTS idx_events_type ON events(type);
61
- CREATE INDEX IF NOT EXISTS idx_events_tool_name ON events(tool_name);
62
-
63
- CREATE VIRTUAL TABLE IF NOT EXISTS events_fts USING fts5(
64
- content, tool_name, tool_args,
65
- content='events',
66
- content_rowid='rowid'
67
- );
68
-
69
- CREATE TRIGGER IF NOT EXISTS events_ai AFTER INSERT ON events BEGIN
70
- INSERT INTO events_fts(rowid, content, tool_name, tool_args)
71
- VALUES (new.rowid, new.content, new.tool_name, new.tool_args);
72
- END;
73
-
74
- CREATE TRIGGER IF NOT EXISTS events_ad AFTER DELETE ON events BEGIN
75
- INSERT INTO events_fts(events_fts, rowid, content, tool_name, tool_args)
76
- VALUES ('delete', old.rowid, old.content, old.tool_name, old.tool_args);
77
- END;
78
-
79
- CREATE TABLE IF NOT EXISTS index_state (
80
- file_path TEXT PRIMARY KEY,
81
- last_offset INTEGER DEFAULT 0,
82
- last_modified TEXT
83
- );
84
-
85
- CREATE TABLE IF NOT EXISTS file_activity (
86
- id INTEGER PRIMARY KEY AUTOINCREMENT,
87
- session_id TEXT NOT NULL,
88
- file_path TEXT NOT NULL,
89
- operation TEXT NOT NULL,
90
- timestamp TEXT,
91
- FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
92
- );
93
-
94
- CREATE INDEX IF NOT EXISTS idx_file_activity_path ON file_activity(file_path);
95
- CREATE INDEX IF NOT EXISTS idx_file_activity_session ON file_activity(session_id);
96
-
97
- CREATE TABLE IF NOT EXISTS archive (
98
- id INTEGER PRIMARY KEY AUTOINCREMENT,
99
- session_id TEXT NOT NULL,
100
- line_number INTEGER NOT NULL,
101
- raw_json TEXT NOT NULL,
102
- FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
103
- );
104
-
105
- CREATE INDEX IF NOT EXISTS idx_archive_session ON archive(session_id);
106
-
107
- CREATE TABLE IF NOT EXISTS session_insights (
108
- session_id TEXT PRIMARY KEY,
109
- signals TEXT,
110
- confusion_score INTEGER DEFAULT 0,
111
- flagged INTEGER DEFAULT 0,
112
- computed_at TEXT,
113
- FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
114
- );
115
-
116
- CREATE INDEX IF NOT EXISTS idx_insights_flagged ON session_insights(flagged);
117
- CREATE INDEX IF NOT EXISTS idx_insights_score ON session_insights(confusion_score DESC);
118
- `);
119
-
120
- // Add columns if missing (migration)
121
- const cols = db.prepare("PRAGMA table_info(sessions)").all().map(c => c.name);
122
- if (!cols.includes('agent')) db.exec("ALTER TABLE sessions ADD COLUMN agent TEXT");
123
- if (!cols.includes('session_type')) db.exec("ALTER TABLE sessions ADD COLUMN session_type TEXT");
124
- if (!cols.includes('total_cost')) db.exec("ALTER TABLE sessions ADD COLUMN total_cost REAL DEFAULT 0");
125
- if (!cols.includes('total_tokens')) db.exec("ALTER TABLE sessions ADD COLUMN total_tokens INTEGER DEFAULT 0");
126
- if (!cols.includes('input_tokens')) db.exec("ALTER TABLE sessions ADD COLUMN input_tokens INTEGER DEFAULT 0");
127
- if (!cols.includes('output_tokens')) db.exec("ALTER TABLE sessions ADD COLUMN output_tokens INTEGER DEFAULT 0");
128
- if (!cols.includes('cache_read_tokens')) db.exec("ALTER TABLE sessions ADD COLUMN cache_read_tokens INTEGER DEFAULT 0");
129
- if (!cols.includes('cache_write_tokens')) db.exec("ALTER TABLE sessions ADD COLUMN cache_write_tokens INTEGER DEFAULT 0");
130
- if (!cols.includes('models')) db.exec("ALTER TABLE sessions ADD COLUMN models TEXT");
131
- if (!cols.includes('projects')) db.exec("ALTER TABLE sessions ADD COLUMN projects TEXT");
132
-
133
- db.close();
134
- }
135
-
136
- function createStmts(db) {
137
- return {
138
- getState: db.prepare('SELECT * FROM index_state WHERE file_path = ?'),
139
- getSession: db.prepare('SELECT id FROM sessions WHERE id = ?'),
140
- deleteEvents: db.prepare('DELETE FROM events WHERE session_id = ?'),
141
- deleteSession: db.prepare('DELETE FROM sessions WHERE id = ?'),
142
- deleteFileActivity: db.prepare('DELETE FROM file_activity WHERE session_id = ?'),
143
- insertEvent: db.prepare(`INSERT OR REPLACE INTO events (id, session_id, timestamp, type, role, content, tool_name, tool_args, tool_result) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`),
144
- 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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
145
- upsertState: db.prepare(`INSERT OR REPLACE INTO index_state (file_path, last_offset, last_modified) VALUES (?, ?, ?)`),
146
- insertFileActivity: db.prepare(`INSERT INTO file_activity (session_id, file_path, operation, timestamp) VALUES (?, ?, ?, ?)`),
147
- deleteArchive: db.prepare('DELETE FROM archive WHERE session_id = ?'),
148
- insertArchive: db.prepare('INSERT INTO archive (session_id, line_number, raw_json) VALUES (?, ?, ?)')
149
- };
150
- }
151
-
152
- module.exports = { open, init, createStmts };
@@ -1,57 +0,0 @@
1
- 'use strict';
2
-
3
- function extractCallBaseId(id) {
4
- if (!id) return '';
5
- return String(id).replace(/:(call|result)$/, '');
6
- }
7
-
8
- function loadDeltaAttributionContext(db, sessionId, rows) {
9
- if (!db || !Array.isArray(rows) || !rows.length) return [];
10
-
11
- const ordered = [...rows].sort((a, b) => {
12
- const ta = Date.parse(a?.timestamp || 0) || 0;
13
- const tb = Date.parse(b?.timestamp || 0) || 0;
14
- if (ta !== tb) return ta - tb;
15
- return String(a?.id || '').localeCompare(String(b?.id || ''));
16
- });
17
-
18
- const first = ordered[0];
19
- const firstTs = first?.timestamp || '1970-01-01T00:00:00.000Z';
20
- const firstId = first?.id || '';
21
- const neighborhoodRows = db.prepare(
22
- `SELECT * FROM events
23
- WHERE session_id = ?
24
- AND (timestamp < ? OR (timestamp = ? AND id < ?))
25
- ORDER BY timestamp DESC, id DESC
26
- LIMIT 12`
27
- ).all(sessionId, firstTs, firstTs, firstId).reverse();
28
-
29
- const callIds = [...new Set(
30
- rows
31
- .filter(row => row && row.type === 'tool_result')
32
- .map(row => extractCallBaseId(row.id))
33
- .filter(Boolean)
34
- .map(base => `${base}:call`)
35
- )];
36
-
37
- if (!callIds.length) return neighborhoodRows;
38
-
39
- const placeholders = callIds.map(() => '?').join(',');
40
- const linkedCallRows = db.prepare(
41
- `SELECT * FROM events
42
- WHERE session_id = ?
43
- AND type = 'tool_call'
44
- AND id IN (${placeholders})`
45
- ).all(sessionId, ...callIds);
46
-
47
- const merged = [];
48
- const seen = new Set();
49
- for (const row of [...neighborhoodRows, ...linkedCallRows]) {
50
- if (!row || !row.id || seen.has(row.id)) continue;
51
- seen.add(row.id);
52
- merged.push(row);
53
- }
54
- return merged;
55
- }
56
-
57
- module.exports = { loadDeltaAttributionContext };