@hanhnd/agent-kit 1.0.34 → 1.0.36

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.
Files changed (41) hide show
  1. package/dist/cli/commands/cli-digest-pending.test.js +19 -10
  2. package/dist/cli/commands/memory.js +44 -4
  3. package/dist/cli.js +348 -179
  4. package/dist/core/config/index.d.ts +3 -0
  5. package/dist/core/config/index.js +20 -0
  6. package/dist/entrypoints/server.js +1 -3
  7. package/dist/mcp/memory.d.ts +2 -1
  8. package/dist/mcp/memory.js +45 -18
  9. package/dist/server.js +294 -200
  10. package/dist/services/digest/constants.d.ts +3 -3
  11. package/dist/services/digest/constants.js +3 -3
  12. package/dist/services/digest/digest-background.test.js +19 -8
  13. package/dist/services/digest/digest-init.test.js +13 -2
  14. package/dist/services/digest/digest-pending.test.js +13 -2
  15. package/dist/services/digest/digest-processor.test.js +52 -0
  16. package/dist/services/digest/files.js +3 -3
  17. package/dist/services/digest/model-registry.js +2 -2
  18. package/dist/services/digest/processor.js +43 -13
  19. package/dist/services/digest/providers/llama-local.js +17 -28
  20. package/dist/services/digest/types.d.ts +3 -1
  21. package/dist/services/digest/types.js +1 -1
  22. package/dist/services/memory/chunker.d.ts +5 -2
  23. package/dist/services/memory/chunker.js +3 -1
  24. package/dist/services/memory/chunker.test.js +21 -11
  25. package/dist/services/memory/constants.js +1 -1
  26. package/dist/services/memory/embedder.js +1 -1
  27. package/dist/services/memory/index.d.ts +1 -1
  28. package/dist/services/memory/indexer.d.ts +2 -1
  29. package/dist/services/memory/indexer.js +24 -8
  30. package/dist/services/memory/indexer.test.js +38 -2
  31. package/dist/services/memory/memory.test.js +172 -15
  32. package/dist/services/memory/store.d.ts +9 -3
  33. package/dist/services/memory/store.js +132 -98
  34. package/dist/services/memory/store.test.js +239 -14
  35. package/dist/services/memory/types.d.ts +9 -1
  36. package/dist/services/memory/types.js +2 -1
  37. package/dist/utils/paths.d.ts +2 -0
  38. package/dist/utils/paths.js +2 -1
  39. package/dist/utils/utils.d.ts +1 -0
  40. package/dist/utils/utils.js +3 -3
  41. package/package.json +3 -5
@@ -1,9 +1,10 @@
1
+ import Database from 'libsql';
1
2
  import * as assert from 'node:assert/strict';
2
3
  import * as fs from 'node:fs';
3
4
  import * as os from 'node:os';
4
5
  import * as path from 'node:path';
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,21 +12,42 @@ const TEST_CONFIG = {
11
12
  topK: 5,
12
13
  chunkSize: 1500,
13
14
  overlapLines: 2,
14
- embeddingModel: EmbeddingModelName.SMALL,
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
  }
