@hanhnd/agent-kit 1.0.32 → 1.0.33

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 (107) hide show
  1. package/dist/cli/commands/cli-digest-pending.test.js +111 -0
  2. package/dist/cli/commands/cli-memory.test.js +34 -0
  3. package/dist/cli/{memory.js → commands/memory.js} +24 -11
  4. package/dist/cli/index.js +2 -2
  5. package/dist/{utils → cli/utils}/args.d.ts +2 -1
  6. package/dist/{kit-cli.js → cli.js} +342 -58
  7. package/dist/{tools/config.d.ts → core/config/index.d.ts} +2 -7
  8. package/dist/{tools/config.js → core/config/index.js} +2 -13
  9. package/dist/core/handoffs/index.d.ts +16 -0
  10. package/dist/{tools/handoffs.js → core/handoffs/index.js} +3 -3
  11. package/dist/{tools/security.d.ts → core/security/index.d.ts} +0 -19
  12. package/dist/{tools/security.js → core/security/index.js} +3 -76
  13. package/dist/entrypoints/cli.js +11 -0
  14. package/dist/entrypoints/server.js +37 -0
  15. package/dist/{tools → mcp}/agent.js +1 -1
  16. package/dist/{tools → mcp}/core.d.ts +1 -1
  17. package/dist/{tools → mcp}/core.js +2 -2
  18. package/dist/{tools → mcp}/integration.js +3 -3
  19. package/dist/mcp/memory.d.ts +15 -0
  20. package/dist/{tools → mcp}/memory.js +15 -13
  21. package/dist/{kit-server.js → server.js} +80 -70
  22. package/dist/services/digest/background.d.ts +11 -0
  23. package/dist/services/digest/background.js +258 -0
  24. package/dist/{memory → services}/digest/constants.d.ts +4 -1
  25. package/dist/{memory → services}/digest/constants.js +6 -2
  26. package/dist/services/digest/digest-background.test.js +206 -0
  27. package/dist/{tools/__tests__ → services/digest}/digest-init.test.js +4 -3
  28. package/dist/{memory/__tests__ → services/digest}/digest-llama-local.test.js +1 -13
  29. package/dist/{memory/__tests__ → services/digest}/digest-pending.test.js +39 -11
  30. package/dist/{memory/__tests__ → services/digest}/digest-processor.test.js +5 -7
  31. package/dist/{memory → services}/digest/files.d.ts +0 -1
  32. package/dist/{memory → services}/digest/files.js +2 -2
  33. package/dist/services/digest/model-registry.d.ts +2 -0
  34. package/dist/{memory → services}/digest/model-registry.js +9 -8
  35. package/dist/services/digest/processor.d.ts +13 -0
  36. package/dist/{memory → services}/digest/processor.js +48 -31
  37. package/dist/services/digest/providers/llama-local.d.ts +2 -0
  38. package/dist/{memory → services}/digest/providers/llama-local.js +3 -4
  39. package/dist/{memory → services}/digest/types.d.ts +34 -2
  40. package/dist/services/digest/types.js +6 -0
  41. package/dist/{memory → services}/digest/worker.d.ts +0 -1
  42. package/dist/{memory → services}/digest/worker.js +2 -2
  43. package/dist/{utils → services/integration}/credentials.js +1 -1
  44. package/dist/{utils/parser.js → services/integration/parser/adf.js} +1 -0
  45. package/dist/{memory → services/memory}/chunker.d.ts +0 -2
  46. package/dist/{memory → services/memory}/chunker.js +3 -3
  47. package/dist/{memory/__tests__ → services/memory}/chunker.test.js +1 -34
  48. package/dist/{memory → services/memory}/constants.d.ts +1 -1
  49. package/dist/{memory → services/memory}/constants.js +2 -1
  50. package/dist/{memory → services/memory}/embedder.d.ts +2 -2
  51. package/dist/{memory → services/memory}/embedder.js +5 -5
  52. package/dist/services/memory/embedder.test.js +13 -0
  53. package/dist/{memory → services/memory}/indexer.d.ts +2 -1
  54. package/dist/{memory → services/memory}/indexer.js +1 -1
  55. package/dist/{memory/__tests__ → services/memory}/indexer.test.js +4 -3
  56. package/dist/{tools/__tests__ → services/memory}/memory.test.js +54 -13
  57. package/dist/{memory/__tests__ → services/memory}/store.test.js +3 -2
  58. package/dist/{memory → services/memory}/types.d.ts +7 -2
  59. package/dist/services/memory/types.js +6 -0
  60. package/dist/utils/files.d.ts +0 -1
  61. package/dist/utils/files.js +1 -1
  62. package/dist/{memory/__tests__/lockfile.test.js → utils/files.test.js} +14 -14
  63. package/dist/utils/index.d.ts +6 -0
  64. package/dist/utils/index.js +6 -0
  65. package/dist/utils/json.d.ts +0 -1
  66. package/dist/utils/json.js +0 -10
  67. package/dist/utils/paths.d.ts +0 -1
  68. package/dist/utils/paths.js +1 -1
  69. package/package.json +6 -6
  70. package/dist/memory/__tests__/digest-worker.test.js +0 -46
  71. package/dist/memory/__tests__/embedder.test.js +0 -14
  72. package/dist/memory/digest/model-registry.d.ts +0 -6
  73. package/dist/memory/digest/processor.d.ts +0 -7
  74. package/dist/memory/digest/providers/llama-local.d.ts +0 -7
  75. package/dist/memory/digest/types.js +0 -1
  76. package/dist/memory/types.js +0 -1
  77. package/dist/tools/__tests__/cli-digest-pending.test.js +0 -51
  78. package/dist/tools/__tests__/cli-memory.test.js +0 -22
  79. package/dist/tools/handoffs.d.ts +0 -22
  80. package/dist/tools/memory.d.ts +0 -16
  81. /package/dist/{tools/__tests__ → cli/commands}/cli-digest-pending.test.d.ts +0 -0
  82. /package/dist/{tools/__tests__ → cli/commands}/cli-memory.test.d.ts +0 -0
  83. /package/dist/cli/{memory.d.ts → commands/memory.d.ts} +0 -0
  84. /package/dist/{utils → cli/utils}/args.js +0 -0
  85. /package/dist/{kit-cli.d.ts → entrypoints/cli.d.ts} +0 -0
  86. /package/dist/{kit-server.d.ts → entrypoints/server.d.ts} +0 -0
  87. /package/dist/{tools → mcp}/agent.d.ts +0 -0
  88. /package/dist/{tools → mcp}/integration.d.ts +0 -0
  89. /package/dist/{memory/__tests__/chunker.test.d.ts → services/digest/digest-background.test.d.ts} +0 -0
  90. /package/dist/{tools/__tests__ → services/digest}/digest-init.test.d.ts +0 -0
  91. /package/dist/{memory/__tests__ → services/digest}/digest-llama-local.test.d.ts +0 -0
  92. /package/dist/{memory/__tests__ → services/digest}/digest-pending.test.d.ts +0 -0
  93. /package/dist/{memory/__tests__ → services/digest}/digest-processor.test.d.ts +0 -0
  94. /package/dist/{utils → services/integration}/credentials.d.ts +0 -0
  95. /package/dist/{utils/parser.d.ts → services/integration/parser/adf.d.ts} +0 -0
  96. /package/dist/{memory/__tests__/digest-worker.test.d.ts → services/memory/chunker.test.d.ts} +0 -0
  97. /package/dist/{memory/__tests__ → services/memory}/embedder.test.d.ts +0 -0
  98. /package/dist/{memory → services/memory}/index.d.ts +0 -0
  99. /package/dist/{memory → services/memory}/index.js +0 -0
  100. /package/dist/{memory/__tests__ → services/memory}/indexer.test.d.ts +0 -0
  101. /package/dist/{tools/__tests__ → services/memory}/memory.test.d.ts +0 -0
  102. /package/dist/{memory → services/memory}/store.d.ts +0 -0
  103. /package/dist/{memory → services/memory}/store.js +0 -0
  104. /package/dist/{memory/__tests__ → services/memory}/store.test.d.ts +0 -0
  105. /package/dist/{memory/__tests__/lockfile.test.d.ts → utils/files.test.d.ts} +0 -0
  106. /package/dist/{__tests__/temp-dir.d.ts → utils/temp-dir.test.d.ts} +0 -0
  107. /package/dist/{__tests__/temp-dir.js → utils/temp-dir.test.js} +0 -0
