ghagga-core 2.0.1 → 2.2.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 +3 -1
- package/dist/agents/consensus.d.ts +2 -1
- package/dist/agents/consensus.d.ts.map +1 -1
- package/dist/agents/consensus.js +4 -2
- package/dist/agents/consensus.js.map +1 -1
- package/dist/agents/prompts.d.ts +10 -1
- package/dist/agents/prompts.d.ts.map +1 -1
- package/dist/agents/prompts.js +24 -2
- package/dist/agents/prompts.js.map +1 -1
- package/dist/agents/simple.d.ts +2 -1
- package/dist/agents/simple.d.ts.map +1 -1
- package/dist/agents/simple.js +4 -2
- package/dist/agents/simple.js.map +1 -1
- package/dist/agents/workflow.d.ts +2 -1
- package/dist/agents/workflow.d.ts.map +1 -1
- package/dist/agents/workflow.js +12 -3
- package/dist/agents/workflow.js.map +1 -1
- package/dist/format.d.ts +11 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +84 -0
- package/dist/format.js.map +1 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/memory/engram-client.d.ts +38 -0
- package/dist/memory/engram-client.d.ts.map +1 -0
- package/dist/memory/engram-client.js +153 -0
- package/dist/memory/engram-client.js.map +1 -0
- package/dist/memory/engram-mapping.d.ts +83 -0
- package/dist/memory/engram-mapping.d.ts.map +1 -0
- package/dist/memory/engram-mapping.js +159 -0
- package/dist/memory/engram-mapping.js.map +1 -0
- package/dist/memory/engram-types.d.ts +51 -0
- package/dist/memory/engram-types.d.ts.map +1 -0
- package/dist/memory/engram-types.js +12 -0
- package/dist/memory/engram-types.js.map +1 -0
- package/dist/memory/engram.d.ts +76 -0
- package/dist/memory/engram.d.ts.map +1 -0
- package/dist/memory/engram.js +194 -0
- package/dist/memory/engram.js.map +1 -0
- package/dist/memory/persist.d.ts +5 -5
- package/dist/memory/persist.d.ts.map +1 -1
- package/dist/memory/persist.js +23 -24
- package/dist/memory/persist.js.map +1 -1
- package/dist/memory/search.d.ts +6 -5
- package/dist/memory/search.d.ts.map +1 -1
- package/dist/memory/search.js +7 -8
- package/dist/memory/search.js.map +1 -1
- package/dist/memory/sqlite.d.ts +49 -0
- package/dist/memory/sqlite.d.ts.map +1 -0
- package/dist/memory/sqlite.js +349 -0
- package/dist/memory/sqlite.js.map +1 -0
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +167 -58
- package/dist/pipeline.js.map +1 -1
- package/dist/providers/index.d.ts +4 -0
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +12 -0
- package/dist/providers/index.js.map +1 -1
- package/dist/tools/cpd.d.ts.map +1 -1
- package/dist/tools/cpd.js +20 -9
- package/dist/tools/cpd.js.map +1 -1
- package/dist/tools/runner.d.ts +5 -2
- package/dist/tools/runner.d.ts.map +1 -1
- package/dist/tools/runner.js +37 -18
- package/dist/tools/runner.js.map +1 -1
- package/dist/tools/semgrep.d.ts.map +1 -1
- package/dist/tools/semgrep.js +21 -4
- package/dist/tools/semgrep.js.map +1 -1
- package/dist/tools/trivy.d.ts.map +1 -1
- package/dist/tools/trivy.js +6 -2
- package/dist/tools/trivy.js.map +1 -1
- package/dist/types.d.ts +133 -11
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/token-budget.d.ts.map +1 -1
- package/dist/utils/token-budget.js +5 -0
- package/dist/utils/token-budget.js.map +1 -1
- package/package.json +4 -3
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite-backed memory storage using sql.js (pure WASM).
|
|
3
|
+
*
|
|
4
|
+
* Thread safety: This class is NOT thread-safe. It operates as an in-memory
|
|
5
|
+
* database with manual file persistence via close(). Designed for single-process
|
|
6
|
+
* environments (CLI, GitHub Action).
|
|
7
|
+
*/
|
|
8
|
+
import initSqlJs from 'fts5-sql-bundle';
|
|
9
|
+
import { createHash } from 'node:crypto';
|
|
10
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
|
|
11
|
+
import { dirname } from 'node:path';
|
|
12
|
+
const SCHEMA_SQL = `
|
|
13
|
+
CREATE TABLE IF NOT EXISTS memory_sessions (
|
|
14
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
15
|
+
project TEXT NOT NULL,
|
|
16
|
+
pr_number INTEGER,
|
|
17
|
+
summary TEXT,
|
|
18
|
+
started_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
19
|
+
ended_at TEXT
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
CREATE TABLE IF NOT EXISTS memory_observations (
|
|
23
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
24
|
+
session_id INTEGER REFERENCES memory_sessions(id) ON DELETE CASCADE,
|
|
25
|
+
project TEXT NOT NULL,
|
|
26
|
+
type TEXT NOT NULL,
|
|
27
|
+
title TEXT NOT NULL,
|
|
28
|
+
content TEXT NOT NULL,
|
|
29
|
+
severity TEXT,
|
|
30
|
+
topic_key TEXT,
|
|
31
|
+
file_paths TEXT DEFAULT '[]',
|
|
32
|
+
content_hash TEXT,
|
|
33
|
+
revision_count INTEGER NOT NULL DEFAULT 1,
|
|
34
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
35
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
CREATE INDEX IF NOT EXISTS idx_obs_project ON memory_observations(project);
|
|
39
|
+
CREATE INDEX IF NOT EXISTS idx_obs_topic_key ON memory_observations(topic_key);
|
|
40
|
+
CREATE INDEX IF NOT EXISTS idx_obs_content_hash ON memory_observations(content_hash);
|
|
41
|
+
|
|
42
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS memory_observations_fts
|
|
43
|
+
USING fts5(title, content, content='memory_observations', content_rowid='id');
|
|
44
|
+
|
|
45
|
+
CREATE TRIGGER IF NOT EXISTS obs_fts_insert AFTER INSERT ON memory_observations BEGIN
|
|
46
|
+
INSERT INTO memory_observations_fts(rowid, title, content)
|
|
47
|
+
VALUES (new.id, new.title, new.content);
|
|
48
|
+
END;
|
|
49
|
+
|
|
50
|
+
CREATE TRIGGER IF NOT EXISTS obs_fts_update AFTER UPDATE ON memory_observations BEGIN
|
|
51
|
+
INSERT INTO memory_observations_fts(memory_observations_fts, rowid, title, content)
|
|
52
|
+
VALUES ('delete', old.id, old.title, old.content);
|
|
53
|
+
INSERT INTO memory_observations_fts(rowid, title, content)
|
|
54
|
+
VALUES (new.id, new.title, new.content);
|
|
55
|
+
END;
|
|
56
|
+
|
|
57
|
+
CREATE TRIGGER IF NOT EXISTS obs_fts_delete AFTER DELETE ON memory_observations BEGIN
|
|
58
|
+
INSERT INTO memory_observations_fts(memory_observations_fts, rowid, title, content)
|
|
59
|
+
VALUES ('delete', old.id, old.title, old.content);
|
|
60
|
+
END;
|
|
61
|
+
`;
|
|
62
|
+
const DEDUP_WINDOW_MINUTES = 15;
|
|
63
|
+
export class SqliteMemoryStorage {
|
|
64
|
+
db;
|
|
65
|
+
filePath;
|
|
66
|
+
constructor(db, filePath) {
|
|
67
|
+
this.db = db;
|
|
68
|
+
this.filePath = filePath;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Async factory — handles WASM initialization and file loading.
|
|
72
|
+
* If filePath exists, loads the existing DB. Otherwise, creates a fresh one.
|
|
73
|
+
*/
|
|
74
|
+
static async create(filePath) {
|
|
75
|
+
const SQL = await initSqlJs();
|
|
76
|
+
let db;
|
|
77
|
+
if (existsSync(filePath)) {
|
|
78
|
+
const buffer = readFileSync(filePath);
|
|
79
|
+
db = new SQL.Database(buffer);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
db = new SQL.Database();
|
|
83
|
+
}
|
|
84
|
+
db.run(SCHEMA_SQL);
|
|
85
|
+
// Migration: add severity column to existing databases
|
|
86
|
+
try {
|
|
87
|
+
db.run('ALTER TABLE memory_observations ADD COLUMN severity TEXT');
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// Column already exists — idempotent migration
|
|
91
|
+
}
|
|
92
|
+
return new SqliteMemoryStorage(db, filePath);
|
|
93
|
+
}
|
|
94
|
+
async searchObservations(project, query, options = {}) {
|
|
95
|
+
const { limit = 10, type } = options;
|
|
96
|
+
// Convert space-separated keywords to FTS5 OR query
|
|
97
|
+
const ftsQuery = query
|
|
98
|
+
.trim()
|
|
99
|
+
.split(/\s+/)
|
|
100
|
+
.filter((w) => w.length > 0)
|
|
101
|
+
.map((w) => `"${w.replace(/"/g, '""')}"`)
|
|
102
|
+
.join(' OR ');
|
|
103
|
+
if (!ftsQuery)
|
|
104
|
+
return [];
|
|
105
|
+
let sql = `
|
|
106
|
+
SELECT o.id, o.type, o.title, o.content, o.file_paths, o.severity
|
|
107
|
+
FROM memory_observations o
|
|
108
|
+
JOIN memory_observations_fts fts ON fts.rowid = o.id
|
|
109
|
+
WHERE memory_observations_fts MATCH ?
|
|
110
|
+
AND o.project = ?
|
|
111
|
+
`;
|
|
112
|
+
const params = [ftsQuery, project];
|
|
113
|
+
if (type) {
|
|
114
|
+
sql += ' AND o.type = ?';
|
|
115
|
+
params.push(type);
|
|
116
|
+
}
|
|
117
|
+
sql += ' ORDER BY bm25(memory_observations_fts) LIMIT ?';
|
|
118
|
+
params.push(limit);
|
|
119
|
+
const stmt = this.db.prepare(sql);
|
|
120
|
+
stmt.bind(params);
|
|
121
|
+
const rows = [];
|
|
122
|
+
while (stmt.step()) {
|
|
123
|
+
const row = stmt.getAsObject();
|
|
124
|
+
rows.push({
|
|
125
|
+
id: row['id'],
|
|
126
|
+
type: row['type'],
|
|
127
|
+
title: row['title'],
|
|
128
|
+
content: row['content'],
|
|
129
|
+
filePaths: row['file_paths'] ? JSON.parse(row['file_paths']) : null,
|
|
130
|
+
severity: row['severity'] ?? null,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
stmt.free();
|
|
134
|
+
return rows;
|
|
135
|
+
}
|
|
136
|
+
async saveObservation(data) {
|
|
137
|
+
const contentHash = createHash('sha256')
|
|
138
|
+
.update(`${data.type}:${data.title}:${data.content}`)
|
|
139
|
+
.digest('hex');
|
|
140
|
+
// Dedup: check for same content hash within 15-minute window
|
|
141
|
+
const dedupRows = this.db.exec(`
|
|
142
|
+
SELECT id, type, title, content, file_paths FROM memory_observations
|
|
143
|
+
WHERE content_hash = ? AND project = ?
|
|
144
|
+
AND created_at > datetime('now', '-${DEDUP_WINDOW_MINUTES} minutes')
|
|
145
|
+
LIMIT 1
|
|
146
|
+
`, [contentHash, data.project]);
|
|
147
|
+
if (dedupRows.length > 0 && dedupRows[0].values.length > 0) {
|
|
148
|
+
const row = dedupRows[0].values[0];
|
|
149
|
+
const existingId = row[0];
|
|
150
|
+
// If the existing observation is from a different session, reassign it
|
|
151
|
+
if (data.sessionId != null) {
|
|
152
|
+
const existingSessionRows = this.db.exec(`SELECT session_id FROM memory_observations WHERE id = ?`, [existingId]);
|
|
153
|
+
const existingSessionId = existingSessionRows[0]?.values[0]?.[0];
|
|
154
|
+
if (existingSessionId !== data.sessionId) {
|
|
155
|
+
this.db.run(`UPDATE memory_observations SET session_id = ?, updated_at = datetime('now') WHERE id = ?`, [data.sessionId, existingId]);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
id: existingId,
|
|
160
|
+
type: row[1],
|
|
161
|
+
title: row[2],
|
|
162
|
+
content: row[3],
|
|
163
|
+
filePaths: row[4] ? JSON.parse(row[4]) : null,
|
|
164
|
+
severity: null,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
// TopicKey upsert
|
|
168
|
+
if (data.topicKey) {
|
|
169
|
+
const existingByTopic = this.db.exec(`
|
|
170
|
+
SELECT id FROM memory_observations
|
|
171
|
+
WHERE topic_key = ? AND project = ?
|
|
172
|
+
LIMIT 1
|
|
173
|
+
`, [data.topicKey, data.project]);
|
|
174
|
+
if (existingByTopic.length > 0 && existingByTopic[0].values.length > 0) {
|
|
175
|
+
const existingId = existingByTopic[0].values[0][0];
|
|
176
|
+
const filePathsJson = JSON.stringify(data.filePaths ?? []);
|
|
177
|
+
const updated = this.db.exec(`
|
|
178
|
+
UPDATE memory_observations
|
|
179
|
+
SET content = ?, title = ?, content_hash = ?, file_paths = ?,
|
|
180
|
+
severity = ?,
|
|
181
|
+
revision_count = revision_count + 1,
|
|
182
|
+
updated_at = datetime('now')
|
|
183
|
+
WHERE id = ?
|
|
184
|
+
RETURNING id, type, title, content, file_paths, severity
|
|
185
|
+
`, [data.content, data.title, contentHash, filePathsJson, data.severity ?? null, existingId]);
|
|
186
|
+
const row = updated[0].values[0];
|
|
187
|
+
return {
|
|
188
|
+
id: row[0],
|
|
189
|
+
type: row[1],
|
|
190
|
+
title: row[2],
|
|
191
|
+
content: row[3],
|
|
192
|
+
filePaths: row[4] ? JSON.parse(row[4]) : null,
|
|
193
|
+
severity: row[5] ?? null,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// New observation
|
|
198
|
+
const filePathsJson = JSON.stringify(data.filePaths ?? []);
|
|
199
|
+
const inserted = this.db.exec(`
|
|
200
|
+
INSERT INTO memory_observations
|
|
201
|
+
(session_id, project, type, title, content, severity, topic_key, file_paths, content_hash)
|
|
202
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
203
|
+
RETURNING id, type, title, content, file_paths, severity
|
|
204
|
+
`, [
|
|
205
|
+
data.sessionId ?? null,
|
|
206
|
+
data.project,
|
|
207
|
+
data.type,
|
|
208
|
+
data.title,
|
|
209
|
+
data.content,
|
|
210
|
+
data.severity ?? null,
|
|
211
|
+
data.topicKey ?? null,
|
|
212
|
+
filePathsJson,
|
|
213
|
+
contentHash,
|
|
214
|
+
]);
|
|
215
|
+
const row = inserted[0].values[0];
|
|
216
|
+
return {
|
|
217
|
+
id: row[0],
|
|
218
|
+
type: row[1],
|
|
219
|
+
title: row[2],
|
|
220
|
+
content: row[3],
|
|
221
|
+
filePaths: row[4] ? JSON.parse(row[4]) : null,
|
|
222
|
+
severity: row[5] ?? null,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
async createSession(data) {
|
|
226
|
+
const result = this.db.exec(`
|
|
227
|
+
INSERT INTO memory_sessions (project, pr_number)
|
|
228
|
+
VALUES (?, ?)
|
|
229
|
+
RETURNING id
|
|
230
|
+
`, [data.project, data.prNumber ?? null]);
|
|
231
|
+
return { id: result[0].values[0][0] };
|
|
232
|
+
}
|
|
233
|
+
async endSession(sessionId, summary) {
|
|
234
|
+
this.db.run(`
|
|
235
|
+
UPDATE memory_sessions
|
|
236
|
+
SET ended_at = datetime('now'), summary = ?
|
|
237
|
+
WHERE id = ?
|
|
238
|
+
`, [summary, sessionId]);
|
|
239
|
+
}
|
|
240
|
+
// ── Management methods ──────────────────────────────────────────
|
|
241
|
+
mapToDetail(row) {
|
|
242
|
+
return {
|
|
243
|
+
id: row['id'],
|
|
244
|
+
type: row['type'],
|
|
245
|
+
title: row['title'],
|
|
246
|
+
content: row['content'],
|
|
247
|
+
filePaths: row['file_paths'] ? JSON.parse(row['file_paths']) : null,
|
|
248
|
+
severity: row['severity'] ?? null,
|
|
249
|
+
project: row['project'],
|
|
250
|
+
topicKey: row['topic_key'] ?? null,
|
|
251
|
+
revisionCount: row['revision_count'],
|
|
252
|
+
createdAt: row['created_at'],
|
|
253
|
+
updatedAt: row['updated_at'],
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
async listObservations(options = {}) {
|
|
257
|
+
const { project, type, limit = 20, offset = 0 } = options;
|
|
258
|
+
let sql = `
|
|
259
|
+
SELECT id, type, title, content, file_paths, severity, project, topic_key,
|
|
260
|
+
revision_count, created_at, updated_at
|
|
261
|
+
FROM memory_observations
|
|
262
|
+
WHERE 1=1
|
|
263
|
+
`;
|
|
264
|
+
const params = [];
|
|
265
|
+
if (project) {
|
|
266
|
+
sql += ' AND project = ?';
|
|
267
|
+
params.push(project);
|
|
268
|
+
}
|
|
269
|
+
if (type) {
|
|
270
|
+
sql += ' AND type = ?';
|
|
271
|
+
params.push(type);
|
|
272
|
+
}
|
|
273
|
+
sql += ' ORDER BY created_at DESC LIMIT ? OFFSET ?';
|
|
274
|
+
params.push(limit, offset);
|
|
275
|
+
const stmt = this.db.prepare(sql);
|
|
276
|
+
stmt.bind(params);
|
|
277
|
+
const rows = [];
|
|
278
|
+
while (stmt.step()) {
|
|
279
|
+
const row = stmt.getAsObject();
|
|
280
|
+
rows.push(this.mapToDetail(row));
|
|
281
|
+
}
|
|
282
|
+
stmt.free();
|
|
283
|
+
return rows;
|
|
284
|
+
}
|
|
285
|
+
async getObservation(id) {
|
|
286
|
+
const stmt = this.db.prepare(`
|
|
287
|
+
SELECT id, type, title, content, file_paths, severity, project, topic_key,
|
|
288
|
+
revision_count, created_at, updated_at
|
|
289
|
+
FROM memory_observations
|
|
290
|
+
WHERE id = ?
|
|
291
|
+
`);
|
|
292
|
+
stmt.bind([id]);
|
|
293
|
+
if (!stmt.step()) {
|
|
294
|
+
stmt.free();
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
const row = stmt.getAsObject();
|
|
298
|
+
stmt.free();
|
|
299
|
+
return this.mapToDetail(row);
|
|
300
|
+
}
|
|
301
|
+
async deleteObservation(id) {
|
|
302
|
+
this.db.run('DELETE FROM memory_observations WHERE id = ?', [id]);
|
|
303
|
+
return this.db.getRowsModified() > 0;
|
|
304
|
+
}
|
|
305
|
+
async getStats() {
|
|
306
|
+
// Query 1: totals and date range
|
|
307
|
+
const totals = this.db.exec('SELECT COUNT(*) AS total, MIN(created_at) AS oldest, MAX(created_at) AS newest FROM memory_observations');
|
|
308
|
+
const totalRow = totals[0]?.values[0];
|
|
309
|
+
const totalObservations = totalRow?.[0] ?? 0;
|
|
310
|
+
const oldestObservation = totalRow?.[1] ?? null;
|
|
311
|
+
const newestObservation = totalRow?.[2] ?? null;
|
|
312
|
+
// Query 2: count by type
|
|
313
|
+
const byTypeResult = this.db.exec('SELECT type, COUNT(*) AS count FROM memory_observations GROUP BY type ORDER BY count DESC');
|
|
314
|
+
const byType = {};
|
|
315
|
+
if (byTypeResult.length > 0) {
|
|
316
|
+
for (const row of byTypeResult[0].values) {
|
|
317
|
+
byType[row[0]] = row[1];
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
// Query 3: count by project
|
|
321
|
+
const byProjectResult = this.db.exec('SELECT project, COUNT(*) AS count FROM memory_observations GROUP BY project ORDER BY count DESC');
|
|
322
|
+
const byProject = {};
|
|
323
|
+
if (byProjectResult.length > 0) {
|
|
324
|
+
for (const row of byProjectResult[0].values) {
|
|
325
|
+
byProject[row[0]] = row[1];
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return { totalObservations, byType, byProject, oldestObservation, newestObservation };
|
|
329
|
+
}
|
|
330
|
+
async clearObservations(options = {}) {
|
|
331
|
+
if (options.project) {
|
|
332
|
+
this.db.run('DELETE FROM memory_observations WHERE project = ?', [options.project]);
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
this.db.run('DELETE FROM memory_observations');
|
|
336
|
+
}
|
|
337
|
+
return this.db.getRowsModified();
|
|
338
|
+
}
|
|
339
|
+
async close() {
|
|
340
|
+
const dir = dirname(this.filePath);
|
|
341
|
+
if (!existsSync(dir)) {
|
|
342
|
+
mkdirSync(dir, { recursive: true });
|
|
343
|
+
}
|
|
344
|
+
const data = this.db.export();
|
|
345
|
+
writeFileSync(this.filePath, Buffer.from(data));
|
|
346
|
+
this.db.close();
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
//# sourceMappingURL=sqlite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../src/memory/sqlite.ts"],"names":[],"mappings":"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"}
|
package/dist/pipeline.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAYH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAYH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAiD,MAAM,YAAY,CAAC;AAuC3G;;;;;;;;;;GAUG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAiM9E"}
|
package/dist/pipeline.js
CHANGED
|
@@ -33,6 +33,15 @@ function validateInput(input) {
|
|
|
33
33
|
if (!input.diff || input.diff.trim().length === 0) {
|
|
34
34
|
throw new Error('Review input must include a non-empty diff');
|
|
35
35
|
}
|
|
36
|
+
// If AI review is explicitly disabled, no provider/model/key needed
|
|
37
|
+
if (input.aiReviewEnabled === false) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// Provider chain mode: validate the chain has entries
|
|
41
|
+
if (input.providerChain && input.providerChain.length > 0) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
// Single provider mode (CLI/Action backward compat)
|
|
36
45
|
if (!input.apiKey) {
|
|
37
46
|
throw new Error('Review input must include an API key');
|
|
38
47
|
}
|
|
@@ -58,6 +67,8 @@ function validateInput(input) {
|
|
|
58
67
|
export async function reviewPipeline(input) {
|
|
59
68
|
const startTime = Date.now();
|
|
60
69
|
const emit = input.onProgress ?? (() => { });
|
|
70
|
+
// Resolve whether AI review is enabled
|
|
71
|
+
const aiEnabled = resolveAiEnabled(input);
|
|
61
72
|
// ── Step 1: Validate ───────────────────────────────────────
|
|
62
73
|
validateInput(input);
|
|
63
74
|
emit({ step: 'validate', message: 'Input validated' });
|
|
@@ -85,17 +96,25 @@ export async function reviewPipeline(input) {
|
|
|
85
96
|
detail: stacks.length > 0 ? stacks.map((s) => ` ${s}`).join('\n') : ' (none detected)',
|
|
86
97
|
});
|
|
87
98
|
// ── Step 4: Truncate diff to fit token budget ──────────────
|
|
88
|
-
const
|
|
99
|
+
const primaryModel = resolvePrimaryModel(input);
|
|
100
|
+
const { diffBudget } = calculateTokenBudget(primaryModel);
|
|
89
101
|
const { truncated: truncatedDiff } = truncateDiff(filteredDiff, diffBudget);
|
|
90
102
|
emit({
|
|
91
103
|
step: 'token-budget',
|
|
92
104
|
message: `Token budget: ${diffBudget.toLocaleString()} tokens for diff`,
|
|
93
105
|
});
|
|
94
106
|
// ── Step 5: Run static analysis (in parallel with memory) ──
|
|
95
|
-
|
|
107
|
+
// If precomputed results are available (from GitHub Actions runner), use those directly.
|
|
108
|
+
// Otherwise, run tools locally (CLI/Action modes).
|
|
109
|
+
emit({ step: 'static-analysis', message: input.precomputedStaticAnalysis
|
|
110
|
+
? 'Using precomputed static analysis from runner...'
|
|
111
|
+
: 'Running static analysis & memory search...',
|
|
112
|
+
});
|
|
96
113
|
const [staticResult, memoryContext] = await Promise.all([
|
|
97
|
-
|
|
98
|
-
|
|
114
|
+
input.precomputedStaticAnalysis
|
|
115
|
+
? Promise.resolve(input.precomputedStaticAnalysis)
|
|
116
|
+
: runStaticAnalysisSafe(fileList, input),
|
|
117
|
+
aiEnabled ? searchMemorySafe(input, fileList) : Promise.resolve(null),
|
|
99
118
|
]);
|
|
100
119
|
const staticContext = formatStaticAnalysisContext(staticResult);
|
|
101
120
|
{
|
|
@@ -108,53 +127,73 @@ export async function reviewPipeline(input) {
|
|
|
108
127
|
detail: toolsSummary + (memoryContext ? '\n memory: loaded' : '\n memory: disabled'),
|
|
109
128
|
});
|
|
110
129
|
}
|
|
111
|
-
// ── Step 6: Execute agent mode
|
|
112
|
-
emit({ step: 'agent-start', message: `Running ${input.mode} agent...` });
|
|
130
|
+
// ── Step 6: Execute agent mode (or skip if AI disabled) ────
|
|
113
131
|
let result;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
132
|
+
if (!aiEnabled) {
|
|
133
|
+
// Static-only mode: no LLM calls
|
|
134
|
+
emit({ step: 'agent-start', message: 'AI review disabled — returning static analysis only' });
|
|
135
|
+
result = createStaticOnlyResult(staticResult, input.mode, startTime);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
// Resolve the primary provider for agent calls
|
|
139
|
+
const primary = resolvePrimaryProvider(input);
|
|
140
|
+
emit({ step: 'agent-start', message: `Running ${input.mode} agent with ${primary.provider}/${primary.model}...` });
|
|
141
|
+
try {
|
|
142
|
+
switch (input.mode) {
|
|
143
|
+
case 'simple':
|
|
144
|
+
result = await runSimpleReview({
|
|
145
|
+
diff: truncatedDiff,
|
|
146
|
+
provider: primary.provider,
|
|
147
|
+
model: primary.model,
|
|
148
|
+
apiKey: primary.apiKey,
|
|
149
|
+
staticContext,
|
|
150
|
+
memoryContext,
|
|
151
|
+
stackHints,
|
|
152
|
+
reviewLevel: input.settings.reviewLevel,
|
|
153
|
+
onProgress: input.onProgress,
|
|
154
|
+
});
|
|
155
|
+
break;
|
|
156
|
+
case 'workflow':
|
|
157
|
+
result = await runWorkflowReview({
|
|
158
|
+
diff: truncatedDiff,
|
|
159
|
+
provider: primary.provider,
|
|
160
|
+
model: primary.model,
|
|
161
|
+
apiKey: primary.apiKey,
|
|
162
|
+
staticContext,
|
|
163
|
+
memoryContext,
|
|
164
|
+
stackHints,
|
|
165
|
+
reviewLevel: input.settings.reviewLevel,
|
|
166
|
+
onProgress: input.onProgress,
|
|
167
|
+
});
|
|
168
|
+
break;
|
|
169
|
+
case 'consensus':
|
|
170
|
+
result = await runConsensusReview({
|
|
171
|
+
diff: truncatedDiff,
|
|
172
|
+
models: [
|
|
173
|
+
{ provider: primary.provider, model: primary.model, apiKey: primary.apiKey, stance: 'for' },
|
|
174
|
+
{ provider: primary.provider, model: primary.model, apiKey: primary.apiKey, stance: 'against' },
|
|
175
|
+
{ provider: primary.provider, model: primary.model, apiKey: primary.apiKey, stance: 'neutral' },
|
|
176
|
+
],
|
|
177
|
+
staticContext,
|
|
178
|
+
memoryContext,
|
|
179
|
+
stackHints,
|
|
180
|
+
reviewLevel: input.settings.reviewLevel,
|
|
181
|
+
onProgress: input.onProgress,
|
|
182
|
+
});
|
|
183
|
+
break;
|
|
184
|
+
default: {
|
|
185
|
+
const _exhaustive = input.mode;
|
|
186
|
+
throw new Error(`Unknown review mode: ${_exhaustive}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
// All providers failed — return static results with NEEDS_HUMAN_REVIEW
|
|
192
|
+
console.warn('[ghagga] All AI providers failed, returning static analysis only:', error instanceof Error ? error.message : String(error));
|
|
193
|
+
emit({ step: 'agent-failed', message: 'AI review failed — returning static analysis only' });
|
|
194
|
+
result = createStaticOnlyResult(staticResult, input.mode, startTime);
|
|
195
|
+
result.status = 'NEEDS_HUMAN_REVIEW';
|
|
196
|
+
result.summary = `AI review failed (${error instanceof Error ? error.message : 'unknown error'}). Static analysis results are shown below.`;
|
|
158
197
|
}
|
|
159
198
|
}
|
|
160
199
|
// ── Step 7: Merge static analysis into result ──────────────
|
|
@@ -180,15 +219,53 @@ export async function reviewPipeline(input) {
|
|
|
180
219
|
}
|
|
181
220
|
// Update execution time to cover the full pipeline
|
|
182
221
|
result.metadata.executionTimeMs = Date.now() - startTime;
|
|
183
|
-
// ── Step 8: Persist to memory (
|
|
184
|
-
if (input.settings.enableMemory && input.
|
|
185
|
-
|
|
186
|
-
persistReviewObservations(input.db, input.context.repoFullName, input.context.prNumber, result).catch((error) => {
|
|
222
|
+
// ── Step 8: Persist to memory (awaited for SQLite correctness) ──
|
|
223
|
+
if (input.settings.enableMemory && input.memoryStorage && input.context) {
|
|
224
|
+
await persistReviewObservations(input.memoryStorage, input.context.repoFullName, input.context.prNumber, result).catch((error) => {
|
|
187
225
|
console.warn('[ghagga] Memory persist failed (non-fatal):', error instanceof Error ? error.message : String(error));
|
|
188
226
|
});
|
|
189
227
|
}
|
|
190
228
|
return result;
|
|
191
229
|
}
|
|
230
|
+
// ─── Provider Resolution ────────────────────────────────────────
|
|
231
|
+
/**
|
|
232
|
+
* Determine if AI review is enabled.
|
|
233
|
+
* Defaults to true for backward compatibility (CLI/Action don't set this).
|
|
234
|
+
*/
|
|
235
|
+
function resolveAiEnabled(input) {
|
|
236
|
+
if (input.aiReviewEnabled === false)
|
|
237
|
+
return false;
|
|
238
|
+
// If chain is explicitly empty and no single provider, treat as disabled
|
|
239
|
+
if (input.providerChain && input.providerChain.length === 0 && !input.provider) {
|
|
240
|
+
console.warn('[ghagga] AI review enabled but provider chain is empty and no single provider — treating as disabled');
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Resolve the primary provider from chain or flat fields.
|
|
247
|
+
* Returns the first entry in the chain, or builds one from flat fields.
|
|
248
|
+
*/
|
|
249
|
+
function resolvePrimaryProvider(input) {
|
|
250
|
+
if (input.providerChain && input.providerChain.length > 0) {
|
|
251
|
+
return input.providerChain[0];
|
|
252
|
+
}
|
|
253
|
+
// Backward compat: single provider from flat fields
|
|
254
|
+
return {
|
|
255
|
+
provider: input.provider,
|
|
256
|
+
model: input.model,
|
|
257
|
+
apiKey: input.apiKey,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Resolve the model name for token budget calculation.
|
|
262
|
+
*/
|
|
263
|
+
function resolvePrimaryModel(input) {
|
|
264
|
+
if (input.providerChain && input.providerChain.length > 0) {
|
|
265
|
+
return input.providerChain[0].model;
|
|
266
|
+
}
|
|
267
|
+
return input.model ?? 'gpt-4o-mini';
|
|
268
|
+
}
|
|
192
269
|
// ─── Helpers ────────────────────────────────────────────────────
|
|
193
270
|
/**
|
|
194
271
|
* Run static analysis with graceful degradation.
|
|
@@ -228,11 +305,11 @@ async function runStaticAnalysisSafe(fileList, input) {
|
|
|
228
305
|
* Returns null if memory is disabled or unavailable.
|
|
229
306
|
*/
|
|
230
307
|
async function searchMemorySafe(input, fileList) {
|
|
231
|
-
if (!input.settings.enableMemory || !input.
|
|
308
|
+
if (!input.settings.enableMemory || !input.memoryStorage || !input.context) {
|
|
232
309
|
return null;
|
|
233
310
|
}
|
|
234
311
|
try {
|
|
235
|
-
return await searchMemoryForContext(input.
|
|
312
|
+
return await searchMemoryForContext(input.memoryStorage, input.context.repoFullName, fileList);
|
|
236
313
|
}
|
|
237
314
|
catch (error) {
|
|
238
315
|
console.warn('[ghagga] Memory search failed (degrading gracefully):', error instanceof Error ? error.message : String(error));
|
|
@@ -243,6 +320,7 @@ async function searchMemorySafe(input, fileList) {
|
|
|
243
320
|
* Create a SKIPPED result when all files are filtered out.
|
|
244
321
|
*/
|
|
245
322
|
function createSkippedResult(input, startTime) {
|
|
323
|
+
const primary = input.providerChain?.[0];
|
|
246
324
|
return {
|
|
247
325
|
status: 'SKIPPED',
|
|
248
326
|
summary: 'All files in the diff matched ignore patterns. No review was performed.',
|
|
@@ -255,8 +333,8 @@ function createSkippedResult(input, startTime) {
|
|
|
255
333
|
memoryContext: null,
|
|
256
334
|
metadata: {
|
|
257
335
|
mode: input.mode,
|
|
258
|
-
provider: input.provider,
|
|
259
|
-
model: input.model,
|
|
336
|
+
provider: primary?.provider ?? input.provider ?? 'none',
|
|
337
|
+
model: primary?.model ?? input.model ?? 'unknown',
|
|
260
338
|
tokensUsed: 0,
|
|
261
339
|
executionTimeMs: Date.now() - startTime,
|
|
262
340
|
toolsRun: [],
|
|
@@ -264,4 +342,35 @@ function createSkippedResult(input, startTime) {
|
|
|
264
342
|
},
|
|
265
343
|
};
|
|
266
344
|
}
|
|
345
|
+
/**
|
|
346
|
+
* Create a result with only static analysis findings (no AI).
|
|
347
|
+
* Used when AI review is disabled or when all providers fail.
|
|
348
|
+
*/
|
|
349
|
+
function createStaticOnlyResult(staticResult, mode, startTime) {
|
|
350
|
+
// Determine status from static findings severity
|
|
351
|
+
const allFindings = [
|
|
352
|
+
...staticResult.semgrep.findings,
|
|
353
|
+
...staticResult.trivy.findings,
|
|
354
|
+
...staticResult.cpd.findings,
|
|
355
|
+
];
|
|
356
|
+
const hasCriticalOrHigh = allFindings.some((f) => f.severity === 'critical' || f.severity === 'high');
|
|
357
|
+
return {
|
|
358
|
+
status: hasCriticalOrHigh ? 'FAILED' : 'PASSED',
|
|
359
|
+
summary: allFindings.length > 0
|
|
360
|
+
? `Static analysis found ${allFindings.length} finding(s). AI review was not performed.`
|
|
361
|
+
: 'Static analysis found no issues. AI review was not performed.',
|
|
362
|
+
findings: [], // Will be merged in step 7
|
|
363
|
+
staticAnalysis: staticResult,
|
|
364
|
+
memoryContext: null,
|
|
365
|
+
metadata: {
|
|
366
|
+
mode,
|
|
367
|
+
provider: 'none',
|
|
368
|
+
model: 'static-only',
|
|
369
|
+
tokensUsed: 0,
|
|
370
|
+
executionTimeMs: Date.now() - startTime,
|
|
371
|
+
toolsRun: [],
|
|
372
|
+
toolsSkipped: [],
|
|
373
|
+
},
|
|
374
|
+
};
|
|
375
|
+
}
|
|
267
376
|
//# sourceMappingURL=pipeline.js.map
|