32
+ function makeEmbedding(activeIndex, dimension = TEST_CONFIG.vectorDimension) {
33
+ const embedding = new Float32Array(dimension);
34
+ embedding[activeIndex] = 1;
35
+ return embedding;
36
+ }
37
+ function readUserVersion(db) {
38
+ const value = db.pragma('user_version', { simple: true });
39
+ if (typeof value === 'number')
40
+ return value;
41
+ if (Array.isArray(value)) {
42
+ const [row] = value;
43
+ return typeof row?.user_version === 'number' ? row.user_version : 0;
44
+ }
45
+ if (value && typeof value === 'object' && 'user_version' in value) {
46
+ const row = value;
47
+ return typeof row.user_version === 'number' ? row.user_version : 0;
48
+ }
49
+ return 0;
50
+ }
29
51
  describe('MemoryStore', () => {
30
52
  let tmpDir;
31
53
  let dbPath;
@@ -39,8 +61,50 @@ describe('MemoryStore', () => {
39
61
  store.close();
40
62
  fs.rmSync(tmpDir, { recursive: true, force: true });
41
63
  });
42
- test('vecAvailable is a boolean', () => {
43
- assert.equal(typeof store.vecAvailable, 'boolean');
64
+ test('libsql supports required local driver and vector features', () => {
65
+ const libsqlDir = fs.mkdtempSync(path.join(os.tmpdir(), 'memory-libsql-compat-'));
66
+ const libsqlDb = new Database(path.join(libsqlDir, 'index.db'));
67
+ try {
68
+ assert.equal(readUserVersion(libsqlDb), 0);
69
+ libsqlDb.pragma('journal_mode = WAL');
70
+ libsqlDb.pragma('busy_timeout = 5000');
71
+ libsqlDb.pragma(`user_version = ${SCHEMA_VERSION}`);
72
+ assert.equal(readUserVersion(libsqlDb), SCHEMA_VERSION);
73
+ libsqlDb.exec(`
74
+ CREATE TABLE vector_items (
75
+ rowid INTEGER PRIMARY KEY AUTOINCREMENT,
76
+ name TEXT NOT NULL,
77
+ embedding F32_BLOB(3) NOT NULL
78
+ );
79
+ CREATE INDEX idx_vector_items_embedding
80
+ ON vector_items (libsql_vector_idx(embedding));
81
+ `);
82
+ const insert = libsqlDb.prepare(`INSERT INTO vector_items (name, embedding) VALUES (?, vector32(?))`);
83
+ const info = insert.run('alpha', '[1,0,0]');
84
+ assert.equal(info.lastInsertRowid, 1);
85
+ insert.run('beta', '[0,1,0]');
86
+ const insertGamma = libsqlDb.transaction(() => insert.run('gamma', '[0,0,1]'));
87
+ insertGamma();
88
+ const distance = libsqlDb
89
+ .prepare(`SELECT vector_distance_cos(embedding, vector32(?)) AS distance FROM vector_items WHERE name = ?`)
90
+ .get('[1,0,0]', 'alpha');
91
+ assert.equal(distance.distance, 0);
92
+ const rows = libsqlDb
93
+ .prepare(`SELECT vector_items.name
94
+ FROM vector_top_k('idx_vector_items_embedding', vector32(?), ?) AS vector_matches
95
+ JOIN vector_items ON vector_items.rowid = vector_matches.id
96
+ ORDER BY vector_distance_cos(vector_items.embedding, vector32(?))`)
97
+ .all('[1,0,0]', 2, '[1,0,0]');
98
+ assert.equal(rows[0].name, 'alpha');
99
+ assert.equal(rows.length, 2);
100
+ }
101
+ finally {
102
+ libsqlDb.close();
103
+ fs.rmSync(libsqlDir, { recursive: true, force: true });
104
+ }
105
+ });
106
+ test('vecAvailable is true after schema creation', () => {
107
+ assert.equal(store.vecAvailable, true);
44
108
  });
45
109
  test('hashesBySource returns empty set for unknown source', () => {
46
110
  const hashes = store.hashesBySource('nonexistent.md');
@@ -48,11 +112,14 @@ describe('MemoryStore', () => {
48
112
  assert.equal(hashes.size, 0);
49
113
  });
50
114
  test('upsert stores chunks and hashesBySource returns their ids', () => {
51
- const chunk = makeChunk({ id: 'upsert-test-0001', source: 'upsert.md' });
52
- const embedding = new Float32Array(384).fill(0.1);
115
+ const chunk = makeChunk({ id: 'upsert-test-0001', source: 'upsert.md', sourceType: 'entity', fileMtimeAt: 2000 });
116
+ const embedding = makeEmbedding(0);
53
117
  store.upsert([chunk], [embedding]);
54
118
  const hashes = store.hashesBySource('upsert.md');
55
119
  assert.ok(hashes.has('upsert-test-0001'));
120
+ const [stored] = store.getChunksByIds(['upsert-test-0001']);
121
+ assert.equal(stored.sourceType, 'entity');
122
+ assert.equal(stored.fileMtimeAt, 2000);
56
123
  });
57
124
  test('deleteBySource removes all chunks for that source', () => {
58
125
  const c1 = makeChunk({
@@ -65,7 +132,7 @@ describe('MemoryStore', () => {
65
132
  source: 'deleteme.md',
66
133
  content: 'delete source chunk 2',
67
134
  });
68
- store.upsert([c1, c2], [new Float32Array(384), new Float32Array(384)]);
135
+ store.upsert([c1, c2], [makeEmbedding(1), makeEmbedding(2)]);
69
136
  store.deleteBySource('deleteme.md');
70
137
  const hashes = store.hashesBySource('deleteme.md');
71
138
  assert.equal(hashes.size, 0);
@@ -76,7 +143,7 @@ describe('MemoryStore', () => {
76
143
  source: 'bm25.md',
77
144
  content: 'uniqueKeywordXYZ for BM25 testing',
78
145
  });
79
- store.upsert([chunk], [new Float32Array(384)]);
146
+ store.upsert([chunk], [makeEmbedding(3)]);
80
147
  const results = store.searchBm25('uniqueKeywordXYZ', 5);
81
148
  assert.ok(results.length > 0, 'Expected at least one BM25 result');
82
149
  assert.ok(results.some((r) => r.id === 'bm25-search-0001'), `Expected chunk id in results, got: ${results.map((r) => r.id).join(', ')}`);
@@ -95,7 +162,7 @@ describe('MemoryStore', () => {
95
162
  heading: 'Open Questions',
96
163
  content: 'How should git manage worktree lifecycle decisions?',
97
164
  });
98
- store.upsert([preference, unrelated], [new Float32Array(384), new Float32Array(384)]);
165
+ store.upsert([preference, unrelated], [makeEmbedding(4), makeEmbedding(5)]);
99
166
  const results = store.searchBm25('personal likes and preferences of the user', 5);
100
167
  assert.ok(results.length > 0, 'Expected preference query to return results');
101
168
  assert.equal(results[0].id, 'preference-search-0001');
@@ -108,26 +175,30 @@ describe('MemoryStore', () => {
108
175
  const chunk = makeChunk({
109
176
  id: 'get-by-ids-0001',
110
177
  source: 'metadata.md',
178
+ sourceType: 'concept',
111
179
  heading: 'Metadata Section',
112
180
  headingLevel: 2,
113
181
  content: 'Content for metadata test',
114
182
  lineStart: 10,
115
183
  lineEnd: 20,
184
+ fileMtimeAt: 3000,
116
185
  });
117
- store.upsert([chunk], [new Float32Array(384)]);
186
+ store.upsert([chunk], [makeEmbedding(6)]);
118
187
  const results = store.getChunksByIds(['get-by-ids-0001']);
119
188
  assert.equal(results.length, 1);
120
189
  const [r] = results;
121
190
  assert.equal(r.id, 'get-by-ids-0001');
122
191
  assert.equal(r.source, 'metadata.md');
192
+ assert.equal(r.sourceType, 'concept');
123
193
  assert.equal(r.heading, 'Metadata Section');
124
194
  assert.equal(r.headingLevel, 2);
125
195
  assert.equal(r.lineStart, 10);
126
196
  assert.equal(r.lineEnd, 20);
197
+ assert.equal(r.fileMtimeAt, 3000);
127
198
  });
128
199
  test('getChunksByIds returns only found rows for mixed ids', () => {
129
200
  const chunk = makeChunk({ id: 'partial-found-0001', source: 'partial.md', content: 'partial' });
130
- store.upsert([chunk], [new Float32Array(384)]);
201
+ store.upsert([chunk], [makeEmbedding(7)]);
131
202
  const results = store.getChunksByIds(['partial-found-0001', 'does-not-exist-999']);
132
203
  assert.equal(results.length, 1);
133
204
  assert.equal(results[0].id, 'partial-found-0001');
@@ -138,14 +209,14 @@ describe('MemoryStore', () => {
138
209
  source: 'indexed-source.md',
139
210
  content: 'indexed',
140
211
  });
141
- store.upsert([chunk], [new Float32Array(384)]);
212
+ store.upsert([chunk], [makeEmbedding(8)]);
142
213
  const sources = store.indexedSources();
143
214
  assert.ok(sources.includes('indexed-source.md'), `Expected 'indexed-source.md' in ${sources.join(', ')}`);
144
215
  });
145
216
  test('deleteByIds removes specific chunks', () => {
146
217
  const c1 = makeChunk({ id: 'del-ids-0001', source: 'del-ids.md', content: 'delete by id 1' });
147
218
  const c2 = makeChunk({ id: 'del-ids-0002', source: 'del-ids.md', content: 'delete by id 2' });
148
- store.upsert([c1, c2], [new Float32Array(384), new Float32Array(384)]);
219
+ store.upsert([c1, c2], [makeEmbedding(9), makeEmbedding(10)]);
149
220
  store.deleteByIds(['del-ids-0001']);
150
221
  const hashes = store.hashesBySource('del-ids.md');
151
222
  assert.ok(!hashes.has('del-ids-0001'), 'Deleted chunk id must be gone');
@@ -158,8 +229,162 @@ describe('MemoryStore', () => {
158
229
  source: 'fts5.md',
159
230
  content: 'degradedModeTest keyword',
160
231
  });
161
- store.upsert([chunk], [new Float32Array(384)]);
232
+ store.upsert([chunk], [makeEmbedding(11)]);
162
233
  const results = store.searchBm25('degradedModeTest', 5);
163
234
  assert.ok(results.length > 0, 'FTS5 search must work regardless of vecAvailable');
164
235
  });
236
+ test('getRecentSources returns distinct sources ordered by newest chunk mtime', () => {
237
+ const recentDir = fs.mkdtempSync(path.join(os.tmpdir(), 'memory-recent-store-'));
238
+ const recentStore = new MemoryStore(path.join(recentDir, 'index.db'), { ...TEST_CONFIG, wikiDir: recentDir });
239
+ try {
240
+ recentStore.upsert([
241
+ makeChunk({
242
+ id: 'recent-digest-old',
243
+ source: 'compiled/provisional/conversation-digests/old.md',
244
+ sourceType: 'digest',
245
+ fileMtimeAt: 100,
246
+ content: 'old digest',
247
+ }),
248
+ makeChunk({
249
+ id: 'recent-digest-new',
250
+ source: 'compiled/provisional/conversation-digests/new.md',
251
+ sourceType: 'digest',
252
+ fileMtimeAt: 300,
253
+ content: 'new digest',
254
+ }),
255
+ makeChunk({
256
+ id: 'recent-digest-newer-same-source',
257
+ source: 'compiled/provisional/conversation-digests/old.md',
258
+ sourceType: 'digest',
259
+ fileMtimeAt: 400,
260
+ content: 'old digest update',
261
+ }),
262
+ makeChunk({
263
+ id: 'recent-entity',
264
+ source: 'compiled/entities/entity.md',
265
+ sourceType: 'entity',
266
+ fileMtimeAt: 500,
267
+ content: 'entity',
268
+ }),
269
+ ], [makeEmbedding(12), makeEmbedding(13), makeEmbedding(14), makeEmbedding(15)]);
270
+ const allRows = recentStore.getRecentSources({ limit: 10 });
271
+ assert.deepEqual(allRows.map((row) => row.source), [
272
+ 'compiled/entities/entity.md',
273
+ 'compiled/provisional/conversation-digests/old.md',
274
+ 'compiled/provisional/conversation-digests/new.md',
275
+ ]);
276
+ const digestRows = recentStore.getRecentSources({ limit: 10, sourceType: 'digest' });
277
+ assert.deepEqual(digestRows.map((row) => row.source), ['compiled/provisional/conversation-digests/old.md', 'compiled/provisional/conversation-digests/new.md']);
278
+ assert.equal(digestRows[0].fileMtimeAt, 400);
279
+ }
280
+ finally {
281
+ recentStore.close();
282
+ fs.rmSync(recentDir, { recursive: true, force: true });
283
+ }
284
+ });
285
+ test('searchDense returns nearest vector with bounded scores', () => {
286
+ const denseDir = fs.mkdtempSync(path.join(os.tmpdir(), 'memory-dense-store-'));
287
+ const denseStore = new MemoryStore(path.join(denseDir, 'index.db'), { ...TEST_CONFIG, wikiDir: denseDir });
288
+ try {
289
+ denseStore.upsert([
290
+ makeChunk({ id: 'dense-alpha', source: 'dense.md', content: 'alpha vector' }),
291
+ makeChunk({ id: 'dense-beta', source: 'dense.md', content: 'beta vector' }),
292
+ makeChunk({ id: 'dense-gamma', source: 'dense.md', content: 'gamma vector' }),
293
+ ], [makeEmbedding(21), makeEmbedding(22), makeEmbedding(23)]);
294
+ const results = denseStore.searchDense(makeEmbedding(22), 3);
295
+ assert.ok(results.length > 0, 'Expected dense search results');
296
+ assert.equal(results[0].id, 'dense-beta');
297
+ assert.ok(results.every((result) => result.score >= 0 && result.score <= 1));
298
+ }
299
+ finally {
300
+ denseStore.close();
301
+ fs.rmSync(denseDir, { recursive: true, force: true });
302
+ }
303
+ });
304
+ test('searchDense returns empty array for invalid query embedding', () => {
305
+ assert.deepEqual(store.searchDense(new Float32Array(3), 5), []);
306
+ });
307
+ test('upsert skips invalid embeddings while persisting valid chunks', () => {
308
+ const validChunk = makeChunk({ id: 'valid-embedding-0001', source: 'embedding-validation.md' });
309
+ const invalidChunk = makeChunk({ id: 'invalid-embedding-0001', source: 'embedding-validation.md' });
310
+ const nonFiniteChunk = makeChunk({ id: 'non-finite-embedding-0001', source: 'embedding-validation.md' });
311
+ const nonFiniteEmbedding = makeEmbedding(24);
312
+ nonFiniteEmbedding[25] = Number.NaN;
313
+ store.upsert([validChunk, invalidChunk, nonFiniteChunk], [makeEmbedding(24), new Float32Array(3), nonFiniteEmbedding]);
314
+ const hashes = store.hashesBySource('embedding-validation.md');
315
+ assert.ok(hashes.has('valid-embedding-0001'));
316
+ assert.ok(!hashes.has('invalid-embedding-0001'));
317
+ assert.ok(!hashes.has('non-finite-embedding-0001'));
318
+ });
319
+ test('schema migration recreates versioned schema with new columns, vector index, and working BM25', () => {
320
+ const migrationDir = fs.mkdtempSync(path.join(os.tmpdir(), 'memory-migration-'));
321
+ const migrationDbPath = path.join(migrationDir, 'index.db');
322
+ const db = new Database(migrationDbPath);
323
+ db.exec(`
324
+ PRAGMA user_version = 1;
325
+ CREATE TABLE memory_chunks (
326
+ rowid INTEGER PRIMARY KEY AUTOINCREMENT,
327
+ id TEXT NOT NULL UNIQUE,
328
+ source TEXT NOT NULL,
329
+ heading TEXT NOT NULL DEFAULT '',
330
+ heading_level INTEGER NOT NULL DEFAULT 0,
331
+ content TEXT NOT NULL,
332
+ line_start INTEGER NOT NULL,
333
+ line_end INTEGER NOT NULL,
334
+ indexed_at INTEGER NOT NULL
335
+ );
336
+ CREATE VIRTUAL TABLE memory_fts USING fts5(
337
+ source,
338
+ heading,
339
+ content,
340
+ content='memory_chunks',
341
+ content_rowid='rowid'
342
+ );
343
+ CREATE TRIGGER memory_chunks_ai
344
+ AFTER INSERT ON memory_chunks BEGIN
345
+ INSERT INTO memory_fts(rowid, source, heading, content)
346
+ VALUES (new.rowid, new.source, new.heading, new.content);
347
+ END;
348
+ CREATE TRIGGER memory_chunks_ad
349
+ AFTER DELETE ON memory_chunks BEGIN
350
+ INSERT INTO memory_fts(memory_fts, rowid, source, heading, content)
351
+ VALUES ('delete', old.rowid, old.source, old.heading, old.content);
352
+ END;
353
+ `);
354
+ db.prepare(`INSERT INTO memory_chunks
355
+ (id, source, heading, heading_level, content, line_start, line_end, indexed_at)
356
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run('old-row', 'old.md', '', 0, 'old content', 1, 1, 1);
357
+ db.close();
358
+ const migratedStore = new MemoryStore(migrationDbPath, { ...TEST_CONFIG, wikiDir: migrationDir });
359
+ try {
360
+ const verifyDb = new Database(migrationDbPath, { readonly: true });
361
+ try {
362
+ assert.equal(readUserVersion(verifyDb), SCHEMA_VERSION);
363
+ const columns = verifyDb.prepare(`PRAGMA table_info(memory_chunks)`).all();
364
+ assert.ok(columns.some((column) => column.name === 'source_type'));
365
+ assert.ok(columns.some((column) => column.name === 'file_mtime_at'));
366
+ assert.ok(columns.some((column) => column.name === 'embedding'));
367
+ const indexes = verifyDb.prepare(`PRAGMA index_list(memory_chunks)`).all();
368
+ assert.ok(indexes.some((index) => index.name === 'idx_memory_chunks_type_mtime'));
369
+ assert.ok(indexes.some((index) => index.name === 'idx_memory_chunks_embedding'));
370
+ const rowCount = verifyDb.prepare(`SELECT COUNT(*) AS count FROM memory_chunks`).get();
371
+ assert.equal(rowCount.count, 0);
372
+ }
373
+ finally {
374
+ verifyDb.close();
375
+ }
376
+ const migratedChunk = makeChunk({
377
+ id: 'migrated-bm25-0001',
378
+ source: 'migrated.md',
379
+ content: 'postMigrationKeyword is searchable after migration',
380
+ });
381
+ migratedStore.upsert([migratedChunk], [makeEmbedding(16)]);
382
+ const results = migratedStore.searchBm25('postMigrationKeyword', 5);
383
+ assert.ok(results.some((result) => result.id === 'migrated-bm25-0001'));
384
+ }
385
+ finally {
386
+ migratedStore.close();
387
+ fs.rmSync(migrationDir, { recursive: true, force: true });
388
+ }
389
+ });
165
390
  });
@@ -1,17 +1,21 @@
1
1
  import type { ConversationDigestSettings } from '../digest/types.js';
2
2
  export declare enum EmbeddingModelName {
3
3
  TINY = "tiny",
4
- SMALL = "small",
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["SMALL"] = "small";
4
+ EmbeddingModelName["BASE"] = "base";
5
5
  EmbeddingModelName["LARGE"] = "large";
6
6
  })(EmbeddingModelName || (EmbeddingModelName = {}));
7
+ export const SOURCE_TYPES = ['digest', 'concept', 'entity', 'preference', 'wiki'];
@@ -1,3 +1,5 @@
1
+ export declare const AGENT_KIT_HOME: string;
1
2
  export declare const CREDENTIALS_FILE: string;
3
+ export declare const GLOBAL_SETTINGS_FILE: string;
2
4
  export declare const MODEL_CACHE_DIR: string;
3
5
  export declare const FASTEMBED_CACHE_DIR: string;
@@ -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');
@@ -7,4 +7,5 @@ type McpTextResult = {
7
7
  };
8
8
  export declare function mcpText(text: string): McpTextResult;
9
9
  export declare function mcpJson(obj: unknown): McpTextResult;
10
+ export declare function formatError(err: unknown): string;
10
11
  export {};
@@ -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.34",
3
+ "version": "1.0.36",
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 --banner:js=\"import { createRequire } from 'module'; const require = createRequire(import.meta.url);\"",
25
+ "bundle": "esbuild src/entrypoints/server.ts src/entrypoints/cli.ts --bundle --platform=node --format=esm --outdir=dist --external:node:* --external:libsql --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' ' ')"
@@ -30,15 +30,13 @@
30
30
  "dependencies": {
31
31
  "node-llama-cpp": "^3.0.0",
32
32
  "@modelcontextprotocol/sdk": "^1.13.1",
33
- "better-sqlite3": "^11.0.0",
34
33
  "dedent": "^1.5.3",
35
34
  "fastembed": "^1.14.0",
36
- "sqlite-vec": "^0.1.0",
35
+ "libsql": "^0.5.29",
37
36
  "stopword": "^3.1.5",
38
37
  "zod": "^3.24.4"
39
38
  },
40
39
  "devDependencies": {
41
- "@types/better-sqlite3": "^7.6.0",
42
40
  "@types/node": "^22.15.21",
43
41
  "esbuild": "^0.25.4",
44
42
  "typescript": "^5.8.3"