@j0hanz/memory-mcp 1.0.0 → 1.1.0

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 (131) hide show
  1. package/README.md +32 -25
  2. package/dist/completions/index.d.ts.map +1 -1
  3. package/dist/completions/index.js +11 -8
  4. package/dist/completions/index.js.map +1 -1
  5. package/dist/db/index.d.ts.map +1 -1
  6. package/dist/db/index.js +68 -8
  7. package/dist/db/index.js.map +1 -1
  8. package/dist/db/typed.d.ts +9 -3
  9. package/dist/db/typed.d.ts.map +1 -1
  10. package/dist/db/typed.js +33 -4
  11. package/dist/db/typed.js.map +1 -1
  12. package/dist/index.js +13 -8
  13. package/dist/index.js.map +1 -1
  14. package/dist/lib/errors.d.ts +4 -0
  15. package/dist/lib/errors.d.ts.map +1 -1
  16. package/dist/lib/errors.js +12 -4
  17. package/dist/lib/errors.js.map +1 -1
  18. package/dist/lib/hash.d.ts.map +1 -1
  19. package/dist/lib/hash.js +9 -2
  20. package/dist/lib/hash.js.map +1 -1
  21. package/dist/lib/instructions.d.ts +2 -0
  22. package/dist/lib/instructions.d.ts.map +1 -0
  23. package/dist/lib/instructions.js +96 -0
  24. package/dist/lib/instructions.js.map +1 -0
  25. package/dist/lib/mcp-utils.d.ts +6 -0
  26. package/dist/lib/mcp-utils.d.ts.map +1 -0
  27. package/dist/lib/mcp-utils.js +26 -0
  28. package/dist/lib/mcp-utils.js.map +1 -0
  29. package/dist/lib/pagination.d.ts.map +1 -1
  30. package/dist/lib/pagination.js +14 -7
  31. package/dist/lib/pagination.js.map +1 -1
  32. package/dist/lib/search-cursor.d.ts +13 -0
  33. package/dist/lib/search-cursor.d.ts.map +1 -0
  34. package/dist/lib/search-cursor.js +89 -0
  35. package/dist/lib/search-cursor.js.map +1 -0
  36. package/dist/lib/search.d.ts +16 -0
  37. package/dist/lib/search.d.ts.map +1 -1
  38. package/dist/lib/search.js +53 -12
  39. package/dist/lib/search.js.map +1 -1
  40. package/dist/lib/sql.d.ts +17 -0
  41. package/dist/lib/sql.d.ts.map +1 -0
  42. package/dist/lib/sql.js +18 -0
  43. package/dist/lib/sql.js.map +1 -0
  44. package/dist/lib/tool-contracts.d.ts +18 -0
  45. package/dist/lib/tool-contracts.d.ts.map +1 -0
  46. package/dist/lib/tool-contracts.js +120 -0
  47. package/dist/lib/tool-contracts.js.map +1 -0
  48. package/dist/lib/tool-response.d.ts +1 -5
  49. package/dist/lib/tool-response.d.ts.map +1 -1
  50. package/dist/lib/tool-response.js +9 -7
  51. package/dist/lib/tool-response.js.map +1 -1
  52. package/dist/lib/types.d.ts.map +1 -1
  53. package/dist/lib/types.js +10 -9
  54. package/dist/lib/types.js.map +1 -1
  55. package/dist/prompts/index.d.ts.map +1 -1
  56. package/dist/prompts/index.js +22 -36
  57. package/dist/prompts/index.js.map +1 -1
  58. package/dist/resources/index.d.ts.map +1 -1
  59. package/dist/resources/index.js +15 -36
  60. package/dist/resources/index.js.map +1 -1
  61. package/dist/schemas/index.d.ts.map +1 -1
  62. package/dist/schemas/index.js +1 -1
  63. package/dist/schemas/index.js.map +1 -1
  64. package/dist/schemas/inputs.d.ts +5 -5
  65. package/dist/schemas/inputs.d.ts.map +1 -1
  66. package/dist/schemas/inputs.js +21 -14
  67. package/dist/schemas/inputs.js.map +1 -1
  68. package/dist/schemas/outputs.d.ts +94 -148
  69. package/dist/schemas/outputs.d.ts.map +1 -1
  70. package/dist/schemas/outputs.js +59 -66
  71. package/dist/schemas/outputs.js.map +1 -1
  72. package/dist/server.d.ts.map +1 -1
  73. package/dist/server.js +20 -18
  74. package/dist/server.js.map +1 -1
  75. package/dist/tools/create-relationship.d.ts.map +1 -1
  76. package/dist/tools/create-relationship.js +38 -25
  77. package/dist/tools/create-relationship.js.map +1 -1
  78. package/dist/tools/delete-memories.d.ts.map +1 -1
  79. package/dist/tools/delete-memories.js +37 -30
  80. package/dist/tools/delete-memories.js.map +1 -1
  81. package/dist/tools/delete-memory.d.ts.map +1 -1
  82. package/dist/tools/delete-memory.js +24 -22
  83. package/dist/tools/delete-memory.js.map +1 -1
  84. package/dist/tools/delete-relationship.d.ts.map +1 -1
  85. package/dist/tools/delete-relationship.js +28 -20
  86. package/dist/tools/delete-relationship.js.map +1 -1
  87. package/dist/tools/get-memory.d.ts.map +1 -1
  88. package/dist/tools/get-memory.js +26 -16
  89. package/dist/tools/get-memory.js.map +1 -1
  90. package/dist/tools/get-relationships.d.ts.map +1 -1
  91. package/dist/tools/get-relationships.js +71 -40
  92. package/dist/tools/get-relationships.js.map +1 -1
  93. package/dist/tools/index.d.ts +6 -14
  94. package/dist/tools/index.d.ts.map +1 -1
  95. package/dist/tools/index.js +5 -0
  96. package/dist/tools/index.js.map +1 -1
  97. package/dist/tools/memory-stats.d.ts.map +1 -1
  98. package/dist/tools/memory-stats.js +29 -39
  99. package/dist/tools/memory-stats.js.map +1 -1
  100. package/dist/tools/progress.d.ts +34 -0
  101. package/dist/tools/progress.d.ts.map +1 -0
  102. package/dist/tools/progress.js +136 -0
  103. package/dist/tools/progress.js.map +1 -0
  104. package/dist/tools/recall.d.ts.map +1 -1
  105. package/dist/tools/recall.js +157 -105
  106. package/dist/tools/recall.js.map +1 -1
  107. package/dist/tools/result.d.ts +7 -0
  108. package/dist/tools/result.d.ts.map +1 -0
  109. package/dist/tools/result.js +19 -0
  110. package/dist/tools/result.js.map +1 -0
  111. package/dist/tools/retrieve-context.d.ts.map +1 -1
  112. package/dist/tools/retrieve-context.js +127 -40
  113. package/dist/tools/retrieve-context.js.map +1 -1
  114. package/dist/tools/search-memories.d.ts.map +1 -1
  115. package/dist/tools/search-memories.js +37 -47
  116. package/dist/tools/search-memories.js.map +1 -1
  117. package/dist/tools/store-memories.d.ts.map +1 -1
  118. package/dist/tools/store-memories.js +44 -28
  119. package/dist/tools/store-memories.js.map +1 -1
  120. package/dist/tools/store-memory.d.ts.map +1 -1
  121. package/dist/tools/store-memory.js +43 -23
  122. package/dist/tools/store-memory.js.map +1 -1
  123. package/dist/tools/update-memory.d.ts.map +1 -1
  124. package/dist/tools/update-memory.js +63 -29
  125. package/dist/tools/update-memory.js.map +1 -1
  126. package/package.json +1 -3
  127. package/dist/instructions.md +0 -123
  128. package/dist/tools/helpers.d.ts +0 -13
  129. package/dist/tools/helpers.d.ts.map +0 -1
  130. package/dist/tools/helpers.js +0 -49
  131. package/dist/tools/helpers.js.map +0 -1
