agentdb 3.2.0 → 3.3.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 (30) hide show
  1. package/dist/src/controllers/ReasoningBank.d.ts +36 -0
  2. package/dist/src/controllers/ReasoningBank.d.ts.map +1 -1
  3. package/dist/src/controllers/ReasoningBank.js +123 -1
  4. package/dist/src/controllers/ReasoningBank.js.map +1 -1
  5. package/dist/src/controllers/ReflexionMemory.d.ts +55 -0
  6. package/dist/src/controllers/ReflexionMemory.d.ts.map +1 -1
  7. package/dist/src/controllers/ReflexionMemory.js +209 -5
  8. package/dist/src/controllers/ReflexionMemory.js.map +1 -1
  9. package/dist/src/controllers/SkillLibrary.d.ts +50 -0
  10. package/dist/src/controllers/SkillLibrary.d.ts.map +1 -1
  11. package/dist/src/controllers/SkillLibrary.js +220 -9
  12. package/dist/src/controllers/SkillLibrary.js.map +1 -1
  13. package/dist/src/controllers/crud-helpers.d.ts +45 -0
  14. package/dist/src/controllers/crud-helpers.d.ts.map +1 -0
  15. package/dist/src/controllers/crud-helpers.js +130 -0
  16. package/dist/src/controllers/crud-helpers.js.map +1 -0
  17. package/dist/src/controllers/episode-hybrid.d.ts +19 -0
  18. package/dist/src/controllers/episode-hybrid.d.ts.map +1 -1
  19. package/dist/src/controllers/episode-hybrid.js +23 -0
  20. package/dist/src/controllers/episode-hybrid.js.map +1 -1
  21. package/dist/src/mcp/agentdb-mcp-server.js +367 -45
  22. package/dist/src/mcp/agentdb-mcp-server.js.map +1 -1
  23. package/dist/src/search/conversation-retrieval.d.ts +5 -0
  24. package/dist/src/search/conversation-retrieval.d.ts.map +1 -1
  25. package/dist/src/search/conversation-retrieval.js +29 -0
  26. package/dist/src/search/conversation-retrieval.js.map +1 -1
  27. package/dist/src/security/mcp-policy.d.ts.map +1 -1
  28. package/dist/src/security/mcp-policy.js +13 -0
  29. package/dist/src/security/mcp-policy.js.map +1 -1
  30. package/package.json +5 -3
@@ -60,6 +60,29 @@ export class EpisodeLexicalIndex {
60
60
  this.count = count;
61
61
  this.maxId = maxId;
62
62
  }
63
+ /**
64
+ * Apply an in-place edit of one episode without a rebuild. `before` must be the row as it was indexed.
65
+ * A no-op when the index is not built yet (the next search builds it from current rows) or the episode
66
+ * is outside the indexed window.
67
+ */
68
+ update(before, after) {
69
+ if (!this.index)
70
+ return;
71
+ const doc = this.ids.indexOf(before.id);
72
+ if (doc < 0)
73
+ return;
74
+ this.index.replace(doc, EpisodeLexicalIndex.textOf(before), EpisodeLexicalIndex.textOf(after));
75
+ }
76
+ /**
77
+ * Force a full rebuild on the next search. Needed after an in-place edit: an UPDATE changes
78
+ * neither COUNT(*) nor MAX(id), so refresh() alone would keep serving the old text.
79
+ */
80
+ invalidate() {
81
+ this.index = null;
82
+ this.ids = [];
83
+ this.count = -1;
84
+ this.maxId = 0;
85
+ }
63
86
  /** Episode ids with a BM25 match, best first, at most `limit`. */
