@hanhnd/agent-kit 1.0.32 → 1.0.34
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 +111 -0
- package/dist/cli/commands/cli-memory.test.js +34 -0
- package/dist/cli/{memory.js → commands/memory.js} +24 -11
- package/dist/cli/index.js +2 -2
- package/dist/{utils → cli/utils}/args.d.ts +2 -1
- package/dist/{kit-cli.js → cli.js} +342 -58
- package/dist/{tools/config.d.ts → core/config/index.d.ts} +2 -7
- package/dist/{tools/config.js → core/config/index.js} +2 -13
- package/dist/core/handoffs/index.d.ts +17 -0
- package/dist/{tools/handoffs.js → core/handoffs/index.js} +15 -7
- package/dist/core/handoffs/index.test.js +88 -0
- package/dist/{tools/security.d.ts → core/security/index.d.ts} +0 -19
- package/dist/{tools/security.js → core/security/index.js} +3 -76
- package/dist/entrypoints/cli.js +11 -0
- package/dist/entrypoints/server.js +37 -0
- package/dist/{tools → mcp}/agent.d.ts +1 -1
- package/dist/{tools → mcp}/agent.js +4 -4
- package/dist/mcp/core.d.ts +17 -0
- package/dist/mcp/core.js +60 -0
- package/dist/mcp/core.test.js +98 -0
- package/dist/{tools → mcp}/integration.js +3 -3
- package/dist/mcp/memory.d.ts +15 -0
- package/dist/{tools → mcp}/memory.js +15 -13
- package/dist/{kit-server.js → server.js} +129 -98
- package/dist/services/digest/background.d.ts +11 -0
- package/dist/services/digest/background.js +258 -0
- package/dist/{memory → services}/digest/constants.d.ts +4 -1
- package/dist/{memory → services}/digest/constants.js +6 -2
- package/dist/services/digest/digest-background.test.js +206 -0
- package/dist/{tools/__tests__ → services/digest}/digest-init.test.js +4 -3
- package/dist/{memory/__tests__ → services/digest}/digest-llama-local.test.js +1 -13
- package/dist/{memory/__tests__ → services/digest}/digest-pending.test.js +39 -11
- package/dist/{memory/__tests__ → services/digest}/digest-processor.test.js +5 -7
- package/dist/{memory → services}/digest/files.d.ts +0 -1
- package/dist/{memory → services}/digest/files.js +2 -2
- package/dist/services/digest/model-registry.d.ts +2 -0
- package/dist/{memory → services}/digest/model-registry.js +9 -8
- package/dist/services/digest/processor.d.ts +13 -0
- package/dist/{memory → services}/digest/processor.js +48 -31
- package/dist/services/digest/providers/llama-local.d.ts +2 -0
- package/dist/{memory → services}/digest/providers/llama-local.js +3 -4
- package/dist/{memory → services}/digest/types.d.ts +34 -2
- package/dist/services/digest/types.js +6 -0
- package/dist/{memory → services}/digest/worker.d.ts +0 -1
- package/dist/{memory → services}/digest/worker.js +2 -2
- package/dist/{utils → services/integration}/credentials.js +1 -1
- package/dist/{utils/parser.js → services/integration/parser/adf.js} +1 -0
- package/dist/{memory → services/memory}/chunker.d.ts +0 -2
- package/dist/{memory → services/memory}/chunker.js +3 -3
- package/dist/{memory/__tests__ → services/memory}/chunker.test.js +1 -34
- package/dist/{memory → services/memory}/constants.d.ts +1 -1
- package/dist/{memory → services/memory}/constants.js +2 -1
- package/dist/{memory → services/memory}/embedder.d.ts +2 -2
- package/dist/{memory → services/memory}/embedder.js +5 -5
- package/dist/services/memory/embedder.test.js +13 -0
- package/dist/{memory → services/memory}/indexer.d.ts +2 -1
- package/dist/{memory → services/memory}/indexer.js +1 -1
- package/dist/{memory/__tests__ → services/memory}/indexer.test.js +4 -3
- package/dist/{tools/__tests__ → services/memory}/memory.test.js +54 -13
- package/dist/{memory/__tests__ → services/memory}/store.test.js +3 -2
- package/dist/{memory → services/memory}/types.d.ts +7 -2
- package/dist/services/memory/types.js +6 -0
- package/dist/utils/files.d.ts +0 -1
- package/dist/utils/files.js +1 -1
- package/dist/{memory/__tests__/lockfile.test.js → utils/files.test.js} +14 -14
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.js +6 -0
- package/dist/utils/json.d.ts +0 -1
- package/dist/utils/json.js +0 -10
- package/dist/utils/paths.d.ts +0 -1
- package/dist/utils/paths.js +1 -1
- package/package.json +6 -6
- package/dist/memory/__tests__/digest-worker.test.js +0 -46
- package/dist/memory/__tests__/embedder.test.js +0 -14
- package/dist/memory/digest/model-registry.d.ts +0 -6
- package/dist/memory/digest/processor.d.ts +0 -7
- package/dist/memory/digest/providers/llama-local.d.ts +0 -7
- package/dist/tools/__tests__/cli-digest-pending.test.js +0 -51
- package/dist/tools/__tests__/cli-memory.test.js +0 -22
- package/dist/tools/core.d.ts +0 -10
- package/dist/tools/core.js +0 -46
- package/dist/tools/handoffs.d.ts +0 -22
- package/dist/tools/memory.d.ts +0 -16
- /package/dist/{tools/__tests__ → cli/commands}/cli-digest-pending.test.d.ts +0 -0
- /package/dist/{tools/__tests__ → cli/commands}/cli-memory.test.d.ts +0 -0
- /package/dist/cli/{memory.d.ts → commands/memory.d.ts} +0 -0
- /package/dist/{utils → cli/utils}/args.js +0 -0
- /package/dist/{memory/__tests__/chunker.test.d.ts → core/handoffs/index.test.d.ts} +0 -0
- /package/dist/{kit-cli.d.ts → entrypoints/cli.d.ts} +0 -0
- /package/dist/{kit-server.d.ts → entrypoints/server.d.ts} +0 -0
- /package/dist/{memory/__tests__/digest-llama-local.test.d.ts → mcp/core.test.d.ts} +0 -0
- /package/dist/{tools → mcp}/integration.d.ts +0 -0
- /package/dist/{memory/__tests__/digest-pending.test.d.ts → services/digest/digest-background.test.d.ts} +0 -0
- /package/dist/{tools/__tests__ → services/digest}/digest-init.test.d.ts +0 -0
- /package/dist/{memory/__tests__/digest-processor.test.d.ts → services/digest/digest-llama-local.test.d.ts} +0 -0
- /package/dist/{memory/__tests__/digest-worker.test.d.ts → services/digest/digest-pending.test.d.ts} +0 -0
- /package/dist/{memory/__tests__/embedder.test.d.ts → services/digest/digest-processor.test.d.ts} +0 -0
- /package/dist/{utils → services/integration}/credentials.d.ts +0 -0
- /package/dist/{utils/parser.d.ts → services/integration/parser/adf.d.ts} +0 -0
- /package/dist/{memory/__tests__/indexer.test.d.ts → services/memory/chunker.test.d.ts} +0 -0
- /package/dist/{memory/__tests__/lockfile.test.d.ts → services/memory/embedder.test.d.ts} +0 -0
- /package/dist/{memory → services/memory}/index.d.ts +0 -0
- /package/dist/{memory → services/memory}/index.js +0 -0
- /package/dist/{memory/__tests__/store.test.d.ts → services/memory/indexer.test.d.ts} +0 -0
- /package/dist/{tools/__tests__ → services/memory}/memory.test.d.ts +0 -0
- /package/dist/{memory → services/memory}/store.d.ts +0 -0
- /package/dist/{memory → services/memory}/store.js +0 -0
- /package/dist/{memory/digest/types.js → services/memory/store.test.d.ts} +0 -0
- /package/dist/{memory/types.js → utils/files.test.d.ts} +0 -0
- /package/dist/{__tests__/temp-dir.d.ts → utils/temp-dir.test.d.ts} +0 -0
- /package/dist/{__tests__/temp-dir.js → utils/temp-dir.test.js} +0 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as assert from 'node:assert/strict';
|
|
2
2
|
import { describe, test } from 'node:test';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { EmbeddingModelName } from './types.js';
|
|
4
|
+
import { resolveMemoryConfig } from '../../core/config/index.js';
|
|
5
|
+
import { registerMemoryTools } from '../../mcp/memory.js';
|
|
5
6
|
// Minimal McpServer stub that captures tool registrations
|
|
6
7
|
function makeMockServer() {
|
|
7
8
|
const tools = new Map();
|
|
@@ -30,7 +31,7 @@ const BASE_CONFIG = {
|
|
|
30
31
|
topK: 5,
|
|
31
32
|
chunkSize: 1500,
|
|
32
33
|
overlapLines: 2,
|
|
33
|
-
embeddingModel:
|
|
34
|
+
embeddingModel: EmbeddingModelName.SMALL,
|
|
34
35
|
vectorDimension: 384,
|
|
35
36
|
};
|
|
36
37
|
function makeIndexerStub(overrides = {}) {
|
|
@@ -61,7 +62,11 @@ describe('kit_memory_search', () => {
|
|
|
61
62
|
const indexer = makeIndexerStub({ search: async () => [searchResult] });
|
|
62
63
|
const store = makeStoreStub(true);
|
|
63
64
|
const { server, tools } = makeMockServer();
|
|
64
|
-
|
|
65
|
+
registerMemoryTools(server, { memory: { enabled: true } }, '/tmp', {
|
|
66
|
+
indexer,
|
|
67
|
+
store,
|
|
68
|
+
config: BASE_CONFIG,
|
|
69
|
+
});
|
|
65
70
|
const result = await tools.get('kit_memory_search')({ query: 'test query' });
|
|
66
71
|
const text = extractText(result);
|
|
67
72
|
assert.ok(text.includes('Important memory content'), 'Result must include chunk content');
|
|
@@ -73,7 +78,11 @@ describe('kit_memory_search', () => {
|
|
|
73
78
|
const indexer = makeIndexerStub({ search: async () => [] });
|
|
74
79
|
const store = makeStoreStub(true);
|
|
75
80
|
const { server, tools } = makeMockServer();
|
|
76
|
-
|
|
81
|
+
registerMemoryTools(server, { memory: { enabled: true } }, '/tmp', {
|
|
82
|
+
indexer,
|
|
83
|
+
store,
|
|
84
|
+
config: BASE_CONFIG,
|
|
85
|
+
});
|
|
77
86
|
const result = await tools.get('kit_memory_search')({ query: 'nothing here' });
|
|
78
87
|
const text = extractText(result);
|
|
79
88
|
assert.ok(text.toLowerCase().includes('no memories') || text.includes('No memories'), `Expected "no memories" message, got: ${text.slice(0, 100)}`);
|
|
@@ -82,7 +91,11 @@ describe('kit_memory_search', () => {
|
|
|
82
91
|
const indexer = makeIndexerStub({ search: async () => [] });
|
|
83
92
|
const store = makeStoreStub(false);
|
|
84
93
|
const { server, tools } = makeMockServer();
|
|
85
|
-
|
|
94
|
+
registerMemoryTools(server, { memory: { enabled: true } }, '/tmp', {
|
|
95
|
+
indexer,
|
|
96
|
+
store,
|
|
97
|
+
config: BASE_CONFIG,
|
|
98
|
+
});
|
|
86
99
|
const result = await tools.get('kit_memory_search')({ query: 'test' });
|
|
87
100
|
const text = extractText(result);
|
|
88
101
|
assert.ok(text.includes('Vector search unavailable') || text.includes('keyword-only'), `Expected degraded warning in: ${text.slice(0, 200)}`);
|
|
@@ -98,7 +111,11 @@ describe('kit_memory_search', () => {
|
|
|
98
111
|
const indexer = makeIndexerStub({ search: async () => [searchResult] });
|
|
99
112
|
const store = makeStoreStub(false);
|
|
100
113
|
const { server, tools } = makeMockServer();
|
|
101
|
-
|
|
114
|
+
registerMemoryTools(server, { memory: { enabled: true } }, '/tmp', {
|
|
115
|
+
indexer,
|
|
116
|
+
store,
|
|
117
|
+
config: BASE_CONFIG,
|
|
118
|
+
});
|
|
102
119
|
const result = await tools.get('kit_memory_search')({ query: 'test' });
|
|
103
120
|
const text = extractText(result);
|
|
104
121
|
assert.ok(text.includes('Vector search unavailable') || text.includes('keyword-only'), `Expected degraded warning with results in: ${text.slice(0, 200)}`);
|
|
@@ -115,7 +132,11 @@ describe('kit_memory_search', () => {
|
|
|
115
132
|
const indexer = makeIndexerStub({ search: async () => [searchResult] });
|
|
116
133
|
const store = makeStoreStub(true);
|
|
117
134
|
const { server, tools } = makeMockServer();
|
|
118
|
-
|
|
135
|
+
registerMemoryTools(server, { memory: { enabled: true } }, '/tmp', {
|
|
136
|
+
indexer,
|
|
137
|
+
store,
|
|
138
|
+
config: BASE_CONFIG,
|
|
139
|
+
});
|
|
119
140
|
const result = await tools.get('kit_memory_search')({ query: 'test' });
|
|
120
141
|
const text = extractText(result);
|
|
121
142
|
assert.ok(text.includes('Source file unavailable'), `Expected fallback warning in: ${text}`);
|
|
@@ -131,7 +152,11 @@ describe('kit_memory_search', () => {
|
|
|
131
152
|
});
|
|
132
153
|
const store = makeStoreStub(true);
|
|
133
154
|
const { server, tools } = makeMockServer();
|
|
134
|
-
|
|
155
|
+
registerMemoryTools(server, { memory: { enabled: true } }, '/tmp', {
|
|
156
|
+
indexer,
|
|
157
|
+
store,
|
|
158
|
+
config: BASE_CONFIG,
|
|
159
|
+
});
|
|
135
160
|
await tools.get('kit_memory_search')({ query: 'test', top_k: 3 });
|
|
136
161
|
assert.equal(capturedTopK, 3);
|
|
137
162
|
});
|
|
@@ -145,7 +170,11 @@ describe('kit_memory_search', () => {
|
|
|
145
170
|
});
|
|
146
171
|
const store = makeStoreStub(true);
|
|
147
172
|
const { server, tools } = makeMockServer();
|
|
148
|
-
|
|
173
|
+
registerMemoryTools(server, { memory: { enabled: true } }, '/tmp', {
|
|
174
|
+
indexer,
|
|
175
|
+
store,
|
|
176
|
+
config: BASE_CONFIG,
|
|
177
|
+
});
|
|
149
178
|
await tools.get('kit_memory_search')({ query: 'test' });
|
|
150
179
|
assert.equal(capturedTopK, BASE_CONFIG.topK);
|
|
151
180
|
});
|
|
@@ -157,7 +186,11 @@ describe('kit_memory_search', () => {
|
|
|
157
186
|
});
|
|
158
187
|
const store = makeStoreStub(true);
|
|
159
188
|
const { server, tools } = makeMockServer();
|
|
160
|
-
|
|
189
|
+
registerMemoryTools(server, { memory: { enabled: true } }, '/tmp', {
|
|
190
|
+
indexer,
|
|
191
|
+
store,
|
|
192
|
+
config: BASE_CONFIG,
|
|
193
|
+
});
|
|
161
194
|
const result = await tools.get('kit_memory_search')({ query: 'test' });
|
|
162
195
|
const text = extractText(result);
|
|
163
196
|
assert.ok(text.includes('kit_memory_search failed'), `Expected error prefix, got: ${text}`);
|
|
@@ -171,7 +204,11 @@ describe('kit_memory_save', () => {
|
|
|
171
204
|
});
|
|
172
205
|
const store = makeStoreStub(true);
|
|
173
206
|
const { server, tools } = makeMockServer();
|
|
174
|
-
|
|
207
|
+
registerMemoryTools(server, { memory: { enabled: true } }, '/tmp', {
|
|
208
|
+
indexer,
|
|
209
|
+
store,
|
|
210
|
+
config: BASE_CONFIG,
|
|
211
|
+
});
|
|
175
212
|
const result = await tools.get('kit_memory_save')({ content: 'Some important note' });
|
|
176
213
|
const text = extractText(result);
|
|
177
214
|
const parsed = JSON.parse(text);
|
|
@@ -187,7 +224,11 @@ describe('kit_memory_save', () => {
|
|
|
187
224
|
});
|
|
188
225
|
const store = makeStoreStub(true);
|
|
189
226
|
const { server, tools } = makeMockServer();
|
|
190
|
-
|
|
227
|
+
registerMemoryTools(server, { memory: { enabled: true } }, '/tmp', {
|
|
228
|
+
indexer,
|
|
229
|
+
store,
|
|
230
|
+
config: BASE_CONFIG,
|
|
231
|
+
});
|
|
191
232
|
const result = await tools.get('kit_memory_save')({ content: 'note' });
|
|
192
233
|
const text = extractText(result);
|
|
193
234
|
const parsed = JSON.parse(text);
|
|
@@ -3,14 +3,15 @@ import * as fs from 'node:fs';
|
|
|
3
3
|
import * as os from 'node:os';
|
|
4
4
|
import * as path from 'node:path';
|
|
5
5
|
import { after, before, describe, test } from 'node:test';
|
|
6
|
-
import { MemoryStore } from '
|
|
6
|
+
import { MemoryStore } from './store.js';
|
|
7
|
+
import { EmbeddingModelName } from './types.js';
|
|
7
8
|
const TEST_CONFIG = {
|
|
8
9
|
enabled: true,
|
|
9
10
|
wikiDir: '',
|
|
10
11
|
topK: 5,
|
|
11
12
|
chunkSize: 1500,
|
|
12
13
|
overlapLines: 2,
|
|
13
|
-
embeddingModel:
|
|
14
|
+
embeddingModel: EmbeddingModelName.SMALL,
|
|
14
15
|
vectorDimension: 384,
|
|
15
16
|
};
|
|
16
17
|
function makeChunk(overrides = {}) {
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import type { ConversationDigestSettings } from '
|
|
1
|
+
import type { ConversationDigestSettings } from '../digest/types.js';
|
|
2
|
+
export declare enum EmbeddingModelName {
|
|
3
|
+
TINY = "tiny",
|
|
4
|
+
SMALL = "small",
|
|
5
|
+
LARGE = "large"
|
|
6
|
+
}
|
|
2
7
|
export interface MemoryChunk {
|
|
3
8
|
id: string;
|
|
4
9
|
source: string;
|
|
@@ -20,7 +25,7 @@ export interface MemoryConfig {
|
|
|
20
25
|
topK: number;
|
|
21
26
|
chunkSize: number;
|
|
22
27
|
overlapLines: number;
|
|
23
|
-
embeddingModel:
|
|
28
|
+
embeddingModel: EmbeddingModelName;
|
|
24
29
|
vectorDimension: number;
|
|
25
30
|
conversationDigest?: ConversationDigestSettings;
|
|
26
31
|
}
|
package/dist/utils/files.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export declare function atomicWriteTextFile(filePath: string, content: string): void;
|
|
2
2
|
export declare function atomicWriteJsonFile(filePath: string, value: unknown): void;
|
|
3
|
-
export declare function tryWriteFileExclusive(filePath: string, content: string): boolean;
|
|
4
3
|
export declare function acquireLock(lockPath: string, timeoutMs: number, retryMs: number): Promise<boolean>;
|
|
5
4
|
export declare function releaseLock(lockPath: string, opts?: {
|
|
6
5
|
ignoreErrors?: boolean;
|
package/dist/utils/files.js
CHANGED
|
@@ -9,7 +9,7 @@ export function atomicWriteTextFile(filePath, content) {
|
|
|
9
9
|
export function atomicWriteJsonFile(filePath, value) {
|
|
10
10
|
atomicWriteTextFile(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
11
11
|
}
|
|
12
|
-
|
|
12
|
+
function tryWriteFileExclusive(filePath, content) {
|
|
13
13
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
14
14
|
try {
|
|
15
15
|
fs.writeFileSync(filePath, content, { flag: 'wx' });
|
|
@@ -2,9 +2,9 @@ import * as assert from 'node:assert/strict';
|
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
3
|
import * as path from 'node:path';
|
|
4
4
|
import { afterEach, describe, test } from 'node:test';
|
|
5
|
-
import { createTempDirTracker } from '
|
|
6
|
-
import { DIGEST_LOCKFILE_REL_PATH } from '../digest/constants.js';
|
|
7
|
-
import { releaseLock, tryAcquireProcessLock } from '
|
|
5
|
+
import { createTempDirTracker } from './temp-dir.test.js';
|
|
6
|
+
import { DIGEST_LOCKFILE_REL_PATH } from '../services/digest/constants.js';
|
|
7
|
+
import { releaseLock, tryAcquireProcessLock } from './files.js';
|
|
8
8
|
const tempDirs = createTempDirTracker();
|
|
9
9
|
afterEach(() => {
|
|
10
10
|
tempDirs.cleanup();
|
|
@@ -12,20 +12,22 @@ afterEach(() => {
|
|
|
12
12
|
function lockPath(workspace) {
|
|
13
13
|
return path.join(workspace, DIGEST_LOCKFILE_REL_PATH);
|
|
14
14
|
}
|
|
15
|
+
function lockDir(workspace) {
|
|
16
|
+
return path.dirname(lockPath(workspace));
|
|
17
|
+
}
|
|
15
18
|
describe('tryAcquireProcessLock', () => {
|
|
16
19
|
test('C1: no lockfile → creates it with current PID and returns true', () => {
|
|
17
20
|
const workspace = tempDirs.makeTempDir('lockfile-c1-');
|
|
18
21
|
const acquired = tryAcquireProcessLock(lockPath(workspace));
|
|
19
22
|
assert.equal(acquired, true);
|
|
20
|
-
const lockFile =
|
|
23
|
+
const lockFile = lockPath(workspace);
|
|
21
24
|
const content = JSON.parse(fs.readFileSync(lockFile, 'utf8'));
|
|
22
25
|
assert.equal(content.pid, process.pid);
|
|
23
26
|
});
|
|
24
27
|
test('C2: lockfile with live foreign PID → returns false without modifying file', () => {
|
|
25
28
|
const workspace = tempDirs.makeTempDir('lockfile-c2-');
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
fs.mkdirSync(lockDir, { recursive: true });
|
|
29
|
+
const lockFile = lockPath(workspace);
|
|
30
|
+
fs.mkdirSync(lockDir(workspace), { recursive: true });
|
|
29
31
|
// Use process.pid as "live" pid (current process is definitely alive)
|
|
30
32
|
const livePid = process.pid;
|
|
31
33
|
fs.writeFileSync(lockFile, JSON.stringify({ pid: livePid }), 'utf8');
|
|
@@ -37,9 +39,8 @@ describe('tryAcquireProcessLock', () => {
|
|
|
37
39
|
});
|
|
38
40
|
test('C3: lockfile with dead PID (ESRCH) → deletes stale lock, creates new one, returns true', () => {
|
|
39
41
|
const workspace = tempDirs.makeTempDir('lockfile-c3-');
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
fs.mkdirSync(lockDir, { recursive: true });
|
|
42
|
+
const lockFile = lockPath(workspace);
|
|
43
|
+
fs.mkdirSync(lockDir(workspace), { recursive: true });
|
|
43
44
|
// PID 999999999 is virtually guaranteed to be dead
|
|
44
45
|
fs.writeFileSync(lockFile, JSON.stringify({ pid: 999999999 }), 'utf8');
|
|
45
46
|
const acquired = tryAcquireProcessLock(lockPath(workspace));
|
|
@@ -57,9 +58,8 @@ describe('tryAcquireProcessLock', () => {
|
|
|
57
58
|
});
|
|
58
59
|
test('stale unparseable lockfile → treated as stale, returns true', () => {
|
|
59
60
|
const workspace = tempDirs.makeTempDir('lockfile-stale-');
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
fs.mkdirSync(lockDir, { recursive: true });
|
|
61
|
+
const lockFile = lockPath(workspace);
|
|
62
|
+
fs.mkdirSync(lockDir(workspace), { recursive: true });
|
|
63
63
|
fs.writeFileSync(lockFile, 'not-json', 'utf8');
|
|
64
64
|
const acquired = tryAcquireProcessLock(lockPath(workspace));
|
|
65
65
|
assert.equal(acquired, true);
|
|
@@ -69,7 +69,7 @@ describe('releaseLock', () => {
|
|
|
69
69
|
test('C5: lockfile exists → deletes it', () => {
|
|
70
70
|
const workspace = tempDirs.makeTempDir('lockfile-c5-');
|
|
71
71
|
assert.equal(tryAcquireProcessLock(lockPath(workspace)), true);
|
|
72
|
-
const lockFile =
|
|
72
|
+
const lockFile = lockPath(workspace);
|
|
73
73
|
assert.equal(fs.existsSync(lockFile), true);
|
|
74
74
|
releaseLock(lockPath(workspace));
|
|
75
75
|
assert.equal(fs.existsSync(lockFile), false);
|
package/dist/utils/json.d.ts
CHANGED
package/dist/utils/json.js
CHANGED
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
import * as fs from 'node:fs';
|
|
2
1
|
export function isRecord(value) {
|
|
3
2
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
4
3
|
}
|
|
5
|
-
export function readJsonObjectFile(filePath) {
|
|
6
|
-
if (!fs.existsSync(filePath))
|
|
7
|
-
return {};
|
|
8
|
-
const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
9
|
-
if (!isRecord(parsed)) {
|
|
10
|
-
throw new Error(`${filePath} must contain a JSON object`);
|
|
11
|
-
}
|
|
12
|
-
return parsed;
|
|
13
|
-
}
|
package/dist/utils/paths.d.ts
CHANGED
package/dist/utils/paths.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as os from 'os';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
-
|
|
3
|
+
const AGENT_KIT_HOME = path.join(os.homedir(), '.agent-kit');
|
|
4
4
|
export const CREDENTIALS_FILE = path.join(AGENT_KIT_HOME, 'credentials');
|
|
5
5
|
export const MODEL_CACHE_DIR = path.join(AGENT_KIT_HOME, 'cache', 'models');
|
|
6
6
|
export const FASTEMBED_CACHE_DIR = path.join(MODEL_CACHE_DIR, 'fastembed');
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanhnd/agent-kit",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.34",
|
|
4
4
|
"description": "Agent Kit MCP server for software development agent workflows",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "dist/
|
|
6
|
+
"main": "dist/server.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"agent-kit": "dist/
|
|
9
|
-
"
|
|
8
|
+
"agent-kit": "dist/server.js",
|
|
9
|
+
"cli": "dist/cli.js"
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
12
|
"dist",
|
|
@@ -22,10 +22,10 @@
|
|
|
22
22
|
},
|
|
23
23
|
"scripts": {
|
|
24
24
|
"build": "rm -rf dist && tsc && npm run bundle",
|
|
25
|
-
"bundle": "esbuild src/
|
|
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);\"",
|
|
26
26
|
"dev": "tsc --watch",
|
|
27
27
|
"prepublishOnly": "npm run build",
|
|
28
|
-
"test": "tsc && node --test $(find
|
|
28
|
+
"test": "tsc && node --test $(find dist -name '*.test.js' | tr '\\n' ' ')"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"node-llama-cpp": "^3.0.0",
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import * as assert from 'node:assert/strict';
|
|
2
|
-
import { describe, test } from 'node:test';
|
|
3
|
-
import { parseDigestWorkerSuccess } from '../digest/worker.js';
|
|
4
|
-
import { DIGEST_WORKER_RESULT_PREFIX } from '../digest/constants.js';
|
|
5
|
-
function workerResultLine(result) {
|
|
6
|
-
return DIGEST_WORKER_RESULT_PREFIX + JSON.stringify(result);
|
|
7
|
-
}
|
|
8
|
-
describe('parseDigestWorkerSuccess', () => {
|
|
9
|
-
test('accepts a structured provisional digest worker line', () => {
|
|
10
|
-
const stdout = [
|
|
11
|
-
'[memory-kit] model-load: 612ms',
|
|
12
|
-
workerResultLine({
|
|
13
|
-
markdown: '/tmp/conversation-digests/abc-conv.md',
|
|
14
|
-
status: 'provisional',
|
|
15
|
-
contentHash: 'abc',
|
|
16
|
-
indexed: true,
|
|
17
|
-
skipped: false,
|
|
18
|
-
}),
|
|
19
|
-
].join('\n');
|
|
20
|
-
assert.equal(parseDigestWorkerSuccess(stdout), true);
|
|
21
|
-
});
|
|
22
|
-
test('rejects stdout that only mentions markdown as plain text', () => {
|
|
23
|
-
const stdout = 'The model produced markdown but no digest result envelope.';
|
|
24
|
-
assert.equal(parseDigestWorkerSuccess(stdout), false);
|
|
25
|
-
});
|
|
26
|
-
test('rejects unprefixed JSON objects', () => {
|
|
27
|
-
const stdout = [
|
|
28
|
-
JSON.stringify({ markdown: '/tmp/file.md', status: 'draft' }),
|
|
29
|
-
JSON.stringify({ markdown: '/tmp/file.md', status: 'provisional' }),
|
|
30
|
-
].join('\n');
|
|
31
|
-
assert.equal(parseDigestWorkerSuccess(stdout), false);
|
|
32
|
-
});
|
|
33
|
-
test('uses the last valid digest worker line after noisy output', () => {
|
|
34
|
-
const stdout = [
|
|
35
|
-
JSON.stringify({ event: 'model-load', markdown: false }),
|
|
36
|
-
'Metal cleanup warning',
|
|
37
|
-
workerResultLine({ markdown: '/tmp/final.md', status: 'provisional' }),
|
|
38
|
-
'trailing non-json output',
|
|
39
|
-
].join('\n');
|
|
40
|
-
assert.equal(parseDigestWorkerSuccess(stdout), true);
|
|
41
|
-
});
|
|
42
|
-
test('rejects malformed digest worker lines', () => {
|
|
43
|
-
const stdout = ['[memory-kit] model-load: 612ms', DIGEST_WORKER_RESULT_PREFIX + '{"markdown":'].join('\n');
|
|
44
|
-
assert.equal(parseDigestWorkerSuccess(stdout), false);
|
|
45
|
-
});
|
|
46
|
-
});
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import * as assert from 'node:assert/strict';
|
|
2
|
-
import * as os from 'node:os';
|
|
3
|
-
import * as path from 'node:path';
|
|
4
|
-
import { describe, test } from 'node:test';
|
|
5
|
-
import { DEFAULT_MODEL_CACHE_DIR, FASTEMBED_CACHE_DIR } from '../embedder.js';
|
|
6
|
-
import { AGENT_KIT_HOME, CREDENTIALS_FILE } from '../../utils/paths.js';
|
|
7
|
-
describe('Embedder cache paths', () => {
|
|
8
|
-
test('uses global Agent Kit home for user-level files', () => {
|
|
9
|
-
assert.equal(AGENT_KIT_HOME, path.join(os.homedir(), '.agent-kit'));
|
|
10
|
-
assert.equal(CREDENTIALS_FILE, path.join(AGENT_KIT_HOME, 'credentials'));
|
|
11
|
-
assert.equal(DEFAULT_MODEL_CACHE_DIR, path.join(AGENT_KIT_HOME, 'cache', 'models'));
|
|
12
|
-
assert.equal(FASTEMBED_CACHE_DIR, path.join(DEFAULT_MODEL_CACHE_DIR, 'fastembed'));
|
|
13
|
-
});
|
|
14
|
-
});
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { DigestModelSpec } from './types.js';
|
|
2
|
-
export declare class DigestModelRegistryError extends Error {
|
|
3
|
-
constructor(message: string);
|
|
4
|
-
}
|
|
5
|
-
export declare const DIGEST_MODEL_REGISTRY: Record<string, DigestModelSpec>;
|
|
6
|
-
export declare function getDigestModelSpec(modelId: string): DigestModelSpec;
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { DigestFileOptions, DigestPendingResult, ProvisionalDigestResult, ConversationDigestInitResult, InitializeConversationDigestInput } from './types.js';
|
|
2
|
-
export declare function digestConversationFile(options: DigestFileOptions): Promise<ProvisionalDigestResult>;
|
|
3
|
-
export declare function digestPendingConversations({ workspaceRoot, digestFn, }: {
|
|
4
|
-
workspaceRoot: string;
|
|
5
|
-
digestFn?: (opts: DigestFileOptions) => Promise<ProvisionalDigestResult>;
|
|
6
|
-
}): Promise<DigestPendingResult>;
|
|
7
|
-
export declare function initializeConversationDigestModel(input: InitializeConversationDigestInput): Promise<ConversationDigestInitResult>;
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { ConversationDigestProvider } from '../types.js';
|
|
2
|
-
export declare class LlamaLocalDigestProviderError extends Error {
|
|
3
|
-
constructor(message: string);
|
|
4
|
-
}
|
|
5
|
-
export declare function trimConversationExport(content: string, maxInputChars: number): string;
|
|
6
|
-
export declare function sanitizeConversationDigestMarkdown(generatedText: string): string;
|
|
7
|
-
export declare function createLlamaLocalDigestProvider(modelId: string): Promise<ConversationDigestProvider>;
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import * as assert from 'node:assert/strict';
|
|
2
|
-
import { afterEach, describe, test } from 'node:test';
|
|
3
|
-
import { createTempDirTracker } from '../../__tests__/temp-dir.js';
|
|
4
|
-
import { runMemoryCli } from '../../cli/memory.js';
|
|
5
|
-
const tempDirs = createTempDirTracker();
|
|
6
|
-
afterEach(() => {
|
|
7
|
-
tempDirs.cleanup();
|
|
8
|
-
});
|
|
9
|
-
describe('cmdDigestPending CLI', () => {
|
|
10
|
-
test('C22: no --hook flag → writes usage to stderr and returns 1', async () => {
|
|
11
|
-
const stderrChunks = [];
|
|
12
|
-
const origWrite = process.stderr.write.bind(process.stderr);
|
|
13
|
-
process.stderr.write = (chunk) => {
|
|
14
|
-
stderrChunks.push(String(chunk));
|
|
15
|
-
return true;
|
|
16
|
-
};
|
|
17
|
-
let code;
|
|
18
|
-
try {
|
|
19
|
-
code = await runMemoryCli(['digest-pending'], process.env);
|
|
20
|
-
}
|
|
21
|
-
finally {
|
|
22
|
-
process.stderr.write = origWrite;
|
|
23
|
-
}
|
|
24
|
-
assert.equal(code, 1);
|
|
25
|
-
assert.ok(stderrChunks.some((s) => s.includes('Usage')));
|
|
26
|
-
});
|
|
27
|
-
test('C23: --hook returns 0 and emits one JSON line matching DigestPendingResult', async () => {
|
|
28
|
-
const workspace = tempDirs.makeTempDir('cli-pending-c23-');
|
|
29
|
-
const stdoutChunks = [];
|
|
30
|
-
const origWrite = process.stdout.write.bind(process.stdout);
|
|
31
|
-
process.stdout.write = (chunk) => {
|
|
32
|
-
stdoutChunks.push(String(chunk));
|
|
33
|
-
return true;
|
|
34
|
-
};
|
|
35
|
-
let code;
|
|
36
|
-
try {
|
|
37
|
-
code = await runMemoryCli(['digest-pending', '--hook'], {
|
|
38
|
-
...process.env,
|
|
39
|
-
WORKSPACE_DIR: workspace,
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
finally {
|
|
43
|
-
process.stdout.write = origWrite;
|
|
44
|
-
}
|
|
45
|
-
assert.equal(code, 0);
|
|
46
|
-
const output = stdoutChunks.join('');
|
|
47
|
-
const parsed = JSON.parse(output.trim());
|
|
48
|
-
assert.ok('ok' in parsed, 'result must have ok field');
|
|
49
|
-
assert.ok('action' in parsed, 'result must have action field');
|
|
50
|
-
});
|
|
51
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import * as assert from 'node:assert/strict';
|
|
2
|
-
import { afterEach, describe, test } from 'node:test';
|
|
3
|
-
import { createTempDirTracker } from '../../__tests__/temp-dir.js';
|
|
4
|
-
import { runMemoryCli } from '../../cli/memory.js';
|
|
5
|
-
const tempDirs = createTempDirTracker();
|
|
6
|
-
afterEach(() => {
|
|
7
|
-
tempDirs.cleanup();
|
|
8
|
-
});
|
|
9
|
-
describe('memory CLI', () => {
|
|
10
|
-
test('digest-pending --hook returns 0 when settings are missing', async () => {
|
|
11
|
-
const workspace = tempDirs.makeTempDir('digest-pending-');
|
|
12
|
-
const code = await runMemoryCli(['digest-pending', '--hook'], {
|
|
13
|
-
...process.env,
|
|
14
|
-
WORKSPACE_DIR: workspace,
|
|
15
|
-
});
|
|
16
|
-
assert.equal(code, 0);
|
|
17
|
-
});
|
|
18
|
-
test('unknown command returns 1', async () => {
|
|
19
|
-
const code = await runMemoryCli(['unknown'], process.env);
|
|
20
|
-
assert.equal(code, 1);
|
|
21
|
-
});
|
|
22
|
-
});
|
package/dist/tools/core.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Core Tools - Extension info and handoff persistence
|
|
3
|
-
*/
|
|
4
|
-
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
5
|
-
import { DEFAULT_EXTENSIONS } from './config.js';
|
|
6
|
-
/**
|
|
7
|
-
* Register core tools with MCP server
|
|
8
|
-
*/
|
|
9
|
-
export declare function registerCoreTools(server: McpServer): void;
|
|
10
|
-
export { DEFAULT_EXTENSIONS };
|
package/dist/tools/core.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Core Tools - Extension info and handoff persistence
|
|
3
|
-
*/
|
|
4
|
-
import * as fs from 'fs';
|
|
5
|
-
import * as path from 'path';
|
|
6
|
-
import { z } from 'zod';
|
|
7
|
-
import { getWorkspaceRoot, mcpText } from '../utils/utils.js';
|
|
8
|
-
import { DEFAULT_EXTENSIONS } from './config.js';
|
|
9
|
-
import { HANDOFF_TYPES, resolveHandoffPath } from './handoffs.js';
|
|
10
|
-
/**
|
|
11
|
-
* Register core tools with MCP server
|
|
12
|
-
*/
|
|
13
|
-
export function registerCoreTools(server) {
|
|
14
|
-
// ═══════════════════════════════════════════════════════════════
|
|
15
|
-
// TOOL: SAVE HANDOFF
|
|
16
|
-
// Writes handoff artifacts to the workspace
|
|
17
|
-
// Returns the saved file path to use in next-step instructions
|
|
18
|
-
// ═══════════════════════════════════════════════════════════════
|
|
19
|
-
server.tool('kit_save_handoff', `Save a handoff artifact to .agent-kit/handoffs/. Returns the saved file path to use in next-step instructions. Do NOT append version numbers (v2, v3, etc.) to the slug.`, {
|
|
20
|
-
type: z.enum(HANDOFF_TYPES).describe('Handoff type'),
|
|
21
|
-
content: z.string().describe('Full markdown content to save'),
|
|
22
|
-
slug: z
|
|
23
|
-
.string()
|
|
24
|
-
.describe('Short identifier for the filename, e.g. "user-auth" or "PROJ-123". Do NOT append version numbers (v2, v3, etc.).'),
|
|
25
|
-
}, async ({ type, content, slug }) => {
|
|
26
|
-
try {
|
|
27
|
-
const location = resolveHandoffPath({
|
|
28
|
-
workspaceRoot: getWorkspaceRoot(),
|
|
29
|
-
type,
|
|
30
|
-
slug,
|
|
31
|
-
content,
|
|
32
|
-
});
|
|
33
|
-
const handoffDir = path.dirname(location.filePath);
|
|
34
|
-
if (!fs.existsSync(handoffDir)) {
|
|
35
|
-
fs.mkdirSync(handoffDir, { recursive: true });
|
|
36
|
-
}
|
|
37
|
-
fs.writeFileSync(location.filePath, content, 'utf8');
|
|
38
|
-
return mcpText(`✅ Saved to: ${location.filePath}`);
|
|
39
|
-
}
|
|
40
|
-
catch (error) {
|
|
41
|
-
return mcpText(`Error saving handoff: ${error}`);
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
// Export DEFAULT_EXTENSIONS for backward compatibility
|
|
46
|
-
export { DEFAULT_EXTENSIONS };
|
package/dist/tools/handoffs.d.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
export declare const HANDOFF_TYPES: readonly ["brainstorm", "clarification", "plan", "ticket", "research", "scenario", "investigation"];
|
|
2
|
-
export type HandoffType = (typeof HANDOFF_TYPES)[number];
|
|
3
|
-
export type CanonicalHandoffType = 'brainstorm' | 'clarification' | 'plan' | 'ticket' | 'research' | 'scenario' | 'investigation';
|
|
4
|
-
export interface SavedHandoffLocation {
|
|
5
|
-
featureSlug: string;
|
|
6
|
-
canonicalType: CanonicalHandoffType;
|
|
7
|
-
filePath: string;
|
|
8
|
-
relativePath: string;
|
|
9
|
-
}
|
|
10
|
-
export declare function sanitizeFeatureSlug(value: string): string;
|
|
11
|
-
export declare function normalizeHandoffType(type: HandoffType): CanonicalHandoffType;
|
|
12
|
-
export declare function deriveFeatureSlug(input: {
|
|
13
|
-
requestedSlug: string;
|
|
14
|
-
content: string;
|
|
15
|
-
type: CanonicalHandoffType;
|
|
16
|
-
}): string;
|
|
17
|
-
export declare function resolveHandoffPath(input: {
|
|
18
|
-
workspaceRoot: string;
|
|
19
|
-
type: HandoffType;
|
|
20
|
-
slug: string;
|
|
21
|
-
content: string;
|
|
22
|
-
}): SavedHandoffLocation;
|
package/dist/tools/memory.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import { MemoryIndexer } from '../memory/indexer.js';
|
|
3
|
-
import { MemoryStore } from '../memory/store.js';
|
|
4
|
-
import type { MemoryConfig } from '../memory/types.js';
|
|
5
|
-
import { type ProjectSettings } from './config.js';
|
|
6
|
-
/**
|
|
7
|
-
* Registers tool handlers onto an already-constructed indexer/store pair.
|
|
8
|
-
* Exported for unit testing with mocks.
|
|
9
|
-
*/
|
|
10
|
-
export declare function registerMemoryToolHandlers(server: McpServer, indexer: MemoryIndexer, store: MemoryStore, config: MemoryConfig, workspaceRoot?: string): void;
|
|
11
|
-
/**
|
|
12
|
-
* Initializes the memory subsystem and registers all memory tools.
|
|
13
|
-
* Returns the MemoryIndexer so the caller can fire startupIndex() after server.connect().
|
|
14
|
-
* Returns null when memory is disabled (settings.memory.enabled !== true).
|
|
15
|
-
*/
|
|
16
|
-
export declare function registerMemoryTools(server: McpServer, settings: ProjectSettings, workspaceRoot: string): MemoryIndexer | null;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|