@@ -0,0 +1,96 @@
1
+ import { getToolContracts } from './tool-contracts.js';
2
+ const SHARED_CONSTRAINTS = [
3
+ 'Idempotence: `store_memory` and `store_memories` return `created: false` if content+tags already exist.',
4
+ 'Atomic Transactions: `store_memories` and `delete_memories` roll back entirely on unexpected errors.',
5
+ 'Hash Changes: `update_memory` changes the hash when content or tags change. Relationships survive via CASCADE.',
6
+ 'FTS Search Limits: Query terms matched individually (all-OR logic). Phrase operators and negation not supported.',
7
+ 'Recall Limits: BFS traversal is bounded by env vars (RECALL_MAX_FRONTIER_SIZE, RECALL_MAX_EDGE_ROWS, RECALL_MAX_VISITED_NODES). Returns `aborted: true` with partial results when limits are hit.',
8
+ ];
9
+ const ERROR_CODES = [
10
+ '| Code | Meaning |',
11
+ '| --- | --- |',
12
+ '| `E_NOT_FOUND` | Hash or relationship does not exist |',
13
+ '| `E_CONFLICT` | `update_memory` target content+tags already maps to an existing hash |',
14
+ '| `E_CANCELLED` | Request was cancelled |',
15
+ '| `E_UNKNOWN` | Unexpected internal error — retry once |',
16
+ ];
17
+ const DATA_MODEL = `
18
+ ### Memory
19
+ - \`hash\` — SHA-256 of \`(content + sorted tags)\`; deterministic; changes when content or tags change
20
+ - \`content\` — Text; 1–100,000 chars
21
+ - \`tags\` — Array; 1–100 tags; each 1–50 chars, no whitespace; minimum 1 required
22
+ - \`memory_type\` — \`general\` | \`fact\` | \`plan\` | \`decision\` | \`reflection\` | \`lesson\` | \`error\` | \`gradient\` (default \`general\`)
23
+ - \`importance\` — Integer 0–10 (default 0; 10 = critical)
24
+ - \`created_at\`, \`updated_at\` — ISO 8601 timestamps
25
+
26
+ ### Relationship
27
+ - Directed edge: \`from_hash -[relation_type]-> to_hash\`
28
+ - \`relation_type\` — Free-form string, 1–50 chars, no whitespace
29
+ - Suggested types: \`related_to\`, \`causes\`, \`depends_on\`, \`parent_of\`, \`child_of\`, \`supersedes\`, \`contradicts\`, \`supports\`, \`references\`
30
+ - Both endpoints must exist before creating a relationship
31
+ - Cascade-deleted when either endpoint memory is deleted
32
+ - Cascade-updated when either endpoint hash changes (ON UPDATE CASCADE)
33
+ `;
34
+ const WORKFLOWS = `
35
+ ### Store and Link
36
+ \`\`\`
37
+ store_memories({ items: [...] }) → { items[].hash, succeeded, failed }
38
+ create_relationship({ from_hash, to_hash, relation_type }) × N
39
+ \`\`\`
40
+
41
+ ### Search and Read
42
+ \`\`\`
43
+ search_memories({ query, limit }) → { memories[], nextCursor }
44
+ # or, for relationship navigation:
45
+ recall({ query, depth: 1 }) → { memories[], graph[] }
46
+ \`\`\`
47
+
48
+ ### Fill Context Window
49
+ \`\`\`
50
+ retrieve_context({ query, token_budget: 4000, strategy: 'relevance' })
51
+ → { memories[], estimated_tokens, truncated }
52
+ \`\`\`
53
+
54
+ ### Update a Memory
55
+ \`\`\`
56
+ update_memory({ hash, content }) → { old_hash, new_hash }
57
+ # Existing relationships auto-update to new_hash via CASCADE
58
+ \`\`\`
59
+
60
+ ### Batch Delete
61
+ \`\`\`
62
+ delete_memories({ hashes: [...] }) → { items[].{ hash, deleted }, succeeded, failed }
63
+ # deleted: false means hash not found — not an error
64
+ \`\`\`
65
+ `;
66
+ let cachedInstructions;
67
+ export function loadInstructions() {
68
+ if (cachedInstructions !== undefined) {
69
+ return cachedInstructions;
70
+ }
71
+ const contracts = getToolContracts();
72
+ const toolRows = contracts.map((c) => {
73
+ return `| \`${c.name}\` | ${c.description.split('.')[0] ?? ''} |`;
74
+ });
75
+ cachedInstructions = [
76
+ '# Memory MCP — Usage Guide',
77
+ '## Tool Routing',
78
+ '| Tool | Purpose |',
79
+ '| --- | --- |',
80
+ ...toolRows,
81
+ '',
82
+ '## Shared Constraints',
83
+ SHARED_CONSTRAINTS.map((c) => `- ${c}`).join('\n'),
84
+ '## Error Codes',
85
+ ERROR_CODES.join('\n'),
86
+ '## Data Model',
87
+ DATA_MODEL,
88
+ '## Common Workflows',
89
+ WORKFLOWS,
90
+ '## Resources',
91
+ '- `internal://instructions` — This document. Read for tool routing, error codes, and workflows.',
92
+ '- `memory://memories/{hash}` — Fetch a single memory by URI with hash auto-completion.',
93
+ ].join('\n\n');
94
+ return cachedInstructions;
95
+ }
96
+ //# sourceMappingURL=instructions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instructions.js","sourceRoot":"","sources":["../../src/lib/instructions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,MAAM,kBAAkB,GAAG;IACzB,yGAAyG;IACzG,sGAAsG;IACtG,gHAAgH;IAChH,kHAAkH;IAClH,mMAAmM;CACpM,CAAC;AAEF,MAAM,WAAW,GAAG;IAClB,oBAAoB;IACpB,eAAe;IACf,yDAAyD;IACzD,yFAAyF;IACzF,2CAA2C;IAC3C,0DAA0D;CAC3D,CAAC;AAEF,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;CAgBlB,CAAC;AAEF,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+BjB,CAAC;AAEF,IAAI,kBAAsC,CAAC;AAE3C,MAAM,UAAU,gBAAgB;IAC9B,IAAI,kBAAkB,KAAK,SAAS,EAAE,CAAC;QACrC,OAAO,kBAAkB,CAAC;IAC5B,CAAC;IAED,MAAM,SAAS,GAAG,gBAAgB,EAAE,CAAC;IACrC,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACnC,OAAO,OAAO,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC;IACpE,CAAC,CAAC,CAAC;IAEH,kBAAkB,GAAG;QACnB,4BAA4B;QAC5B,iBAAiB;QACjB,oBAAoB;QACpB,eAAe;QACf,GAAG,QAAQ;QACX,EAAE;QACF,uBAAuB;QACvB,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QAClD,gBAAgB;QAChB,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC;QACtB,eAAe;QACf,UAAU;QACV,qBAAqB;QACrB,SAAS;QACT,cAAc;QACd,iGAAiG;QACjG,wFAAwF;KACzF,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAEf,OAAO,kBAAkB,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ type LogLevel = 'debug' | 'info' | 'notice' | 'warning' | 'error';
3
+ export declare function logToolEvent(server: McpServer, logger: string, data: unknown, level?: LogLevel): Promise<void>;
4
+ export declare function notifyMemoryResourceUpdated(server: McpServer, hash: string): Promise<void>;
5
+ export {};
6
+ //# sourceMappingURL=mcp-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-utils.d.ts","sourceRoot":"","sources":["../../src/lib/mcp-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,KAAK,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;AAElE,wBAAsB,YAAY,CAChC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,EACb,KAAK,GAAE,QAAiB,GACvB,OAAO,CAAC,IAAI,CAAC,CAUf;AAID,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAYf"}
@@ -0,0 +1,26 @@
1
+ export async function logToolEvent(server, logger, data, level = 'info') {
2
+ if (!server.isConnected()) {
3
+ return;
4
+ }
5
+ try {
6
+ await server.server.sendLoggingMessage({ level, logger, data });
7
+ }
8
+ catch {
9
+ // best-effort logging
10
+ }
11
+ }
12
+ const MEMORY_RESOURCE_URI_PREFIX = 'memory://memories/';
13
+ export async function notifyMemoryResourceUpdated(server, hash) {
14
+ if (!server.isConnected()) {
15
+ return;
16
+ }
17
+ try {
18
+ await server.server.sendResourceUpdated({
19
+ uri: `${MEMORY_RESOURCE_URI_PREFIX}${hash}`,
20
+ });
21
+ }
22
+ catch {
23
+ // best-effort notification
24
+ }
25
+ }
26
+ //# sourceMappingURL=mcp-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-utils.js","sourceRoot":"","sources":["../../src/lib/mcp-utils.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAAiB,EACjB,MAAc,EACd,IAAa,EACb,QAAkB,MAAM;IAExB,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC;QAC1B,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,sBAAsB;IACxB,CAAC;AACH,CAAC;AAED,MAAM,0BAA0B,GAAG,oBAAoB,CAAC;AAExD,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,MAAiB,EACjB,IAAY;IAEZ,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC;QAC1B,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAAC;YACtC,GAAG,EAAE,GAAG,0BAA0B,GAAG,IAAI,EAAE;SAC5C,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,2BAA2B;IAC7B,CAAC;AACH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../../src/lib/pagination.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,OAAO,EAAE,OAAO,CAAC;CAClB;AAwBD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CASnD;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,CAM5E"}
1
+ {"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../../src/lib/pagination.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,OAAO,EAAE,OAAO,CAAC;CAClB;AAiCD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CASnD;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,CAM5E"}
@@ -1,21 +1,28 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
2
  import { E_INVALID_CURSOR } from './errors.js';