@@ -5,7 +5,7 @@ import { LLAMA_CONTEXT_SIZE, LLAMA_MAX_GENERATED_TOKENS, LLAMA_TEMPERATURE } fro
5
5
  import { getDigestModelSpec } from '../model-registry.js';
6
6
  import { withTimeout } from '../../../utils/async.js';
7
7
  import { MODEL_CACHE_DIR } from '../../../utils/paths.js';
8
- export class LlamaLocalDigestProviderError extends Error {
8
+ class LlamaLocalDigestProviderError extends Error {
9
9
  constructor(message) {
10
10
  super(message);
11
11
  this.name = 'LlamaLocalDigestProviderError';
@@ -17,7 +17,7 @@ function titleFromSource(sourcePath) {
17
17
  .replace(/\.[^.]+$/, '')
18
18
  .replace(/[_-]+/g, ' ');
19
19
  }
20
- export function trimConversationExport(content, maxInputChars) {
20
+ function trimConversationExport(content, maxInputChars) {
21
21
  const trimmed = content.trim();
22
22
  if (trimmed.length <= maxInputChars)
23
23
  return trimmed;
@@ -28,7 +28,6 @@ export function trimConversationExport(content, maxInputChars) {
28
28
  }
29
29
  function buildPrompt(input, maxInputChars) {
30
30
  const conversationExport = trimConversationExport(input.content, maxInputChars);
31
- const sourceName = path.basename(input.sourcePath);
32
31
  return [
33
32
  {
34
33
  role: 'system',
@@ -75,7 +74,7 @@ function stripCodeFence(text) {
75
74
  const match = trimmed.match(/^```(?:markdown|md)?\s*\n([\s\S]*?)\n```$/i);
76
75
  return match ? match[1].trim() : trimmed;
77
76
  }
78
- export function sanitizeConversationDigestMarkdown(generatedText) {
77
+ function sanitizeConversationDigestMarkdown(generatedText) {
79
78
  return stripCodeFence(generatedText).trim() + '\n';
80
79
  }
81
80
  export async function createLlamaLocalDigestProvider(modelId) {
@@ -1,3 +1,8 @@
1
+ export declare enum DigestModelId {
2
+ TINY = "tiny",
3
+ DEFAULT = "default",
4
+ LARGE = "large"
5
+ }
1
6
  export interface ConversationDigestSettings {
2
7
  enabled?: boolean;
3
8
  initialized: boolean;
@@ -5,7 +10,7 @@ export interface ConversationDigestSettings {
5
10
  initializedAt: string;
6
11
  }
7
12
  export interface DigestModelSpec {
8
- id: string;
13
+ id: DigestModelId;
9
14
  ggufUri: string;
10
15
  approxSizeBytes: number;
11
16
  license: string;
@@ -17,7 +22,7 @@ export interface ConversationDigestInput {
17
22
  content: string;
18
23
  contentHash: string;
19
24
  }
20
- export interface ConversationDigestOptions {
25
+ interface ConversationDigestOptions {
21
26
  modelId: string;
22
27
  maxInputChars: number;
23
28
  timeoutMs: number;
@@ -83,3 +88,30 @@ export type DigestPendingResult = {
83
88
  action: 'error';
84
89
  error: string;
85
90
  };
91
+ export type DigestWorkerState = 'running' | 'complete' | 'failed' | 'not-initialized' | 'no-pending' | 'locked' | 'stale';
92
+ export interface DigestWorkerStatus {
93
+ state: DigestWorkerState;
94
+ pid: number | null;
95
+ startedAt: string;
96
+ updatedAt: string;
97
+ pendingAtStart: number;
98
+ processed: number;
99
+ skipped: number;
100
+ errors: number;
101
+ lastError?: string;
102
+ }
103
+ export interface DigestPendingCandidateSummary {
104
+ initialized: boolean;
105
+ pending: number;
106
+ reason?: 'not-initialized' | 'no-pending';
107
+ }
108
+ export interface DigestPendingLauncherResult {
109
+ ok: boolean;
110
+ mode: 'background-launcher';
111
+ status: DigestWorkerStatus;
112
+ spawned: boolean;
113
+ pid?: number;
114
+ reason?: string;
115
+ error?: string;
116
+ }
117
+ export {};
@@ -0,0 +1,6 @@
1
+ export var DigestModelId;
2
+ (function (DigestModelId) {
3
+ DigestModelId["TINY"] = "tiny";
4
+ DigestModelId["DEFAULT"] = "default";
5
+ DigestModelId["LARGE"] = "large";
6
+ })(DigestModelId || (DigestModelId = {}));
@@ -1,3 +1,2 @@
1
- export declare function parseDigestWorkerSuccess(stdout: string): boolean;
2
1
  export declare function isDigestWorkerInvocation(args: string[]): boolean;
3
2
  export declare function runDigestFileInWorker(args: string[]): number | undefined;
@@ -1,5 +1,5 @@
1
1
  import { spawnSync } from 'node:child_process';
2
- import { parseArgs, numberFlag } from '../../utils/args.js';
2
+ import { parseArgs, numberFlag } from '../../cli/utils/args.js';
3
3
  import { DEFAULT_DIGEST_TIMEOUT_MS, DIGEST_TIMEOUT_GRACE_MS, DIGEST_WORKER_FLAG, DIGEST_WORKER_RESULT_PREFIX, } from './constants.js';
4
4
  function isDigestWorkerResult(value) {
5
5
  if (typeof value !== 'object' || value === null || Array.isArray(value))
@@ -7,7 +7,7 @@ function isDigestWorkerResult(value) {
7
7
  const record = value;
8
8
  return typeof record.markdown === 'string' && record.status === 'provisional';
9
9
  }
10
- export function parseDigestWorkerSuccess(stdout) {
10
+ function parseDigestWorkerSuccess(stdout) {
11
11
  const line = stdout
12
12
  .split(/\r?\n/)
13
13
  .reverse()
@@ -14,7 +14,7 @@
14
14
  * Active profile: KIT_PROFILE env var (defaults to "default")
15
15
  */
16
16
  import * as fs from 'fs';
17
- import { CREDENTIALS_FILE } from './paths.js';
17
+ import { CREDENTIALS_FILE } from '../../utils/paths.js';
18
18
  let cachedCredentials = null;
19
19
  /**
20
20
  * Parse a simple INI file into a map of profiles.
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
1
2
  export function adfToMarkdown(adf) {
2
3
  if (!adf || typeof adf !== 'object')
3
4
  return 'No description';
@@ -1,4 +1,2 @@
1
1
  import type { MemoryChunk, MemoryConfig } from './types.js';
2
- export declare function computeChunkId(content: string): string;
3
- export declare function cleanContentForEmbedding(text: string): string;
4
2
  export declare function chunkMarkdown(text: string, source: string, config: Pick<MemoryConfig, 'chunkSize' | 'overlapLines'>): MemoryChunk[];
@@ -1,8 +1,8 @@
1
- import { sha256Hex } from '../utils/hash.js';
2
- export function computeChunkId(content) {
1
+ import { sha256Hex } from '../../utils/hash.js';
2
+ function computeChunkId(content) {
3
3
  return sha256Hex(content).slice(0, 16);
4
4
  }
5
- export function cleanContentForEmbedding(text) {
5
+ function cleanContentForEmbedding(text) {
6
6
  return text.replace(/<!--[\s\S]*?-->/g, '');
7
7
  }
8
8
  const HEADING_RE = /^(#{1,6}) (.+)$/;
@@ -1,6 +1,6 @@
1
1
  import * as assert from 'node:assert/strict';
2
2
  import { describe, test } from 'node:test';
3
- import { chunkMarkdown, cleanContentForEmbedding, computeChunkId } from '../chunker.js';
3
+ import { chunkMarkdown } from './chunker.js';
4
4
  const CFG = { chunkSize: 200, overlapLines: 2 };
5
5
  const SRC = 'test.md';
6
6
  describe('chunkMarkdown', () => {
@@ -70,36 +70,3 @@ describe('chunkMarkdown', () => {
70
70
  assert.ok(chunk.content.includes('Before.'), 'Text before comment must remain');
71
71
  });
72
72
  });
73
- describe('computeChunkId', () => {
74
- test('returns 16-character hex string', () => {
75
- const id = computeChunkId('hello world');
76
- assert.equal(id.length, 16);
77
- assert.match(id, /^[0-9a-f]{16}$/);
78
- });
79
- test('same content always produces same id', () => {
80
- assert.equal(computeChunkId('abc'), computeChunkId('abc'));
81
- });
82
- test('different content produces different id', () => {
83
- assert.notEqual(computeChunkId('abc'), computeChunkId('xyz'));
84
- });
85
- });
86
- describe('cleanContentForEmbedding', () => {
87
- test('strips HTML comment', () => {
88
- assert.equal(cleanContentForEmbedding('hello <!-- comment --> world'), 'hello world');
89
- });
90
- test('strips multiline HTML comment', () => {
91
- const input = 'before <!-- line1\nline2 --> after';
92
- const result = cleanContentForEmbedding(input);
93
- assert.ok(!result.includes('line1'));
94
- assert.ok(result.includes('before'));
95
- assert.ok(result.includes('after'));
96
- });
97
- test('returns original string when no comments present', () => {
98
- const text = 'plain text without comments';
99
- assert.equal(cleanContentForEmbedding(text), text);
100
- });
101
- test('preserves fenced code block content', () => {
102
- const text = '```js\nconst x = 1;\n```';
103
- assert.equal(cleanContentForEmbedding(text), text);
104
- });
105
- });
@@ -1,4 +1,4 @@
1
- import type { MemoryConfig } from './types.js';
1
+ import { type MemoryConfig } from './types.js';
2
2
  export declare const DEFAULT_MEMORY_CONFIG: MemoryConfig;
3
3
  export declare const LOCK_RETRY_MS = 50;
4
4
  export declare const LOCK_TIMEOUT_MS = 500;
@@ -1,10 +1,11 @@
1
+ import { EmbeddingModelName } from './types.js';
1
2
  export const DEFAULT_MEMORY_CONFIG = {
2
3
  enabled: false,
3
4
  wikiDir: '',
4
5
  topK: 5,
5
6
  chunkSize: 1500,
6
7
  overlapLines: 2,
7
- embeddingModel: 'Xenova/bge-small-en-v1.5',
8
+ embeddingModel: EmbeddingModelName.SMALL,
8
9
  vectorDimension: 384,
9
10
  };
10
11
  export const LOCK_RETRY_MS = 50;
@@ -1,4 +1,4 @@
1
- export { FASTEMBED_CACHE_DIR, MODEL_CACHE_DIR as DEFAULT_MODEL_CACHE_DIR } from '../utils/paths.js';
1
+ import { EmbeddingModelName } from './types.js';
2
2
  export declare class EmbedderError extends Error {
3
3
  constructor(message: string, cause?: unknown);
4
4
  }
@@ -6,7 +6,7 @@ export declare class Embedder {
6
6
  private readonly modelName;
7
7
  private pipeline;
8
8
  private _dimension;
9
- constructor(modelName: string);
9
+ constructor(modelName: EmbeddingModelName);
10
10
  embed(texts: string[]): Promise<Float32Array[]>;
11
11
  get dimension(): number | undefined;
12
12
  isReady(): boolean;
@@ -1,5 +1,5 @@
1
- import { FASTEMBED_CACHE_DIR } from '../utils/paths.js';
2
- export { FASTEMBED_CACHE_DIR, MODEL_CACHE_DIR as DEFAULT_MODEL_CACHE_DIR } from '../utils/paths.js';
1
+ import { FASTEMBED_CACHE_DIR } from '../../utils/paths.js';
2
+ import { EmbeddingModelName } from './types.js';
3
3
  export class EmbedderError extends Error {
4
4
  constructor(message, cause) {
5
5
  super(message);
@@ -49,9 +49,9 @@ export class Embedder {
49
49
  try {
50
50
  const { EmbeddingModel, FlagEmbedding } = await import('fastembed');
51
51
  const modelMap = {
52
- 'Xenova/bge-small-en-v1.5': EmbeddingModel.BGESmallENV15,
53
- 'Xenova/bge-base-en-v1.5': EmbeddingModel.BGEBaseENV15,
54
- 'Xenova/all-MiniLM-L6-v2': EmbeddingModel.AllMiniLML6V2,
52
+ [EmbeddingModelName.TINY]: EmbeddingModel.AllMiniLML6V2,
53
+ [EmbeddingModelName.SMALL]: EmbeddingModel.BGESmallENV15,
54
+ [EmbeddingModelName.LARGE]: EmbeddingModel.BGEBaseENV15,
55
55
  };
56
56
  const model = modelMap[this.modelName] ?? EmbeddingModel.BGESmallENV15;
57
57
  const embedder = await FlagEmbedding.init({
@@ -0,0 +1,13 @@
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 { CREDENTIALS_FILE, MODEL_CACHE_DIR, FASTEMBED_CACHE_DIR } from '../../utils/paths.js';
6
+ describe('Embedder cache paths', () => {
7
+ test('uses global Agent Kit home for user-level files', () => {
8
+ const home = path.join(os.homedir(), '.agent-kit');
9
+ assert.equal(CREDENTIALS_FILE, path.join(home, 'credentials'));
10
+ assert.equal(MODEL_CACHE_DIR, path.join(home, 'cache', 'models'));
11
+ assert.equal(FASTEMBED_CACHE_DIR, path.join(MODEL_CACHE_DIR, 'fastembed'));
12
+ });
13
+ });
@@ -1,6 +1,6 @@
1
1
  import type { MemoryStore } from './store.js';
2
2
  import type { IndexStats, MemoryConfig, SearchResult } from './types.js';
3
- export interface TextEmbedder {
3
+ interface TextEmbedder {
4
4
  embed(texts: string[]): Promise<Float32Array[]>;
5
5
  initialize(): Promise<void>;
6
6
  }
@@ -21,3 +21,4 @@ export declare class MemoryIndexer {
21
21
  startupIndex(): Promise<void>;
22
22
  get ready(): boolean;
23
23
  }
24
+ export {};
@@ -2,7 +2,7 @@ import * as fs from 'fs';
2
2
  import * as path from 'path';
3
3
  import { chunkMarkdown } from './chunker.js';
4
4
  import { LOCK_RETRY_MS, LOCK_TIMEOUT_MS, RRF_K } from './constants.js';
5
- import { acquireLock, releaseLock } from '../utils/files.js';
5
+ import { acquireLock, releaseLock } from '../../utils/files.js';
6
6
  export class MemoryIndexer {
7
7
  store;
8
8
  embedder;
@@ -5,8 +5,9 @@ import { syncBuiltinESMExports } from 'node:module';
5
5
  import * as os from 'node:os';
6
6
  import * as path from 'node:path';
7
7
  import { after, before, describe, test } from 'node:test';
8
- import { MemoryIndexer } from '../indexer.js';
9
- import { MemoryStore } from '../store.js';
8
+ import { MemoryIndexer } from './indexer.js';
9
+ import { MemoryStore } from './store.js';
10
+ import { EmbeddingModelName } from './types.js';
10
11
  class StubEmbedder {
11
12
  async embed(texts) {
12
13
  return texts.map(() => new Float32Array(384).fill(0.05));
@@ -22,7 +23,7 @@ function makeConfig(wikiDir) {
22
23
  topK: 5,
23
24
  chunkSize: 1500,
24
25
  overlapLines: 2,
25
- embeddingModel: 'Xenova/bge-small-en-v1.5',
26
+ embeddingModel: EmbeddingModelName.SMALL,
26
27
  vectorDimension: 384,
27
28
  };
28
29
  }
@@ -1,7 +1,8 @@
1
1
  import * as assert from 'node:assert/strict';
2
2
  import { describe, test } from 'node:test';
3
- import { resolveMemoryConfig } from '../config.js';
4
- import { registerMemoryToolHandlers } from '../memory.js';
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: 'Xenova/bge-small-en-v1.5',
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
- registerMemoryToolHandlers(server, indexer, store, BASE_CONFIG);
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
- registerMemoryToolHandlers(server, indexer, store, BASE_CONFIG);
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
- registerMemoryToolHandlers(server, indexer, store, BASE_CONFIG);
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
- registerMemoryToolHandlers(server, indexer, store, BASE_CONFIG);
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
- registerMemoryToolHandlers(server, indexer, store, BASE_CONFIG);
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
- registerMemoryToolHandlers(server, indexer, store, BASE_CONFIG);
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
- registerMemoryToolHandlers(server, indexer, store, BASE_CONFIG);
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
- registerMemoryToolHandlers(server, indexer, store, BASE_CONFIG);
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
- registerMemoryToolHandlers(server, indexer, store, BASE_CONFIG);
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
- registerMemoryToolHandlers(server, indexer, store, BASE_CONFIG);
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 '../store.js';
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: 'Xenova/bge-small-en-v1.5',
14
+ embeddingModel: EmbeddingModelName.SMALL,
14
15
  vectorDimension: 384,
15
16
  };
16
17
  function makeChunk(overrides = {}) {
@@ -1,4 +1,9 @@
1
- import type { ConversationDigestSettings } from './digest/types.js';
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: string;
28
+ embeddingModel: EmbeddingModelName;
24
29
  vectorDimension: number;
25
30
  conversationDigest?: ConversationDigestSettings;
26
31
  }
@@ -0,0 +1,6 @@
1
+ export var EmbeddingModelName;
2
+ (function (EmbeddingModelName) {
3
+ EmbeddingModelName["TINY"] = "tiny";
4
+ EmbeddingModelName["SMALL"] = "small";
5
+ EmbeddingModelName["LARGE"] = "large";
6
+ })(EmbeddingModelName || (EmbeddingModelName = {}));
@@ -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;
@@ -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
- export function tryWriteFileExclusive(filePath, content) {
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 '../../__tests__/temp-dir.js';
6
- import { DIGEST_LOCKFILE_REL_PATH } from '../digest/constants.js';
7
- import { releaseLock, tryAcquireProcessLock } from '../../utils/files.js';
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 = path.join(workspace, '.agent-kit', 'digest-worker.lock');
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 lockDir = path.join(workspace, '.agent-kit');
27
- const lockFile = path.join(lockDir, 'digest-worker.lock');
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 lockDir = path.join(workspace, '.agent-kit');
41
- const lockFile = path.join(lockDir, 'digest-worker.lock');
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 lockDir = path.join(workspace, '.agent-kit');
61
- const lockFile = path.join(lockDir, 'digest-worker.lock');
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 = path.join(workspace, '.agent-kit', 'digest-worker.lock');
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);
@@ -0,0 +1,6 @@
1
+ export * from './async.js';
2
+ export * from './files.js';
3
+ export * from './hash.js';
4
+ export * from './json.js';
5
+ export * from './paths.js';
6
+ export * from './utils.js';
@@ -0,0 +1,6 @@
1
+ export * from './async.js';
2
+ export * from './files.js';
3
+ export * from './hash.js';
4
+ export * from './json.js';
5
+ export * from './paths.js';
6
+ export * from './utils.js';
@@ -1,2 +1 @@
1
1
  export declare function isRecord(value: unknown): value is Record<string, unknown>;
2
- export declare function readJsonObjectFile(filePath: string): Record<string, unknown>;
@@ -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
- }
@@ -1,4 +1,3 @@
1
- export declare const AGENT_KIT_HOME: string;
2
1
  export declare const CREDENTIALS_FILE: string;
3
2
  export declare const MODEL_CACHE_DIR: string;
4
3
  export declare const FASTEMBED_CACHE_DIR: string;