64
87
  search(query, limit) {
65
88
  this.refresh();
@@ -1 +1 @@
1
- {"version":3,"file":"episode-hybrid.js","sourceRoot":"","sources":["../../../src/controllers/episode-hybrid.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"episode-hybrid.js","sourceRoot":"","sources":["../../../src/controllers/episode-hybrid.ts"],"names":[],"mappings":"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"}
@@ -22,7 +22,8 @@ import { EmbeddingService } from '../controllers/EmbeddingService.js';
22
22
  import { BatchOperations } from '../optimizations/BatchOperations.js';
23
23
  import { ReasoningBank } from '../controllers/ReasoningBank.js';
24
24
  import { MCPToolCaches } from '../optimizations/ToolCache.js';
25
- import { validateId, validateTimestamp, validateSessionId, validateTaskString, validateNumericRange, validateArrayLength, validateBoolean, validateEnum, ValidationError, handleSecurityError, } from '../security/input-validation.js';
25
+ import { validateId, validateTimestamp, validateSessionId, validateTaskString, validateNumericRange, validateArrayLength, validateBoolean, validateEnum, ValidationError, handleSecurityError, sanitizeText, } from '../security/input-validation.js';
26
+ import { assertMetadata, assertTags, inTransaction, toRecordId, CrudValidationError } from '../controllers/crud-helpers.js';
26
27
  import * as path from 'path';
27
28
  import * as fs from 'fs';
28
29
  import { fileURLToPath } from 'url';
@@ -408,6 +409,141 @@ const tools = [
408
409
  // ==========================================================================
409
410
  // FRONTIER MEMORY FEATURES (EXISTING)
410
411
  // ==========================================================================
412
+ {
413
+ name: 'agentdb_get',
414
+ description: 'Get one stored vector record (episode) by id. Returns null when it does not exist. The embedding is omitted unless include_embedding is true.',
415
+ inputSchema: {
416
+ type: 'object',
417
+ properties: {
418
+ id: { type: 'number', description: 'Record id returned by agentdb_insert' },
419
+ include_embedding: { type: 'boolean', description: 'Also return the stored embedding vector', default: false },
420
+ },
421
+ required: ['id'],
422
+ },
423
+ },
424
+ {
425
+ name: 'agentdb_list',
426
+ description: 'Page through stored vector records (episodes), newest first. Returns items plus the total count.',
427
+ inputSchema: {
428
+ type: 'object',
429
+ properties: {
430
+ session_id: { type: 'string', description: 'Only records from this session' },
431
+ limit: { type: 'number', description: 'Page size (1-500)', default: 50 },
432
+ offset: { type: 'number', description: 'Records to skip', default: 0 },
433
+ order: { type: 'string', enum: ['newest', 'oldest'], default: 'newest' },
434
+ },
435
+ },
436
+ },
437
+ {
438
+ name: 'agentdb_update',
439
+ description: 'Update a stored vector record in place, keeping its id. Changing text re-embeds it and refreshes the keyword index; tag or metadata changes do not re-embed. Metadata is merged (null removes a key) unless replace_metadata is true.',
440
+ inputSchema: {
441
+ type: 'object',
442
+ properties: {
443
+ id: { type: 'number', description: 'Record id' },
444
+ text: { type: 'string', description: 'New text content (re-embedded)' },
445
+ tags: { type: 'array', items: { type: 'string' }, description: 'Replacement tag list' },
446
+ metadata: { type: 'object', description: 'Metadata patch (merged by default)' },
447
+ replace_metadata: { type: 'boolean', description: 'Replace metadata instead of merging', default: false },
448
+ reward: { type: 'number', description: 'New reward value' },
449
+ success: { type: 'boolean', description: 'New success flag' },
450
+ },
451
+ required: ['id'],
452
+ },
453
+ },
454
+ {
455
+ name: 'agentdb_pattern_get',
456
+ description: 'Get one reasoning pattern by id.',
457
+ inputSchema: {
458
+ type: 'object',
459
+ properties: { pattern_id: { type: 'number', description: 'Pattern id' } },
460
+ required: ['pattern_id'],
461
+ },
462
+ },
463
+ {
464
+ name: 'agentdb_pattern_list',
465
+ description: 'Page through reasoning patterns, newest first, optionally filtered by task type.',
466
+ inputSchema: {
467
+ type: 'object',
468
+ properties: {
469
+ task_type: { type: 'string', description: 'Only patterns of this task type' },
470
+ limit: { type: 'number', default: 50 },
471
+ offset: { type: 'number', default: 0 },
472
+ },
473
+ },
474
+ },
475
+ {
476
+ name: 'agentdb_pattern_update',
477
+ description: 'Update a reasoning pattern in place. Changing taskType or approach re-embeds it. Usage statistics are not writable here (use agentdb_pattern_feedback).',
478
+ inputSchema: {
479
+ type: 'object',
480
+ properties: {
481
+ pattern_id: { type: 'number', description: 'Pattern id' },
482
+ taskType: { type: 'string' },
483
+ approach: { type: 'string' },
484
+ tags: { type: 'array', items: { type: 'string' } },
485
+ metadata: { type: 'object', description: 'Metadata patch (merged by default)' },
486
+ replace_metadata: { type: 'boolean', default: false },
487
+ },
488
+ required: ['pattern_id'],
489
+ },
490
+ },
491
+ {
492
+ name: 'agentdb_pattern_delete',
493
+ description: 'Delete a reasoning pattern, its embedding and its vector-index entry.',
494
+ inputSchema: {
495
+ type: 'object',
496
+ properties: { pattern_id: { type: 'number', description: 'Pattern id' } },
497
+ required: ['pattern_id'],
498
+ },
499
+ },
500
+ {
501
+ name: 'skill_get',
502
+ description: 'Get one skill by id.',
503
+ inputSchema: {
504
+ type: 'object',
505
+ properties: { skill_id: { type: 'number', description: 'Skill id' } },
506
+ required: ['skill_id'],
507
+ },
508
+ },
509
+ {
510
+ name: 'skill_list',
511
+ description: 'Page through skills, newest first.',
512
+ inputSchema: {
513
+ type: 'object',
514
+ properties: {
515
+ min_success_rate: { type: 'number', description: 'Only skills at or above this success rate' },
516
+ limit: { type: 'number', default: 50 },
517
+ offset: { type: 'number', default: 0 },
518
+ },
519
+ },
520
+ },
521
+ {
522
+ name: 'skill_update',
523
+ description: 'Update a skill in place. Changing name, description or signature re-embeds it; renaming onto an existing name is rejected. Usage statistics are not writable here (use agentdb_skill_feedback).',
524
+ inputSchema: {
525
+ type: 'object',
526
+ properties: {
527
+ skill_id: { type: 'number', description: 'Skill id' },
528
+ name: { type: 'string' },
529
+ description: { type: 'string' },
530
+ code: { type: 'string' },
531
+ signature: { type: 'object', description: '{ inputs: {...}, outputs: {...} }' },
532
+ metadata: { type: 'object', description: 'Metadata patch (merged by default)' },
533
+ replace_metadata: { type: 'boolean', default: false },
534
+ },
535
+ required: ['skill_id'],
536
+ },
537
+ },
538
+ {
539
+ name: 'skill_delete',
540
+ description: 'Delete a skill with its embedding, links and vector-index entry.',
541
+ inputSchema: {
542
+ type: 'object',
543
+ properties: { skill_id: { type: 'number', description: 'Skill id' } },
544
+ required: ['skill_id'],
545
+ },
546
+ },
411
547
  {
412
548
  name: 'reflexion_store',
413
549
  description: 'Store an episode with self-critique for reflexion-based learning',
@@ -974,6 +1110,157 @@ const tools = [
974
1110
  server.setRequestHandler(ListToolsRequestSchema, async () => {
975
1111
  return { tools: filterMcpTools(tools, toolPolicy) };
976
1112
  });
1113
+ /** Bulk-delete episodes together with their embeddings (FK cascades are off by default). */
1114
+ function deleteEpisodesWhere(condition, value) {
1115
+ const changes = inTransaction(db, () => {
1116
+ db.prepare(`DELETE FROM episode_embeddings WHERE episode_id IN (SELECT id FROM episodes WHERE ${condition})`).run(value);
1117
+ return db.prepare(`DELETE FROM episodes WHERE ${condition}`).run(value).changes;
1118
+ });
1119
+ if (changes > 0) {
1120
+ reflexion.clearCache();
1121
+ caches.stats.clear();
1122
+ }
1123
+ return changes;
1124
+ }
1125
+ /** Reject arguments a tool does not define, so a typo can never silently become a no-op update. */
1126
+ function rejectUnknownArgs(args, allowed, tool) {
1127
+ for (const key of Object.keys(args)) {
1128
+ if (!allowed.includes(key))
1129
+ throw new CrudValidationError(`${tool} does not accept "${key}"`, key);
1130
+ }
1131
+ }
1132
+ function optionalString(value, field, maxLength = 100_000) {
1133
+ if (value === undefined)
1134
+ return undefined;
1135
+ if (typeof value !== 'string')
1136
+ throw new CrudValidationError(`${field} must be a string`, field);
1137
+ if (value.length > maxLength)
1138
+ throw new CrudValidationError(`${field} exceeds ${maxLength} characters`, field);
1139
+ return value;
1140
+ }
1141
+ /** Dispatch for the ADR-017 CRUD tools. Returns a JSON-serialisable result. */
1142
+ async function handleCrudTool(name, args) {
1143
+ switch (name) {
1144
+ case 'agentdb_get': {
1145
+ rejectUnknownArgs(args, ['id', 'include_embedding'], name);
1146
+ const episode = reflexion.getEpisode(toRecordId(args.id), { includeEmbedding: args.include_embedding === true });
1147
+ if (!episode)
1148
+ return null;
1149
+ const { embedding, ...rest } = episode;
1150
+ return embedding ? { ...rest, embedding: Array.from(embedding) } : rest;
1151
+ }
1152
+ case 'agentdb_list': {
1153
+ rejectUnknownArgs(args, ['session_id', 'limit', 'offset', 'order'], name);
1154
+ const order = args.order === undefined ? 'newest' : validateEnum(args.order, 'order', ['newest', 'oldest']);
1155
+ return reflexion.listEpisodes({
1156
+ sessionId: args.session_id === undefined ? undefined : validateSessionId(args.session_id),
1157
+ limit: args.limit,
1158
+ offset: args.offset,
1159
+ order,
1160
+ });
1161
+ }
1162
+ case 'agentdb_update': {
1163
+ rejectUnknownArgs(args, ['id', 'text', 'tags', 'metadata', 'replace_metadata', 'reward', 'success'], name);
1164
+ // agentdb_insert stores the text as both task and input, so an update keeps them in step.
1165
+ const text = args.text === undefined ? undefined : requireText(args.text, 'text');
1166
+ const result = await reflexion.updateEpisode(toRecordId(args.id), {
1167
+ task: text,
1168
+ input: text,
1169
+ tags: args.tags === undefined ? undefined : assertTags(args.tags),
1170
+ metadata: args.metadata === undefined ? undefined : assertMetadata(args.metadata),
1171
+ reward: args.reward === undefined ? undefined : validateNumericRange(args.reward, 'reward', -1e6, 1e6),
1172
+ success: args.success === undefined ? undefined : validateBoolean(args.success, 'success'),
1173
+ }, { replaceMetadata: args.replace_metadata === true });
1174
+ if (result)
1175
+ caches.stats.clear();
1176
+ return result ? { updated: true, ...result } : { updated: false, reason: 'not found' };
1177
+ }
1178
+ case 'agentdb_pattern_get': {
1179
+ rejectUnknownArgs(args, ['pattern_id'], name);
1180
+ const pattern = reasoningBank.getPattern(toRecordId(args.pattern_id, 'pattern_id'));
1181
+ if (!pattern)
1182
+ return null;
1183
+ const { embedding: _omit, ...rest } = pattern;
1184
+ return rest;
1185
+ }
1186
+ case 'agentdb_pattern_list': {
1187
+ rejectUnknownArgs(args, ['task_type', 'limit', 'offset'], name);
1188
+ return reasoningBank.listPatterns({
1189
+ taskType: optionalString(args.task_type, 'task_type', 1000),
1190
+ limit: args.limit,
1191
+ offset: args.offset,
1192
+ });
1193
+ }
1194
+ case 'agentdb_pattern_update': {
1195
+ rejectUnknownArgs(args, ['pattern_id', 'taskType', 'approach', 'tags', 'metadata', 'replace_metadata'], name);
1196
+ const result = await reasoningBank.updatePattern(toRecordId(args.pattern_id, 'pattern_id'), {
1197
+ taskType: args.taskType === undefined ? undefined : requireText(args.taskType, 'taskType'),
1198
+ approach: args.approach === undefined ? undefined : requireText(args.approach, 'approach'),
1199
+ tags: args.tags === undefined ? undefined : assertTags(args.tags),
1200
+ metadata: args.metadata === undefined ? undefined : assertMetadata(args.metadata),
1201
+ }, { replaceMetadata: args.replace_metadata === true });
1202
+ if (result) {
1203
+ caches.patterns.clear();
1204
+ caches.stats.clear();
1205
+ }
1206
+ if (!result)
1207
+ return { updated: false, reason: 'not found' };
1208
+ const { embedding: _omit, ...pattern } = result.pattern;
1209
+ return { updated: true, ...result, pattern };
1210
+ }
1211
+ case 'agentdb_pattern_delete': {
1212
+ rejectUnknownArgs(args, ['pattern_id'], name);
1213
+ const deleted = reasoningBank.deletePattern(toRecordId(args.pattern_id, 'pattern_id'));
1214
+ if (deleted) {
1215
+ caches.patterns.clear();
1216
+ caches.stats.clear();
1217
+ }
1218
+ return { deleted };
1219
+ }
1220
+ case 'skill_get': {
1221
+ rejectUnknownArgs(args, ['skill_id'], name);
1222
+ return skills.getSkill(toRecordId(args.skill_id, 'skill_id'));
1223
+ }
1224
+ case 'skill_list': {
1225
+ rejectUnknownArgs(args, ['min_success_rate', 'limit', 'offset'], name);
1226
+ return skills.listSkills({
1227
+ minSuccessRate: args.min_success_rate === undefined ? undefined : validateNumericRange(args.min_success_rate, 'min_success_rate', 0, 1),
1228
+ limit: args.limit,
1229
+ offset: args.offset,
1230
+ });
1231
+ }
1232
+ case 'skill_update': {
1233
+ rejectUnknownArgs(args, ['skill_id', 'name', 'description', 'code', 'signature', 'metadata', 'replace_metadata'], name);
1234
+ const result = await skills.updateSkill(toRecordId(args.skill_id, 'skill_id'), {
1235
+ name: args.name === undefined ? undefined : requireText(args.name, 'name'),
1236
+ description: args.description === undefined ? undefined : requireText(args.description, 'description'),
1237
+ code: optionalString(args.code, 'code'),
1238
+ signature: args.signature === undefined ? undefined
1239
+ : assertMetadata(args.signature, 'signature'),
1240
+ metadata: args.metadata === undefined ? undefined : assertMetadata(args.metadata),
1241
+ }, { replaceMetadata: args.replace_metadata === true });
1242
+ if (result)
1243
+ caches.stats.clear();
1244
+ return result ? { updated: true, ...result } : { updated: false, reason: 'not found' };
1245
+ }
1246
+ case 'skill_delete': {
1247
+ rejectUnknownArgs(args, ['skill_id'], name);
1248
+ const deleted = skills.deleteSkill(toRecordId(args.skill_id, 'skill_id'));
1249
+ if (deleted)
1250
+ caches.stats.clear();
1251
+ return { deleted };
1252
+ }
1253
+ default:
1254
+ throw new CrudValidationError(`unknown CRUD tool ${name}`, 'name');
1255
+ }
1256
+ }
1257
+ /** Non-empty text up to `maxLength`, null bytes stripped. Deliberately no content heuristics: stored text may be code. */
1258
+ function requireText(value, field, maxLength = 100_000) {
1259
+ const text = sanitizeText(value, maxLength, field);
1260
+ if (!text.trim())
1261
+ throw new CrudValidationError(`${field} cannot be empty`, field);
1262
+ return text;
1263
+ }
977
1264
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
978
1265
  const { name, arguments: args } = request.params;
979
1266
  try {
@@ -991,7 +1278,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
991
1278
  }
992
1279
  // Initialize schema
993
1280
  initializeSchema(db);
994
- const stats = db.prepare('SELECT COUNT(*) as count FROM sqlite_master WHERE type="table"').get();
1281
+ const stats = db.prepare("SELECT COUNT(*) as count FROM sqlite_master WHERE type = 'table'").get();
995
1282
  return {
996
1283
  content: [
997
1284
  {
@@ -1006,10 +1293,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1006
1293
  };
1007
1294
  }
1008
1295
  case 'agentdb_insert': {
1009
- const text = args?.text;
1010
- const sessionId = args?.session_id || 'default';
1011
- const tags = args?.tags || [];
1012
- const metadata = args?.metadata || {};
1296
+ const text = requireText(args?.text, 'text');
1297
+ const sessionId = args?.session_id === undefined ? 'default' : validateSessionId(args.session_id);
1298
+ const tags = args?.tags === undefined ? [] : assertTags(args.tags);
1299
+ const metadata = args?.metadata === undefined ? {} : assertMetadata(args.metadata);
1013
1300
  const episodeId = await reflexion.storeEpisode({
1014
1301
  sessionId,
1015
1302
  task: text,
@@ -1039,19 +1326,24 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1039
1326
  case 'agentdb_insert_batch': {
1040
1327
  const items = validateArrayLength(args?.items ?? [], 'items', 0, MAX_INSERT_BATCH_ITEMS);
1041
1328
  const batchSize = args?.batch_size || 100;
1042
- const episodes = items.map((item) => ({
1043
- sessionId: item.session_id || 'default',
1044
- task: item.text,
1045
- reward: 1.0,
1046
- success: true,
1047
- input: item.text,
1048
- output: '',
1049
- critique: '',
1050
- latencyMs: 0,
1051
- tokensUsed: 0,
1052
- tags: item.tags || [],
1053
- metadata: item.metadata || {},
1054
- }));
1329
+ const episodes = items.map((item, i) => {
1330
+ if (!item || typeof item !== 'object')
1331
+ throw new ValidationError(`items[${i}] must be an object`, 'INVALID_TYPE', 'items');
1332
+ const text = requireText(item.text, `items[${i}].text`);
1333
+ return {
1334
+ sessionId: item.session_id === undefined ? 'default' : validateSessionId(item.session_id),
1335
+ task: text,
1336
+ reward: 1.0,
1337
+ success: true,
1338
+ input: text,
1339
+ output: '',
1340
+ critique: '',
1341
+ latencyMs: 0,
1342
+ tokensUsed: 0,
1343
+ tags: item.tags === undefined ? [] : assertTags(item.tags, `items[${i}].tags`),
1344
+ metadata: item.metadata === undefined ? {} : assertMetadata(item.metadata, `items[${i}].metadata`),
1345
+ };
1346
+ });
1055
1347
  const batchOpsConfig = new BatchOperations(db, embeddingService, {
1056
1348
  batchSize,
1057
1349
  parallelism: 4,
@@ -1117,10 +1409,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1117
1409
  if (id !== undefined) {
1118
1410
  // Validate ID
1119
1411
  const validatedId = validateId(id, 'id');
1120
- // Delete single vector using parameterized query
1121
- const stmt = db.prepare('DELETE FROM episodes WHERE id = ?');
1122
- const result = stmt.run(validatedId);
1123
- deleted = result.changes;
1412
+ // Through the controller so the embedding, vector index and caches go with the row
1413
+ deleted = (await reflexion.deleteEpisode(validatedId)) ? 1 : 0;
1124
1414
  }
1125
1415
  else if (filters) {
1126
1416
  // Bulk delete with validated filters
@@ -1128,17 +1418,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1128
1418
  // Validate session_id
1129
1419
  const validatedSessionId = validateSessionId(filters.session_id);
1130
1420
  // Use parameterized query
1131
- const stmt = db.prepare('DELETE FROM episodes WHERE session_id = ?');
1132
- const result = stmt.run(validatedSessionId);
1133
- deleted = result.changes;
1421
+ deleted = deleteEpisodesWhere('session_id = ?', validatedSessionId);
1134
1422
  }
1135
1423
  else if (filters.before_timestamp) {
1136
1424
  // Validate timestamp
1137
1425
  const validatedTimestamp = validateTimestamp(filters.before_timestamp, 'before_timestamp');
1138
1426
  // Use parameterized query
1139
- const stmt = db.prepare('DELETE FROM episodes WHERE ts < ?');
1140
- const result = stmt.run(validatedTimestamp);
1141
- deleted = result.changes;
1427
+ deleted = deleteEpisodesWhere('ts < ?', validatedTimestamp);
1142
1428
  }
1143
1429
  else {
1144
1430
  throw new ValidationError('Invalid or missing filter criteria', 'INVALID_FILTER');
@@ -1172,6 +1458,29 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1172
1458
  }
1173
1459
  }
1174
1460
  // ======================================================================
1461
+ // CRUD: get / list / update / delete (ADR-017)
1462
+ // ======================================================================
1463
+ case 'agentdb_get':
1464
+ case 'agentdb_list':
1465
+ case 'agentdb_update':
1466
+ case 'agentdb_pattern_get':
1467
+ case 'agentdb_pattern_list':
1468
+ case 'agentdb_pattern_update':
1469
+ case 'agentdb_pattern_delete':
1470
+ case 'skill_get':
1471
+ case 'skill_list':
1472
+ case 'skill_update':
1473
+ case 'skill_delete': {
1474
+ try {
1475
+ const result = await handleCrudTool(name, (args ?? {}));
1476
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
1477
+ }
1478
+ catch (error) {
1479
+ const message = error instanceof CrudValidationError ? error.message : handleSecurityError(error);
1480
+ return { content: [{ type: 'text', text: `❌ ${name} failed: ${message}` }], isError: true };
1481
+ }
1482
+ }
1483
+ // ======================================================================
1175
1484
  // FRONTIER MEMORY FEATURES (EXISTING)
1176
1485
  // ======================================================================
1177
1486
  case 'reflexion_store': {
@@ -2277,30 +2586,40 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2277
2586
  };
2278
2587
  }
2279
2588
  case 'agentdb_delete_batch': {
2280
- const ids = args?.ids || [];
2589
+ const ids = validateArrayLength(args?.ids ?? [], 'ids', 0, MAX_INSERT_BATCH_ITEMS);
2281
2590
  const requestedTable = args?.table || 'episodes';
2282
- const allowedTables = new Set(['episodes', 'reasoning_patterns']);
2283
- if (!allowedTables.has(requestedTable)) {
2284
- throw new Error(`Invalid table: ${requestedTable}`);
2591
+ // Each table with the embedding table that must be cleaned alongside it (FK cascades are off by default)
2592
+ const embeddingTables = {
2593
+ episodes: ['episode_embeddings', 'episode_id'],
2594
+ reasoning_patterns: ['pattern_embeddings', 'pattern_id'],
2595
+ };
2596
+ if (!Object.prototype.hasOwnProperty.call(embeddingTables, requestedTable)) {
2597
+ throw new ValidationError(`Invalid table: ${requestedTable}`, 'INVALID_TABLE', 'table');
2285
2598
  }
2286
2599
  if (ids.length === 0) {
2287
2600
  return { content: [{ type: 'text', text: '⚠️ No IDs supplied — nothing to delete.' }] };
2288
2601
  }
2289
- // Validate each id is a positive integer (defense in depth)
2290
- const safeIds = ids.map((raw) => {
2291
- const n = Number(raw);
2292
- if (!Number.isInteger(n) || n <= 0)
2293
- throw new Error(`Invalid id: ${raw}`);
2294
- return n;
2295
- });
2602
+ const safeIds = ids.map((raw, i) => toRecordId(raw, `ids[${i}]`));
2296
2603
  const placeholders = safeIds.map(() => '?').join(',');
2297
- // requestedTable is whitelist-checked above; parameterising values via ?.
2298
- const stmt = db.prepare(`DELETE FROM ${requestedTable} WHERE id IN (${placeholders})`);
2299
- const result = stmt.run(...safeIds);
2604
+ const [embTable, embColumn] = embeddingTables[requestedTable];
2605
+ // Table names come from the fixed map above; values are bound parameters.
2606
+ const changes = inTransaction(db, () => {
2607
+ db.prepare(`DELETE FROM ${embTable} WHERE ${embColumn} IN (${placeholders})`).run(...safeIds);
2608
+ return db.prepare(`DELETE FROM ${requestedTable} WHERE id IN (${placeholders})`).run(...safeIds).changes;
2609
+ });
2610
+ if (changes > 0) {
2611
+ if (requestedTable === 'episodes')
2612
+ reflexion.clearCache();
2613
+ else {
2614
+ reasoningBank.clearCache();
2615
+ caches.patterns.clear();
2616
+ }
2617
+ caches.stats.clear();
2618
+ }
2300
2619
  return {
2301
2620
  content: [{
2302
2621
  type: 'text',
2303
- text: `🗑️ Batch delete from \`${requestedTable}\`: ${result.changes}/${safeIds.length} rows removed (atomic).`,
2622
+ text: `🗑️ Batch delete from \`${requestedTable}\`: ${changes}/${safeIds.length} rows removed (atomic).`,
2304
2623
  }],
2305
2624
  };
2306
2625
  }
@@ -2325,11 +2644,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2325
2644
  }
2326
2645
  }
2327
2646
  catch (error) {
2647
+ if (!(error instanceof ValidationError))
2648
+ console.error(`[agentdb-mcp] ${name} failed:`, error);
2328
2649
  return {
2329
2650
  content: [
2330
2651
  {
2331
2652
  type: 'text',
2332
- text: `❌ Error: ${error.message}\n${error.stack || ''}`,
2653
+ // The stack trace stays in the server log; clients only see the message.
2654
+ text: `❌ Error: ${error?.message ?? String(error)}`,
2333
2655
  },
2334
2656
  ],
2335
2657
  isError: true,