3
3
  const CURSOR_ENCODING = 'base64url';
4
+ const INVALID_CURSOR_STRUCTURE_MESSAGE = 'Invalid cursor structure';
5
+ function isRecord(value) {
6
+ return typeof value === 'object' && value !== null;
7
+ }
8
+ function isNonNegativeInteger(value) {
9
+ return (typeof value === 'number' &&
10
+ Number.isInteger(value) &&
11
+ Number.isFinite(value) &&
12
+ value >= 0);
13
+ }
4
14
  function isCursorPayload(value) {
5
- if (typeof value !== 'object' || value === null) {
15
+ if (!isRecord(value)) {
6
16
  return false;
7
17
  }
8
18
  const { offset } = value;
9
- return (typeof offset === 'number' &&
10
- Number.isInteger(offset) &&
11
- Number.isFinite(offset) &&
12
- offset >= 0);
19
+ return isNonNegativeInteger(offset);
13
20
  }
14
21
  function parseCursorPayload(cursor) {
15
22
  const json = Buffer.from(cursor, CURSOR_ENCODING).toString();
16
23
  const parsed = JSON.parse(json);
17
24
  if (!isCursorPayload(parsed)) {
18
- throw new Error('Invalid cursor structure');
25
+ throw new Error(INVALID_CURSOR_STRUCTURE_MESSAGE);
19
26
  }
20
27
  return parsed;
21
28
  }
@@ -35,6 +42,6 @@ export function splitPage(rows, limit) {
35
42
  if (rows.length > limit) {
36
43
  return { page: rows.slice(0, limit), hasMore: true };
37
44
  }
38
- return { page: [...rows], hasMore: false };
45
+ return { page: rows.slice(), hasMore: false };
39
46
  }
40
47
  //# sourceMappingURL=pagination.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pagination.js","sourceRoot":"","sources":["../../src/lib/pagination.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AAEzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,MAAM,eAAe,GAAG,WAAW,CAAC;AAWpC,SAAS,eAAe,CAAC,KAAc;IACrC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,EAAE,MAAM,EAAE,GAAG,KAAgC,CAAC;IACpD,OAAO,CACL,OAAO,MAAM,KAAK,QAAQ;QAC1B,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;QACxB,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;QACvB,MAAM,IAAI,CAAC,CACZ,CAAC;AACJ,CAAC;AAED,SAAS,kBAAkB,CAAC,MAAc;IACxC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC7D,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACzC,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,MAAM,OAAO,GAAkB,EAAE,MAAM,EAAE,CAAC;IAC1C,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,IAAI,CAAC;QACH,OAAO,kBAAkB,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,QAAQ,CAChB,SAAS,CAAC,aAAa,EACvB,GAAG,gBAAgB,oBAAoB,CACxC,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS,CAAI,IAAkB,EAAE,KAAa;IAC5D,IAAI,IAAI,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC;QACxB,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACvD,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAC7C,CAAC"}
1
+ {"version":3,"file":"pagination.js","sourceRoot":"","sources":["../../src/lib/pagination.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AAEzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,MAAM,eAAe,GAAG,WAAW,CAAC;AAUpC,MAAM,gCAAgC,GAAG,0BAA0B,CAAC;AAEpE,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AACrD,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAc;IAC1C,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC;QACvB,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QACtB,KAAK,IAAI,CAAC,CACX,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CAAC,KAAc;IACrC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IACzB,OAAO,oBAAoB,CAAC,MAAM,CAAC,CAAC;AACtC,CAAC;AAED,SAAS,kBAAkB,CAAC,MAAc;IACxC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC7D,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACzC,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,MAAM,OAAO,GAAkB,EAAE,MAAM,EAAE,CAAC;IAC1C,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,IAAI,CAAC;QACH,OAAO,kBAAkB,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,QAAQ,CAChB,SAAS,CAAC,aAAa,EACvB,GAAG,gBAAgB,oBAAoB,CACxC,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS,CAAI,IAAkB,EAAE,KAAa;IAC5D,IAAI,IAAI,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC;QACxB,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACvD,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAChD,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { MemoryFilters } from './search.js';
2
+ export type DecodedSearchCursor = {
3
+ mode: 'keyset';
4
+ rank: number;
5
+ hash: string;
6
+ } | {
7
+ mode: 'offset';
8
+ offset: number;
9
+ };
10
+ export declare function buildSearchCursorScope(query: string, filters: MemoryFilters): string;
11
+ export declare function encodeSearchCursor(scope: string, rank: number, hash: string): string;
12
+ export declare function decodeSearchCursor(cursor: string, expectedScope: string): DecodedSearchCursor;
13
+ //# sourceMappingURL=search-cursor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-cursor.d.ts","sourceRoot":"","sources":["../../src/lib/search-cursor.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAmBjD,MAAM,MAAM,mBAAmB,GAC3B;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,GACD;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AA+DN,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,aAAa,GACrB,MAAM,CAKR;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,MAAM,CASR;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,GACpB,mBAAmB,CAuBrB"}
@@ -0,0 +1,89 @@
1
+ import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
+ import { createHash } from 'node:crypto';
3
+ import { E_INVALID_CURSOR } from './errors.js';
4
+ const CURSOR_ENCODING = 'base64url';
5
+ const CURSOR_VERSION = 2;
6
+ const CURSOR_KIND = 'fts-keyset';
7
+ const CURSOR_SCOPE_HASH_LENGTH = 24;
8
+ function invalidCursor(reason) {
9
+ return new McpError(ErrorCode.InvalidParams, `${E_INVALID_CURSOR}: ${reason}`);
10
+ }
11
+ function isRecord(value) {
12
+ return typeof value === 'object' && value !== null;
13
+ }
14
+ function isKeysetCursorPayload(value) {
15
+ if (!isRecord(value)) {
16
+ return false;
17
+ }
18
+ return (value['v'] === CURSOR_VERSION &&
19
+ value['kind'] === CURSOR_KIND &&
20
+ typeof value['scope'] === 'string' &&
21
+ typeof value['rank'] === 'number' &&
22
+ Number.isFinite(value['rank']) &&
23
+ typeof value['hash'] === 'string' &&
24
+ /^[a-f0-9]{64}$/.test(value['hash']));
25
+ }
26
+ function isLegacyOffsetCursorPayload(value) {
27
+ if (!isRecord(value)) {
28
+ return false;
29
+ }
30
+ const { offset } = value;
31
+ return (typeof offset === 'number' &&
32
+ Number.isInteger(offset) &&
33
+ Number.isFinite(offset) &&
34
+ offset >= 0);
35
+ }
36
+ function parseCursorPayload(cursor) {
37
+ try {
38
+ const json = Buffer.from(cursor, CURSOR_ENCODING).toString();
39
+ return JSON.parse(json);
40
+ }
41
+ catch {
42
+ throw invalidCursor('malformed cursor');
43
+ }
44
+ }
45
+ function toScopeInput(query, filters) {
46
+ return JSON.stringify({
47
+ query,
48
+ min_importance: filters.min_importance ?? null,
49
+ max_importance: filters.max_importance ?? null,
50
+ memory_type: filters.memory_type ?? null,
51
+ });
52
+ }
53
+ export function buildSearchCursorScope(query, filters) {
54
+ return createHash('sha256')
55
+ .update(toScopeInput(query, filters))
56
+ .digest('hex')
57
+ .slice(0, CURSOR_SCOPE_HASH_LENGTH);
58
+ }
59
+ export function encodeSearchCursor(scope, rank, hash) {
60
+ const payload = {
61
+ v: CURSOR_VERSION,
62
+ kind: CURSOR_KIND,
63
+ scope,
64
+ rank,
65
+ hash,
66
+ };
67
+ return Buffer.from(JSON.stringify(payload)).toString(CURSOR_ENCODING);
68
+ }
69
+ export function decodeSearchCursor(cursor, expectedScope) {
70
+ const payload = parseCursorPayload(cursor);
71
+ if (isKeysetCursorPayload(payload)) {
72
+ if (payload.scope !== expectedScope) {
73
+ throw invalidCursor('cursor does not match current query or filters');
74
+ }
75
+ return {
76
+ mode: 'keyset',
77
+ rank: payload.rank,
78
+ hash: payload.hash,
79
+ };
80
+ }
81
+ if (isLegacyOffsetCursorPayload(payload)) {
82
+ return {
83
+ mode: 'offset',
84
+ offset: payload.offset,
85
+ };
86
+ }
87
+ throw invalidCursor('malformed cursor');
88
+ }
89
+ //# sourceMappingURL=search-cursor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-cursor.js","sourceRoot":"","sources":["../../src/lib/search-cursor.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,6 @@
1
1
  import type { SQLInputValue } from 'node:sqlite';
2
+ import type { TypedDb } from '../db/typed.js';
3
+ import type { MemoryRow } from './types.js';
2
4
  export declare function sanitizeFtsQuery(query: string): string;
3
5
  export interface MemoryFilters {
4
6
  min_importance?: number;
@@ -9,6 +11,20 @@ export interface FilterClauses {
9
11
  clauses: string[];
10
12
  params: SQLInputValue[];
11
13
  }
14
+ export type SearchCursorState = {
15
+ mode: 'offset';
16
+ offset: number;
17
+ } | {
18
+ mode: 'keyset';
19
+ rank: number;
20
+ hash: string;
21
+ };
12
22
  export declare function buildFilterClauses(filters: MemoryFilters): FilterClauses;
13
23
  export declare function buildAndWhereClause(clauses: readonly string[]): string;
24
+ export declare function loadRankedSearchRows(db: TypedDb, query: string, limit: number, cursor: SearchCursorState | undefined, filters: MemoryFilters): MemoryRow[];
25
+ export declare function toMemoryFilters(params: {
26
+ min_importance?: number | undefined;
27
+ max_importance?: number | undefined;
28
+ memory_type?: string | undefined;
29
+ }): MemoryFilters;
14
30
  //# sourceMappingURL=search.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/lib/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AASjD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOtD;AAED,MAAM,WAAW,aAAa;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,aAAa,GAAG,aAAa,CAgBxE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAMtE"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/lib/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAW5C,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOtD;AAED,MAAM,WAAW,aAAa;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB;AAED,MAAM,MAAM,iBAAiB,GACzB;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAWN,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,aAAa,GAAG,aAAa,CAaxE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAMtE;AAuBD,wBAAgB,oBAAoB,CAClC,EAAE,EAAE,OAAO,EACX,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,iBAAiB,GAAG,SAAS,EACrC,OAAO,EAAE,aAAa,GACrB,SAAS,EAAE,CAoBb;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE;IACtC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC,GAAG,aAAa,CAUhB"}
@@ -1,5 +1,7 @@
1
1
  const FTS_SAFE_TOKEN_REGEX = /[A-Za-z0-9_]+/g;
2
2
  const FTS_EMPTY_QUERY_FALLBACK = '"__mcp_no_results__"';
3
+ const BASE_RANKED_SEARCH_SQL = `SELECT m.*, memories_fts.rank AS rank FROM memories m
4
+ JOIN memories_fts ON memories_fts.rowid = m.rowid`;
3
5
  function tokenizeQuery(query) {
4
6
  return query.match(FTS_SAFE_TOKEN_REGEX) ?? [];
5
7
  }
@@ -10,20 +12,20 @@ export function sanitizeFtsQuery(query) {
10
12
  }
11
13
  return tokens.map((token) => `"${token}"`).join(' ');
12
14
  }
15
+ const FILTER_RULES = [
16
+ { key: 'min_importance', clause: 'm.importance >= ?' },
17
+ { key: 'max_importance', clause: 'm.importance <= ?' },
18
+ { key: 'memory_type', clause: 'm.memory_type = ?' },
19
+ ];
13
20
  export function buildFilterClauses(filters) {
14
21
  const clauses = [];
15
22
  const params = [];
16
- if (filters.min_importance != null) {
17
- clauses.push('m.importance >= ?');
18
- params.push(filters.min_importance);
19
- }
20
- if (filters.max_importance != null) {
21
- clauses.push('m.importance <= ?');
22
- params.push(filters.max_importance);
23
- }
24
- if (filters.memory_type != null) {
25
- clauses.push('m.memory_type = ?');
26
- params.push(filters.memory_type);
23
+ for (const rule of FILTER_RULES) {
24
+ const value = filters[rule.key];
25
+ if (value != null) {
26
+ clauses.push(rule.clause);
27
+ params.push(value);
28
+ }
27
29
  }
28
30
  return { clauses, params };
29
31
  }
@@ -31,6 +33,45 @@ export function buildAndWhereClause(clauses) {
31
33
  if (clauses.length === 0) {
32
34
  return '';
33
35
  }
34
- return ` ${clauses.map((clause) => `AND ${clause}`).join(' ')}`;
36
+ return clauses.map((clause) => ` AND ${clause}`).join('');
37
+ }
38
+ function buildRankedSearchSql(whereExtra, cursor) {
39
+ if (!cursor || cursor.mode === 'offset') {
40
+ return `${BASE_RANKED_SEARCH_SQL}
41
+ WHERE memories_fts MATCH ?${whereExtra}
42
+ ORDER BY memories_fts.rank, m.hash
43
+ LIMIT ? OFFSET ?`;
44
+ }
45
+ return `${BASE_RANKED_SEARCH_SQL}
46
+ WHERE memories_fts MATCH ?${whereExtra}
47
+ AND (
48
+ memories_fts.rank > ?
49
+ OR (memories_fts.rank = ? AND m.hash > ?)
50
+ )
51
+ ORDER BY memories_fts.rank, m.hash
52
+ LIMIT ?`;
53
+ }
54
+ export function loadRankedSearchRows(db, query, limit, cursor, filters) {
55
+ const ftsQuery = sanitizeFtsQuery(query);
56
+ const filter = buildFilterClauses(filters);
57
+ const whereExtra = buildAndWhereClause(filter.clauses);
58
+ const sql = buildRankedSearchSql(whereExtra, cursor);
59
+ const stmt = db.prepareOnce(sql);
60
+ if (!cursor || cursor.mode === 'offset') {
61
+ const offset = cursor?.offset ?? 0;
62
+ return stmt.all(ftsQuery, ...filter.params, limit + 1, offset);
63
+ }
64
+ return stmt.all(ftsQuery, ...filter.params, cursor.rank, cursor.rank, cursor.hash, limit + 1);
65
+ }
66
+ export function toMemoryFilters(params) {
67
+ return {
68
+ ...(params.min_importance != null
69
+ ? { min_importance: params.min_importance }
70
+ : {}),
71
+ ...(params.max_importance != null
72
+ ? { max_importance: params.max_importance }
73
+ : {}),
74
+ ...(params.memory_type != null ? { memory_type: params.memory_type } : {}),
75
+ };
35
76
  }
36
77
  //# sourceMappingURL=search.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/lib/search.ts"],"names":[],"mappings":"AAEA,MAAM,oBAAoB,GAAG,gBAAgB,CAAC;AAC9C,MAAM,wBAAwB,GAAG,sBAAsB,CAAC;AAExD,SAAS,aAAa,CAAC,KAAa;IAClC,OAAO,KAAK,CAAC,KAAK,CAAC,oBAAoB,CAAC,IAAI,EAAE,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IACpC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,wBAAwB,CAAC;IAClC,CAAC;IAED,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACvD,CAAC;AAaD,MAAM,UAAU,kBAAkB,CAAC,OAAsB;IACvD,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,MAAM,GAAoB,EAAE,CAAC;IACnC,IAAI,OAAO,CAAC,cAAc,IAAI,IAAI,EAAE,CAAC;QACnC,OAAO,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAClC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,OAAO,CAAC,cAAc,IAAI,IAAI,EAAE,CAAC;QACnC,OAAO,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAClC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,OAAO,CAAC,WAAW,IAAI,IAAI,EAAE,CAAC;QAChC,OAAO,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAClC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,OAA0B;IAC5D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;AAClE,CAAC"}
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/lib/search.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /** Shared SQL statement constants. Centralised here to ensure all callers
2
+ * cache the same string key via `prepareOnce`. */
3
+ /** Insert or silently ignore a duplicate memory (content+tags hash is PK). */
4
+ export declare const INSERT_MEMORY_SQL = "INSERT OR IGNORE INTO memories (hash, content, tags, memory_type, importance, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?)";
5
+ /** Delete one memory row by hash. Relationships cascade via FK. */
6
+ export declare const DELETE_MEMORY_SQL = "DELETE FROM memories WHERE hash = ?";
7
+ /** Fetch a full memory row by hash. */
8
+ export declare const SELECT_MEMORY_BY_HASH_SQL = "SELECT * FROM memories WHERE hash = ?";
9
+ /** Check existence: returns only the hash column to minimise data transfer. */
10
+ export declare const SELECT_MEMORY_HASH_SQL = "SELECT hash FROM memories WHERE hash = ?";
11
+ /** Aggregate memory store stats in a single scan. */
12
+ export declare const MEMORY_AGGREGATE_SQL = "SELECT COUNT(*) AS total, MIN(created_at) AS oldest, MAX(created_at) AS newest, AVG(importance) AS avg_importance FROM memories";
13
+ /** Total relationship count. */
14
+ export declare const RELATIONSHIP_COUNT_SQL = "SELECT COUNT(*) AS total FROM relationships";
15
+ /** Per-type memory breakdown. */
16
+ export declare const TYPE_COUNTS_SQL = "SELECT memory_type, COUNT(*) AS count FROM memories GROUP BY memory_type ORDER BY count DESC";
17
+ //# sourceMappingURL=sql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../../src/lib/sql.ts"],"names":[],"mappings":"AAAA;mDACmD;AAEnD,8EAA8E;AAC9E,eAAO,MAAM,iBAAiB,0IACC,CAAC;AAEhC,mEAAmE;AACnE,eAAO,MAAM,iBAAiB,wCAAwC,CAAC;AAEvE,uCAAuC;AACvC,eAAO,MAAM,yBAAyB,0CACG,CAAC;AAE1C,+EAA+E;AAC/E,eAAO,MAAM,sBAAsB,6CACS,CAAC;AAE7C,qDAAqD;AACrD,eAAO,MAAM,oBAAoB,oIACkG,CAAC;AAEpI,gCAAgC;AAChC,eAAO,MAAM,sBAAsB,gDACY,CAAC;AAEhD,iCAAiC;AACjC,eAAO,MAAM,eAAe,iGACoE,CAAC"}
@@ -0,0 +1,18 @@
1
+ /** Shared SQL statement constants. Centralised here to ensure all callers
2
+ * cache the same string key via `prepareOnce`. */
3
+ /** Insert or silently ignore a duplicate memory (content+tags hash is PK). */
4
+ export const INSERT_MEMORY_SQL = `INSERT OR IGNORE INTO memories (hash, content, tags, memory_type, importance, created_at, updated_at)
5
+ VALUES (?, ?, ?, ?, ?, ?, ?)`;
6
+ /** Delete one memory row by hash. Relationships cascade via FK. */
7
+ export const DELETE_MEMORY_SQL = 'DELETE FROM memories WHERE hash = ?';
8
+ /** Fetch a full memory row by hash. */
9
+ export const SELECT_MEMORY_BY_HASH_SQL = 'SELECT * FROM memories WHERE hash = ?';
10
+ /** Check existence: returns only the hash column to minimise data transfer. */
11
+ export const SELECT_MEMORY_HASH_SQL = 'SELECT hash FROM memories WHERE hash = ?';
12
+ /** Aggregate memory store stats in a single scan. */
13
+ export const MEMORY_AGGREGATE_SQL = 'SELECT COUNT(*) AS total, MIN(created_at) AS oldest, MAX(created_at) AS newest, AVG(importance) AS avg_importance FROM memories';
14
+ /** Total relationship count. */
15
+ export const RELATIONSHIP_COUNT_SQL = 'SELECT COUNT(*) AS total FROM relationships';
16
+ /** Per-type memory breakdown. */
17
+ export const TYPE_COUNTS_SQL = 'SELECT memory_type, COUNT(*) AS count FROM memories GROUP BY memory_type ORDER BY count DESC';
18
+ //# sourceMappingURL=sql.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql.js","sourceRoot":"","sources":["../../src/lib/sql.ts"],"names":[],"mappings":"AAAA;mDACmD;AAEnD,8EAA8E;AAC9E,MAAM,CAAC,MAAM,iBAAiB,GAAG;+BACF,CAAC;AAEhC,mEAAmE;AACnE,MAAM,CAAC,MAAM,iBAAiB,GAAG,qCAAqC,CAAC;AAEvE,uCAAuC;AACvC,MAAM,CAAC,MAAM,yBAAyB,GACpC,uCAAuC,CAAC;AAE1C,+EAA+E;AAC/E,MAAM,CAAC,MAAM,sBAAsB,GACjC,0CAA0C,CAAC;AAE7C,qDAAqD;AACrD,MAAM,CAAC,MAAM,oBAAoB,GAC/B,iIAAiI,CAAC;AAEpI,gCAAgC;AAChC,MAAM,CAAC,MAAM,sBAAsB,GACjC,6CAA6C,CAAC;AAEhD,iCAAiC;AACjC,MAAM,CAAC,MAAM,eAAe,GAC1B,8FAA8F,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { type z } from 'zod/v4';
2
+ export interface ToolContract {
3
+ name: string;
4
+ title: string;
5
+ description: string;
6
+ inputSchema: z.ZodType;
7
+ outputSchema: z.ZodType;
8
+ annotations: {
9
+ readOnlyHint?: boolean;
10
+ idempotentHint?: boolean;
11
+ destructiveHint?: boolean;
12
+ openWorldHint?: boolean;
13
+ };
14
+ }
15
+ export declare const TOOL_CONTRACTS: ToolContract[];
16
+ export declare function getToolContracts(): ToolContract[];
17
+ export declare function getToolContract(name: string): ToolContract;
18
+ //# sourceMappingURL=tool-contracts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-contracts.d.ts","sourceRoot":"","sources":["../../src/lib/tool-contracts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,QAAQ,CAAC;AAgChC,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC;IACvB,YAAY,EAAE,CAAC,CAAC,OAAO,CAAC;IACxB,WAAW,EAAE;QACX,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,eAAe,CAAC,EAAE,OAAO,CAAC;QAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;KACzB,CAAC;CACH;AAED,eAAO,MAAM,cAAc,EAAE,YAAY,EAsHxC,CAAC;AAEF,wBAAgB,gBAAgB,IAAI,YAAY,EAAE,CAEjD;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAM1D"}