@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.
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 +307 -129
  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 +253 -150
  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 +66 -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 +6 -1
  33. package/dist/services/memory/store.js +87 -47
  34. package/dist/services/memory/store.test.js +123 -3
  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 +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
- db.exec(`
61
- CREATE TABLE IF NOT EXISTS memory_chunks (
62
- rowid INTEGER PRIMARY KEY AUTOINCREMENT,
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
- CREATE VIRTUAL TABLE IF NOT EXISTS memory_vec USING vec0(
97
- embedding FLOAT[${this.config.vectorDimension}]
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
- catch (err) {
102
- console.warn('[memory-store] Failed to create memory_vec, disabling vector search:', err);
103
- this._vecAvailable = false;
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 = Number(info.lastInsertRowid);
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.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
  }
@@ -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
- 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.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 --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: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' ' ')"