@hanhnd/agent-kit 1.0.34 → 1.0.35
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/dist/cli/commands/cli-digest-pending.test.js +19 -10
- package/dist/cli/commands/memory.js +44 -4
- package/dist/cli.js +307 -129
- package/dist/core/config/index.d.ts +3 -0
- package/dist/core/config/index.js +20 -0
- package/dist/entrypoints/server.js +1 -3
- package/dist/mcp/memory.d.ts +2 -1
- package/dist/mcp/memory.js +45 -18
- package/dist/server.js +253 -150
- package/dist/services/digest/constants.d.ts +3 -3
- package/dist/services/digest/constants.js +3 -3
- package/dist/services/digest/digest-background.test.js +19 -8
- package/dist/services/digest/digest-init.test.js +13 -2
- package/dist/services/digest/digest-pending.test.js +13 -2
- package/dist/services/digest/digest-processor.test.js +66 -0
- package/dist/services/digest/files.js +3 -3
- package/dist/services/digest/model-registry.js +2 -2
- package/dist/services/digest/processor.js +43 -13
- package/dist/services/digest/providers/llama-local.js +17 -28
- package/dist/services/digest/types.d.ts +3 -1
- package/dist/services/digest/types.js +1 -1
- package/dist/services/memory/chunker.d.ts +5 -2
- package/dist/services/memory/chunker.js +3 -1
- package/dist/services/memory/chunker.test.js +21 -11
- package/dist/services/memory/constants.js +1 -1
- package/dist/services/memory/embedder.js +1 -1
- package/dist/services/memory/index.d.ts +1 -1
- package/dist/services/memory/indexer.d.ts +2 -1
- package/dist/services/memory/indexer.js +24 -8
- package/dist/services/memory/indexer.test.js +38 -2
- package/dist/services/memory/memory.test.js +172 -15
- package/dist/services/memory/store.d.ts +6 -1
- package/dist/services/memory/store.js +87 -47
- package/dist/services/memory/store.test.js +123 -3
- package/dist/services/memory/types.d.ts +9 -1
- package/dist/services/memory/types.js +2 -1
- package/dist/utils/paths.d.ts +2 -0
- package/dist/utils/paths.js +2 -1
- package/dist/utils/utils.d.ts +1 -0
- package/dist/utils/utils.js +3 -3
- package/package.json +2 -2
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import Database from 'better-sqlite3';
|
|
2
2
|
import * as fs from 'fs';
|
|
3
|
+
import { createRequire } from 'module';
|
|
3
4
|
import * as path from 'path';
|
|
4
5
|
import { eng, removeStopwords } from 'stopword';
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
export const SCHEMA_VERSION = 2;
|
|
5
8
|
export class StoreError extends Error {
|
|
6
9
|
constructor(message, cause) {
|
|
7
10
|
super(message);
|
|
@@ -46,7 +49,6 @@ export class MemoryStore {
|
|
|
46
49
|
}
|
|
47
50
|
loadVecExtension(db) {
|
|
48
51
|
try {
|
|
49
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
50
52
|
const sqliteVec = require('sqlite-vec');
|
|
51
53
|
sqliteVec.load(db);
|
|
52
54
|
this._vecAvailable = true;
|
|
@@ -57,58 +59,79 @@ export class MemoryStore {
|
|
|
57
59
|
}
|
|
58
60
|
}
|
|
59
61
|
createSchema(db) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
id TEXT NOT NULL UNIQUE,
|
|
64
|
-
source TEXT NOT NULL,
|
|
65
|
-
heading TEXT NOT NULL DEFAULT '',
|
|
66
|
-
heading_level INTEGER NOT NULL DEFAULT 0,
|
|
67
|
-
content TEXT NOT NULL,
|
|
68
|
-
line_start INTEGER NOT NULL,
|
|
69
|
-
line_end INTEGER NOT NULL,
|
|
70
|
-
indexed_at INTEGER NOT NULL
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS memory_fts USING fts5(
|
|
74
|
-
source,
|
|
75
|
-
heading,
|
|
76
|
-
content,
|
|
77
|
-
content='memory_chunks',
|
|
78
|
-
content_rowid='rowid'
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
CREATE TRIGGER IF NOT EXISTS memory_chunks_ai
|
|
82
|
-
AFTER INSERT ON memory_chunks BEGIN
|
|
83
|
-
INSERT INTO memory_fts(rowid, source, heading, content)
|
|
84
|
-
VALUES (new.rowid, new.source, new.heading, new.content);
|
|
85
|
-
END;
|
|
86
|
-
|
|
87
|
-
CREATE TRIGGER IF NOT EXISTS memory_chunks_ad
|
|
88
|
-
AFTER DELETE ON memory_chunks BEGIN
|
|
89
|
-
INSERT INTO memory_fts(memory_fts, rowid, source, heading, content)
|
|
90
|
-
VALUES ('delete', old.rowid, old.source, old.heading, old.content);
|
|
91
|
-
END;
|
|
92
|
-
`);
|
|
93
|
-
if (this._vecAvailable) {
|
|
94
|
-
try {
|
|
62
|
+
try {
|
|
63
|
+
const current = Number(db.pragma('user_version', { simple: true }) ?? 0);
|
|
64
|
+
if (current < SCHEMA_VERSION) {
|
|
95
65
|
db.exec(`
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
66
|
+
DROP TRIGGER IF EXISTS memory_chunks_ai;
|
|
67
|
+
DROP TRIGGER IF EXISTS memory_chunks_ad;
|
|
68
|
+
DROP TABLE IF EXISTS memory_fts;
|
|
69
|
+
DROP TABLE IF EXISTS memory_vec;
|
|
70
|
+
DROP TABLE IF EXISTS memory_chunks;
|
|
99
71
|
`);
|
|
72
|
+
db.pragma(`user_version = ${SCHEMA_VERSION}`);
|
|
100
73
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
74
|
+
db.exec(`
|
|
75
|
+
CREATE TABLE IF NOT EXISTS memory_chunks (
|
|
76
|
+
rowid INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
77
|
+
id TEXT NOT NULL UNIQUE,
|
|
78
|
+
source TEXT NOT NULL,
|
|
79
|
+
source_type TEXT NOT NULL DEFAULT 'wiki',
|
|
80
|
+
heading TEXT NOT NULL DEFAULT '',
|
|
81
|
+
heading_level INTEGER NOT NULL DEFAULT 0,
|
|
82
|
+
content TEXT NOT NULL,
|
|
83
|
+
line_start INTEGER NOT NULL,
|
|
84
|
+
line_end INTEGER NOT NULL,
|
|
85
|
+
indexed_at INTEGER NOT NULL,
|
|
86
|
+
file_mtime_at INTEGER NOT NULL DEFAULT 0
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
CREATE INDEX IF NOT EXISTS idx_memory_chunks_type_mtime
|
|
90
|
+
ON memory_chunks (source_type, file_mtime_at DESC);
|
|
91
|
+
|
|
92
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS memory_fts USING fts5(
|
|
93
|
+
source,
|
|
94
|
+
heading,
|
|
95
|
+
content,
|
|
96
|
+
content='memory_chunks',
|
|
97
|
+
content_rowid='rowid'
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
CREATE TRIGGER IF NOT EXISTS memory_chunks_ai
|
|
101
|
+
AFTER INSERT ON memory_chunks BEGIN
|
|
102
|
+
INSERT INTO memory_fts(rowid, source, heading, content)
|
|
103
|
+
VALUES (new.rowid, new.source, new.heading, new.content);
|
|
104
|
+
END;
|
|
105
|
+
|
|
106
|
+
CREATE TRIGGER IF NOT EXISTS memory_chunks_ad
|
|
107
|
+
AFTER DELETE ON memory_chunks BEGIN
|
|
108
|
+
INSERT INTO memory_fts(memory_fts, rowid, source, heading, content)
|
|
109
|
+
VALUES ('delete', old.rowid, old.source, old.heading, old.content);
|
|
110
|
+
END;
|
|
111
|
+
`);
|
|
112
|
+
if (this._vecAvailable) {
|
|
113
|
+
try {
|
|
114
|
+
db.exec(`
|
|
115
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS memory_vec USING vec0(
|
|
116
|
+
embedding FLOAT[${this.config.vectorDimension}]
|
|
117
|
+
);
|
|
118
|
+
`);
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
console.warn('[memory-store] Failed to create memory_vec, disabling vector search:', err);
|
|
122
|
+
this._vecAvailable = false;
|
|
123
|
+
}
|
|
104
124
|
}
|
|
105
125
|
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
throw new StoreError('schema migration failed', err);
|
|
128
|
+
}
|
|
106
129
|
}
|
|
107
130
|
upsert(chunks, embeddings) {
|
|
108
131
|
const insertChunk = this.db.prepare(`
|
|
109
132
|
INSERT OR REPLACE INTO memory_chunks
|
|
110
|
-
(id, source, heading, heading_level, content, line_start, line_end, indexed_at)
|
|
111
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
133
|
+
(id, source, source_type, heading, heading_level, content, line_start, line_end, indexed_at, file_mtime_at)
|
|
134
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
112
135
|
`);
|
|
113
136
|
let insertVec = null;
|
|
114
137
|
if (this._vecAvailable) {
|
|
@@ -120,9 +143,9 @@ export class MemoryStore {
|
|
|
120
143
|
try {
|
|
121
144
|
// Delete existing row first to get correct rowid
|
|
122
145
|
this.db.prepare(`DELETE FROM memory_chunks WHERE id = ?`).run(chunk.id);
|
|
123
|
-
const info = insertChunk.run(chunk.id, chunk.source, chunk.heading, chunk.headingLevel, chunk.content, chunk.lineStart, chunk.lineEnd, Date.now());
|
|
146
|
+
const info = insertChunk.run(chunk.id, chunk.source, chunk.sourceType, chunk.heading, chunk.headingLevel, chunk.content, chunk.lineStart, chunk.lineEnd, Date.now(), chunk.fileMtimeAt);
|
|
124
147
|
if (insertVec && embeddings[i] && info.lastInsertRowid) {
|
|
125
|
-
const rowid =
|
|
148
|
+
const rowid = typeof info.lastInsertRowid === 'bigint' ? info.lastInsertRowid : BigInt(info.lastInsertRowid);
|
|
126
149
|
const emb = embeddings[i];
|
|
127
150
|
const buf = Buffer.from(emb.buffer, emb.byteOffset, emb.byteLength);
|
|
128
151
|
insertVec.run(rowid, buf);
|
|
@@ -148,6 +171,21 @@ export class MemoryStore {
|
|
|
148
171
|
const rows = this.db.prepare(`SELECT DISTINCT source FROM memory_chunks`).all();
|
|
149
172
|
return rows.map((r) => r.source);
|
|
150
173
|
}
|
|
174
|
+
getRecentSources(options) {
|
|
175
|
+
const limit = Math.min(Math.max(1, options.limit || 5), 50);
|
|
176
|
+
const whereClause = options.sourceType ? ' WHERE source_type = ?' : '';
|
|
177
|
+
const params = options.sourceType ? [options.sourceType, limit] : [limit];
|
|
178
|
+
try {
|
|
179
|
+
const rows = this.db
|
|
180
|
+
.prepare(`SELECT source, MAX(file_mtime_at) AS t FROM memory_chunks${whereClause} GROUP BY source ORDER BY t DESC LIMIT ?`)
|
|
181
|
+
.all(...params);
|
|
182
|
+
return rows.map((row) => ({ source: row.source, fileMtimeAt: row.t }));
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
console.warn('[memory-store] Recent source lookup failed:', err);
|
|
186
|
+
return [];
|
|
187
|
+
}
|
|
188
|
+
}
|
|
151
189
|
deleteBySource(source) {
|
|
152
190
|
const rows = this.db.prepare(`SELECT rowid FROM memory_chunks WHERE source = ?`).all(source);
|
|
153
191
|
const deleteAll = this.db.transaction(() => {
|
|
@@ -239,17 +277,19 @@ export class MemoryStore {
|
|
|
239
277
|
return [];
|
|
240
278
|
const placeholders = ids.map(() => '?').join(', ');
|
|
241
279
|
const rows = this.db
|
|
242
|
-
.prepare(`SELECT id, source, heading, heading_level, content, line_start, line_end
|
|
280
|
+
.prepare(`SELECT id, source, source_type, heading, heading_level, content, line_start, line_end, file_mtime_at
|
|
243
281
|
FROM memory_chunks WHERE id IN (${placeholders})`)
|
|
244
282
|
.all(...ids);
|
|
245
283
|
return rows.map((r) => ({
|
|
246
284
|
id: r.id,
|
|
247
285
|
source: r.source,
|
|
286
|
+
sourceType: r.source_type,
|
|
248
287
|
heading: r.heading,
|
|
249
288
|
headingLevel: r.heading_level,
|
|
250
289
|
content: r.content,
|
|
251
290
|
lineStart: r.line_start,
|
|
252
291
|
lineEnd: r.line_end,
|
|
292
|
+
fileMtimeAt: r.file_mtime_at,
|
|
253
293
|
}));
|
|
254
294
|
}
|
|
255
295
|
get vecAvailable() {
|
|
@@ -2,8 +2,9 @@ import * as assert from 'node:assert/strict';
|
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
3
|
import * as os from 'node:os';
|
|
4
4
|
import * as path from 'node:path';
|
|
5
|
+
import Database from 'better-sqlite3';
|
|
5
6
|
import { after, before, describe, test } from 'node:test';
|
|
6
|
-
import { MemoryStore } from './store.js';
|
|
7
|
+
import { MemoryStore, SCHEMA_VERSION } from './store.js';
|
|
7
8
|
import { EmbeddingModelName } from './types.js';
|
|
8
9
|
const TEST_CONFIG = {
|
|
9
10
|
enabled: true,
|
|
@@ -11,18 +12,20 @@ const TEST_CONFIG = {
|
|
|
11
12
|
topK: 5,
|
|
12
13
|
chunkSize: 1500,
|
|
13
14
|
overlapLines: 2,
|
|
14
|
-
embeddingModel: EmbeddingModelName.
|
|
15
|
+
embeddingModel: EmbeddingModelName.BASE,
|
|
15
16
|
vectorDimension: 384,
|
|
16
17
|
};
|
|
17
18
|
function makeChunk(overrides = {}) {
|
|
18
19
|
return {
|
|
19
20
|
id: 'test-id-0000001',
|
|
20
21
|
source: 'test.md',
|
|
22
|
+
sourceType: 'wiki',
|
|
21
23
|
heading: 'Test Heading',
|
|
22
24
|
headingLevel: 1,
|
|
23
25
|
content: 'This is test content for BM25 search.',
|
|
24
26
|
lineStart: 1,
|
|
25
27
|
lineEnd: 5,
|
|
28
|
+
fileMtimeAt: 1000,
|
|
26
29
|
...overrides,
|
|
27
30
|
};
|
|
28
31
|
}
|
|
@@ -48,11 +51,14 @@ describe('MemoryStore', () => {
|
|
|
48
51
|
assert.equal(hashes.size, 0);
|
|
49
52
|
});
|
|
50
53
|
test('upsert stores chunks and hashesBySource returns their ids', () => {
|
|
51
|
-
const chunk = makeChunk({ id: 'upsert-test-0001', source: 'upsert.md' });
|
|
54
|
+
const chunk = makeChunk({ id: 'upsert-test-0001', source: 'upsert.md', sourceType: 'entity', fileMtimeAt: 2000 });
|
|
52
55
|
const embedding = new Float32Array(384).fill(0.1);
|
|
53
56
|
store.upsert([chunk], [embedding]);
|
|
54
57
|
const hashes = store.hashesBySource('upsert.md');
|
|
55
58
|
assert.ok(hashes.has('upsert-test-0001'));
|
|
59
|
+
const [stored] = store.getChunksByIds(['upsert-test-0001']);
|
|
60
|
+
assert.equal(stored.sourceType, 'entity');
|
|
61
|
+
assert.equal(stored.fileMtimeAt, 2000);
|
|
56
62
|
});
|
|
57
63
|
test('deleteBySource removes all chunks for that source', () => {
|
|
58
64
|
const c1 = makeChunk({
|
|
@@ -108,11 +114,13 @@ describe('MemoryStore', () => {
|
|
|
108
114
|
const chunk = makeChunk({
|
|
109
115
|
id: 'get-by-ids-0001',
|
|
110
116
|
source: 'metadata.md',
|
|
117
|
+
sourceType: 'concept',
|
|
111
118
|
heading: 'Metadata Section',
|
|
112
119
|
headingLevel: 2,
|
|
113
120
|
content: 'Content for metadata test',
|
|
114
121
|
lineStart: 10,
|
|
115
122
|
lineEnd: 20,
|
|
123
|
+
fileMtimeAt: 3000,
|
|
116
124
|
});
|
|
117
125
|
store.upsert([chunk], [new Float32Array(384)]);
|
|
118
126
|
const results = store.getChunksByIds(['get-by-ids-0001']);
|
|
@@ -120,10 +128,12 @@ describe('MemoryStore', () => {
|
|
|
120
128
|
const [r] = results;
|
|
121
129
|
assert.equal(r.id, 'get-by-ids-0001');
|
|
122
130
|
assert.equal(r.source, 'metadata.md');
|
|
131
|
+
assert.equal(r.sourceType, 'concept');
|
|
123
132
|
assert.equal(r.heading, 'Metadata Section');
|
|
124
133
|
assert.equal(r.headingLevel, 2);
|
|
125
134
|
assert.equal(r.lineStart, 10);
|
|
126
135
|
assert.equal(r.lineEnd, 20);
|
|
136
|
+
assert.equal(r.fileMtimeAt, 3000);
|
|
127
137
|
});
|
|
128
138
|
test('getChunksByIds returns only found rows for mixed ids', () => {
|
|
129
139
|
const chunk = makeChunk({ id: 'partial-found-0001', source: 'partial.md', content: 'partial' });
|
|
@@ -162,4 +172,114 @@ describe('MemoryStore', () => {
|
|
|
162
172
|
const results = store.searchBm25('degradedModeTest', 5);
|
|
163
173
|
assert.ok(results.length > 0, 'FTS5 search must work regardless of vecAvailable');
|
|
164
174
|
});
|
|
175
|
+
test('getRecentSources returns distinct sources ordered by newest chunk mtime', () => {
|
|
176
|
+
const recentDir = fs.mkdtempSync(path.join(os.tmpdir(), 'memory-recent-store-'));
|
|
177
|
+
const recentStore = new MemoryStore(path.join(recentDir, 'index.db'), { ...TEST_CONFIG, wikiDir: recentDir });
|
|
178
|
+
try {
|
|
179
|
+
recentStore.upsert([
|
|
180
|
+
makeChunk({
|
|
181
|
+
id: 'recent-digest-old',
|
|
182
|
+
source: 'compiled/provisional/conversation-digests/old.md',
|
|
183
|
+
sourceType: 'digest',
|
|
184
|
+
fileMtimeAt: 100,
|
|
185
|
+
content: 'old digest',
|
|
186
|
+
}),
|
|
187
|
+
makeChunk({
|
|
188
|
+
id: 'recent-digest-new',
|
|
189
|
+
source: 'compiled/provisional/conversation-digests/new.md',
|
|
190
|
+
sourceType: 'digest',
|
|
191
|
+
fileMtimeAt: 300,
|
|
192
|
+
content: 'new digest',
|
|
193
|
+
}),
|
|
194
|
+
makeChunk({
|
|
195
|
+
id: 'recent-digest-newer-same-source',
|
|
196
|
+
source: 'compiled/provisional/conversation-digests/old.md',
|
|
197
|
+
sourceType: 'digest',
|
|
198
|
+
fileMtimeAt: 400,
|
|
199
|
+
content: 'old digest update',
|
|
200
|
+
}),
|
|
201
|
+
makeChunk({
|
|
202
|
+
id: 'recent-entity',
|
|
203
|
+
source: 'compiled/entities/entity.md',
|
|
204
|
+
sourceType: 'entity',
|
|
205
|
+
fileMtimeAt: 500,
|
|
206
|
+
content: 'entity',
|
|
207
|
+
}),
|
|
208
|
+
], [new Float32Array(384), new Float32Array(384), new Float32Array(384), new Float32Array(384)]);
|
|
209
|
+
const allRows = recentStore.getRecentSources({ limit: 10 });
|
|
210
|
+
assert.deepEqual(allRows.map((row) => row.source), [
|
|
211
|
+
'compiled/entities/entity.md',
|
|
212
|
+
'compiled/provisional/conversation-digests/old.md',
|
|
213
|
+
'compiled/provisional/conversation-digests/new.md',
|
|
214
|
+
]);
|
|
215
|
+
const digestRows = recentStore.getRecentSources({ limit: 10, sourceType: 'digest' });
|
|
216
|
+
assert.deepEqual(digestRows.map((row) => row.source), ['compiled/provisional/conversation-digests/old.md', 'compiled/provisional/conversation-digests/new.md']);
|
|
217
|
+
assert.equal(digestRows[0].fileMtimeAt, 400);
|
|
218
|
+
}
|
|
219
|
+
finally {
|
|
220
|
+
recentStore.close();
|
|
221
|
+
fs.rmSync(recentDir, { recursive: true, force: true });
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
test('schema migration recreates versioned schema with new columns and recency index', () => {
|
|
225
|
+
const migrationDir = fs.mkdtempSync(path.join(os.tmpdir(), 'memory-migration-'));
|
|
226
|
+
const migrationDbPath = path.join(migrationDir, 'index.db');
|
|
227
|
+
const db = new Database(migrationDbPath);
|
|
228
|
+
db.exec(`
|
|
229
|
+
PRAGMA user_version = 1;
|
|
230
|
+
CREATE TABLE memory_chunks (
|
|
231
|
+
rowid INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
232
|
+
id TEXT NOT NULL UNIQUE,
|
|
233
|
+
source TEXT NOT NULL,
|
|
234
|
+
heading TEXT NOT NULL DEFAULT '',
|
|
235
|
+
heading_level INTEGER NOT NULL DEFAULT 0,
|
|
236
|
+
content TEXT NOT NULL,
|
|
237
|
+
line_start INTEGER NOT NULL,
|
|
238
|
+
line_end INTEGER NOT NULL,
|
|
239
|
+
indexed_at INTEGER NOT NULL
|
|
240
|
+
);
|
|
241
|
+
CREATE VIRTUAL TABLE memory_fts USING fts5(
|
|
242
|
+
source,
|
|
243
|
+
heading,
|
|
244
|
+
content,
|
|
245
|
+
content='memory_chunks',
|
|
246
|
+
content_rowid='rowid'
|
|
247
|
+
);
|
|
248
|
+
CREATE TRIGGER memory_chunks_ai
|
|
249
|
+
AFTER INSERT ON memory_chunks BEGIN
|
|
250
|
+
INSERT INTO memory_fts(rowid, source, heading, content)
|
|
251
|
+
VALUES (new.rowid, new.source, new.heading, new.content);
|
|
252
|
+
END;
|
|
253
|
+
CREATE TRIGGER memory_chunks_ad
|
|
254
|
+
AFTER DELETE ON memory_chunks BEGIN
|
|
255
|
+
INSERT INTO memory_fts(memory_fts, rowid, source, heading, content)
|
|
256
|
+
VALUES ('delete', old.rowid, old.source, old.heading, old.content);
|
|
257
|
+
END;
|
|
258
|
+
`);
|
|
259
|
+
db.prepare(`INSERT INTO memory_chunks
|
|
260
|
+
(id, source, heading, heading_level, content, line_start, line_end, indexed_at)
|
|
261
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run('old-row', 'old.md', '', 0, 'old content', 1, 1, 1);
|
|
262
|
+
db.close();
|
|
263
|
+
const migratedStore = new MemoryStore(migrationDbPath, { ...TEST_CONFIG, wikiDir: migrationDir });
|
|
264
|
+
try {
|
|
265
|
+
const verifyDb = new Database(migrationDbPath, { readonly: true });
|
|
266
|
+
try {
|
|
267
|
+
assert.equal(verifyDb.pragma('user_version', { simple: true }), SCHEMA_VERSION);
|
|
268
|
+
const columns = verifyDb.prepare(`PRAGMA table_info(memory_chunks)`).all();
|
|
269
|
+
assert.ok(columns.some((column) => column.name === 'source_type'));
|
|
270
|
+
assert.ok(columns.some((column) => column.name === 'file_mtime_at'));
|
|
271
|
+
const indexes = verifyDb.prepare(`PRAGMA index_list(memory_chunks)`).all();
|
|
272
|
+
assert.ok(indexes.some((index) => index.name === 'idx_memory_chunks_type_mtime'));
|
|
273
|
+
const rowCount = verifyDb.prepare(`SELECT COUNT(*) AS count FROM memory_chunks`).get();
|
|
274
|
+
assert.equal(rowCount.count, 0);
|
|
275
|
+
}
|
|
276
|
+
finally {
|
|
277
|
+
verifyDb.close();
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
finally {
|
|
281
|
+
migratedStore.close();
|
|
282
|
+
fs.rmSync(migrationDir, { recursive: true, force: true });
|
|
283
|
+
}
|
|
284
|
+
});
|
|
165
285
|
});
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import type { ConversationDigestSettings } from '../digest/types.js';
|
|
2
2
|
export declare enum EmbeddingModelName {
|
|
3
3
|
TINY = "tiny",
|
|
4
|
-
|
|
4
|
+
BASE = "base",
|
|
5
5
|
LARGE = "large"
|
|
6
6
|
}
|
|
7
|
+
export declare const SOURCE_TYPES: readonly ["digest", "concept", "entity", "preference", "wiki"];
|
|
8
|
+
export type SourceType = (typeof SOURCE_TYPES)[number];
|
|
7
9
|
export interface MemoryChunk {
|
|
8
10
|
id: string;
|
|
9
11
|
source: string;
|
|
12
|
+
sourceType: SourceType;
|
|
10
13
|
heading: string;
|
|
11
14
|
headingLevel: number;
|
|
12
15
|
content: string;
|
|
13
16
|
lineStart: number;
|
|
14
17
|
lineEnd: number;
|
|
18
|
+
fileMtimeAt: number;
|
|
15
19
|
}
|
|
16
20
|
export interface SearchResult {
|
|
17
21
|
chunk: MemoryChunk;
|
|
@@ -34,3 +38,7 @@ export interface IndexStats {
|
|
|
34
38
|
deleted: number;
|
|
35
39
|
skipped: number;
|
|
36
40
|
}
|
|
41
|
+
export interface RecentSource {
|
|
42
|
+
source: string;
|
|
43
|
+
fileMtimeAt: number;
|
|
44
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export var EmbeddingModelName;
|
|
2
2
|
(function (EmbeddingModelName) {
|
|
3
3
|
EmbeddingModelName["TINY"] = "tiny";
|
|
4
|
-
EmbeddingModelName["
|
|
4
|
+
EmbeddingModelName["BASE"] = "base";
|
|
5
5
|
EmbeddingModelName["LARGE"] = "large";
|
|
6
6
|
})(EmbeddingModelName || (EmbeddingModelName = {}));
|
|
7
|
+
export const SOURCE_TYPES = ['digest', 'concept', 'entity', 'preference', 'wiki'];
|
package/dist/utils/paths.d.ts
CHANGED
package/dist/utils/paths.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as os from 'os';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
-
const AGENT_KIT_HOME = path.join(os.homedir(), '.agent-kit');
|
|
3
|
+
export const AGENT_KIT_HOME = path.join(os.homedir(), '.agent-kit');
|
|
4
4
|
export const CREDENTIALS_FILE = path.join(AGENT_KIT_HOME, 'credentials');
|
|
5
|
+
export const GLOBAL_SETTINGS_FILE = path.join(AGENT_KIT_HOME, 'settings.json');
|
|
5
6
|
export const MODEL_CACHE_DIR = path.join(AGENT_KIT_HOME, 'cache', 'models');
|
|
6
7
|
export const FASTEMBED_CACHE_DIR = path.join(MODEL_CACHE_DIR, 'fastembed');
|
package/dist/utils/utils.d.ts
CHANGED
package/dist/utils/utils.js
CHANGED
|
@@ -3,9 +3,6 @@ export function getWorkspaceRoot() {
|
|
|
3
3
|
process.env.CLAUDE_PROJECT_DIR ||
|
|
4
4
|
process.env.CODEX_PROJECT_DIR ||
|
|
5
5
|
process.env.GEMINI_PROJECT_DIR ||
|
|
6
|
-
process.env.CLAUDE_WORKSPACE ||
|
|
7
|
-
process.env.CODEX_WORKSPACE ||
|
|
8
|
-
process.env.GEMINI_WORKSPACE ||
|
|
9
6
|
process.env.INIT_CWD ||
|
|
10
7
|
process.env.PWD ||
|
|
11
8
|
process.cwd());
|
|
@@ -16,3 +13,6 @@ export function mcpText(text) {
|
|
|
16
13
|
export function mcpJson(obj) {
|
|
17
14
|
return mcpText(JSON.stringify(obj, null, 2));
|
|
18
15
|
}
|
|
16
|
+
export function formatError(err) {
|
|
17
|
+
return err instanceof Error ? err.message : String(err);
|
|
18
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanhnd/agent-kit",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.35",
|
|
4
4
|
"description": "Agent Kit MCP server for software development agent workflows",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/server.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"scripts": {
|
|
24
24
|
"build": "rm -rf dist && tsc && npm run bundle",
|
|
25
|
-
"bundle": "esbuild src/entrypoints/server.ts src/entrypoints/cli.ts --bundle --platform=node --format=esm --outdir=dist --external:node:* --external:better-sqlite3 --external:sqlite-vec --external:fastembed --external:node-llama-cpp
|
|
25
|
+
"bundle": "esbuild src/entrypoints/server.ts src/entrypoints/cli.ts --bundle --platform=node --format=esm --outdir=dist --external:node:* --external:better-sqlite3 --external:sqlite-vec --external:fastembed --external:node-llama-cpp",
|
|
26
26
|
"dev": "tsc --watch",
|
|
27
27
|
"prepublishOnly": "npm run build",
|
|
28
28
|
"test": "tsc && node --test $(find dist -name '*.test.js' | tr '\\n' ' ')"
|