@desplega.ai/agent-swarm 1.64.0 → 1.65.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.
@@ -4,15 +4,12 @@ import {
4
4
  closeDb,
5
5
  completeTask,
6
6
  createAgent,
7
- createMemory,
8
7
  createTaskExtended,
9
8
  failTask,
10
9
  getAgentById,
11
10
  initDb,
12
- searchMemoriesByVector,
13
- updateMemoryEmbedding,
14
11
  } from "../be/db";
15
- import { serializeEmbedding } from "../be/embedding";
12
+ import { SqliteMemoryStore } from "../be/memory/providers/sqlite-store";
16
13
  import { getBasePrompt } from "../prompts/base-prompt";
17
14
 
18
15
  const TEST_DB_PATH = "./test-self-improvement.sqlite";
@@ -21,6 +18,7 @@ describe("Self-Improvement Mechanisms", () => {
21
18
  const leadId = "aaaa0000-0000-4000-8000-000000000001";
22
19
  const workerId = "bbbb0000-0000-4000-8000-000000000002";
23
20
  const otherWorkerId = "cccc0000-0000-4000-8000-000000000003";
21
+ let store: SqliteMemoryStore;
24
22
 
25
23
  beforeAll(async () => {
26
24
  for (const suffix of ["", "-wal", "-shm"]) {
@@ -33,6 +31,7 @@ describe("Self-Improvement Mechanisms", () => {
33
31
 
34
32
  closeDb();
35
33
  initDb(TEST_DB_PATH);
34
+ store = new SqliteMemoryStore();
36
35
 
37
36
  createAgent({ id: leadId, name: "Test Lead", isLead: true, status: "idle" });
38
37
  createAgent({ id: workerId, name: "Test Worker", isLead: false, status: "idle" });
@@ -67,7 +66,7 @@ describe("Self-Improvement Mechanisms", () => {
67
66
 
68
67
  // Simulate what store-progress does: create memory for completed task
69
68
  const taskContent = `Task: ${task.task}\n\nOutput:\n${output}`;
70
- const memory = createMemory({
69
+ const memory = store.store({
71
70
  agentId: workerId,
72
71
  content: taskContent,
73
72
  name: `Task: ${task.task.slice(0, 80)}`,
@@ -113,7 +112,7 @@ describe("Self-Improvement Mechanisms", () => {
113
112
 
114
113
  // Simulate store-progress failed task memory creation
115
114
  const taskContent = `Task: ${task.task}\n\nFailure reason:\n${failureReason}\n\nThis task failed. Learn from this to avoid repeating the mistake.`;
116
- const memory = createMemory({
115
+ const memory = store.store({
117
116
  agentId: workerId,
118
117
  content: taskContent,
119
118
  name: `Task: ${task.task.slice(0, 80)}`,
@@ -170,7 +169,7 @@ describe("Self-Improvement Mechanisms", () => {
170
169
  expect(shouldShareWithSwarm).toBe(true);
171
170
 
172
171
  // Verify swarm memory can be created
173
- const swarmMemory = createMemory({
172
+ const swarmMemory = store.store({
174
173
  agentId: workerId,
175
174
  scope: "swarm",
176
175
  name: `Shared: ${task.task.slice(0, 80)}`,
@@ -266,7 +265,7 @@ describe("Self-Improvement Mechanisms", () => {
266
265
  const learning = "Always run lint before committing";
267
266
  const content = `[Lead Feedback — ${category}]\n\n${learning}`;
268
267
 
269
- const memory = createMemory({
268
+ const memory = store.store({
270
269
  agentId: workerId,
271
270
  scope: "swarm",
272
271
  name: `Lead feedback: ${category} — ${learning.slice(0, 60)}`,
@@ -293,7 +292,7 @@ describe("Self-Improvement Mechanisms", () => {
293
292
  test("injected learning is visible to target worker in memory search", () => {
294
293
  // Create memory with embedding for searchability
295
294
  const content = "[Lead Feedback — mistake-pattern]\n\nNever force-push to main branch";
296
- const memory = createMemory({
295
+ const memory = store.store({
297
296
  agentId: workerId,
298
297
  scope: "agent",
299
298
  name: "Lead feedback: mistake-pattern — Never force-push to main branch",
@@ -302,10 +301,10 @@ describe("Self-Improvement Mechanisms", () => {
302
301
  });
303
302
 
304
303
  const embedding = new Float32Array([0.7, 0.3, 0.0]);
305
- updateMemoryEmbedding(memory.id, serializeEmbedding(embedding));
304
+ store.updateEmbedding(memory.id, embedding, "test-model");
306
305
 
307
306
  // Worker can find it via search
308
- const results = searchMemoriesByVector(new Float32Array([0.7, 0.3, 0.0]), workerId, {
307
+ const results = store.search(new Float32Array([0.7, 0.3, 0.0]), workerId, {
309
308
  isLead: false,
310
309
  scope: "agent",
311
310
  });
@@ -317,7 +316,7 @@ describe("Self-Improvement Mechanisms", () => {
317
316
 
318
317
  test("injected learning is NOT visible to other workers", () => {
319
318
  const content = "[Lead Feedback — preference]\n\nUse bun instead of npm";
320
- const memory = createMemory({
319
+ const memory = store.store({
321
320
  agentId: workerId,
322
321
  scope: "agent",
323
322
  name: "Lead feedback: preference — Use bun instead of npm",
@@ -326,10 +325,10 @@ describe("Self-Improvement Mechanisms", () => {
326
325
  });
327
326
 
328
327
  const embedding = new Float32Array([0.2, 0.8, 0.1]);
329
- updateMemoryEmbedding(memory.id, serializeEmbedding(embedding));
328
+ store.updateEmbedding(memory.id, embedding, "test-model");
330
329
 
331
330
  // Other worker should NOT see it
332
- const results = searchMemoriesByVector(new Float32Array([0.2, 0.8, 0.1]), otherWorkerId, {
331
+ const results = store.search(new Float32Array([0.2, 0.8, 0.1]), otherWorkerId, {
333
332
  isLead: false,
334
333
  scope: "agent",
335
334
  });
@@ -355,26 +354,26 @@ describe("Self-Improvement Mechanisms", () => {
355
354
  describe("memory search agent ID security", () => {
356
355
  test("agent can only search their own memories (not others)", () => {
357
356
  // Create private memories for worker and other worker
358
- const workerMemory = createMemory({
357
+ const workerMemory = store.store({
359
358
  agentId: workerId,
360
359
  scope: "agent",
361
360
  name: "Worker Private Secret",
362
361
  content: "My secret API key pattern",
363
362
  source: "manual",
364
363
  });
365
- updateMemoryEmbedding(workerMemory.id, serializeEmbedding(new Float32Array([0.5, 0.5, 0.0])));
364
+ store.updateEmbedding(workerMemory.id, new Float32Array([0.5, 0.5, 0.0]), "test-model");
366
365
 
367
- const otherMemory = createMemory({
366
+ const otherMemory = store.store({
368
367
  agentId: otherWorkerId,
369
368
  scope: "agent",
370
369
  name: "Other Worker Secret",
371
370
  content: "Other agent's private data",
372
371
  source: "manual",
373
372
  });
374
- updateMemoryEmbedding(otherMemory.id, serializeEmbedding(new Float32Array([0.5, 0.5, 0.0])));
373
+ store.updateEmbedding(otherMemory.id, new Float32Array([0.5, 0.5, 0.0]), "test-model");
375
374
 
376
375
  // Worker searching with their own ID should see their memory but not other's
377
- const workerResults = searchMemoriesByVector(new Float32Array([0.5, 0.5, 0.0]), workerId, {
376
+ const workerResults = store.search(new Float32Array([0.5, 0.5, 0.0]), workerId, {
378
377
  isLead: false,
379
378
  scope: "all",
380
379
  });
@@ -178,8 +178,8 @@ describe("Tool Annotations & Classification", () => {
178
178
  expect(overlap).toEqual([]);
179
179
  });
180
180
 
181
- test("CORE_TOOLS contains exactly 13 tools", () => {
182
- expect(CORE_TOOLS.size).toBe(13);
181
+ test("CORE_TOOLS contains exactly 14 tools", () => {
182
+ expect(CORE_TOOLS.size).toBe(14);
183
183
  });
184
184
 
185
185
  test("ALL_TOOLS equals CORE_TOOLS union DEFERRED_TOOLS", () => {
@@ -1,7 +1,7 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import * as z from "zod";
3
- import { createMemory, getAgentById, updateMemoryEmbedding } from "@/be/db";
4
- import { getEmbedding, serializeEmbedding } from "@/be/embedding";
3
+ import { getAgentById } from "@/be/db";
4
+ import { getEmbeddingProvider, getMemoryStore } from "@/be/memory";
5
5
  import { createToolRegistrar } from "@/tools/utils";
6
6
 
7
7
  const LearningCategoryEnum = z.enum([
@@ -69,7 +69,8 @@ export const registerInjectLearningTool = (server: McpServer) => {
69
69
 
70
70
  // Create swarm-scoped memory — lead learnings are organizational knowledge visible to all workers
71
71
  const content = `[Lead Feedback — ${category}]\n\n${learning}`;
72
- const memory = createMemory({
72
+ const store = getMemoryStore();
73
+ const memory = store.store({
73
74
  agentId: targetAgentId,
74
75
  scope: "swarm",
75
76
  name: `Lead feedback: ${category} — ${learning.slice(0, 60)}`,
@@ -79,9 +80,10 @@ export const registerInjectLearningTool = (server: McpServer) => {
79
80
 
80
81
  // Generate and store embedding (async, best-effort)
81
82
  try {
82
- const embedding = await getEmbedding(content);
83
+ const provider = getEmbeddingProvider();
84
+ const embedding = await provider.embed(content);
83
85
  if (embedding) {
84
- updateMemoryEmbedding(memory.id, serializeEmbedding(embedding));
86
+ store.updateEmbedding(memory.id, embedding, provider.name);
85
87
  }
86
88
  } catch {
87
89
  // Non-blocking — memory was created, embedding is optional
@@ -0,0 +1,89 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import * as z from "zod";
3
+ import { getAgentById } from "@/be/db";
4
+ import { getMemoryStore } from "@/be/memory";
5
+ import { createToolRegistrar } from "@/tools/utils";
6
+
7
+ export const registerMemoryDeleteTool = (server: McpServer) => {
8
+ createToolRegistrar(server)(
9
+ "memory-delete",
10
+ {
11
+ title: "Delete a memory",
12
+ description:
13
+ "Delete a specific memory by its ID. Agents can delete their own memories; lead agents can also delete swarm-scoped memories.",
14
+ annotations: { destructiveHint: true },
15
+
16
+ inputSchema: z.object({
17
+ memoryId: z.uuid().describe("The ID of the memory to delete."),
18
+ }),
19
+ outputSchema: z.object({
20
+ yourAgentId: z.string().uuid().optional(),
21
+ success: z.boolean(),
22
+ message: z.string(),
23
+ }),
24
+ },
25
+ async ({ memoryId }, requestInfo, _meta) => {
26
+ if (!requestInfo.agentId) {
27
+ return {
28
+ content: [{ type: "text", text: "Agent ID required to delete memories." }],
29
+ structuredContent: {
30
+ yourAgentId: undefined,
31
+ success: false,
32
+ message: "Agent ID required. Are you registered in the swarm?",
33
+ },
34
+ };
35
+ }
36
+
37
+ const store = getMemoryStore();
38
+ const memory = store.peek(memoryId);
39
+
40
+ if (!memory) {
41
+ return {
42
+ content: [{ type: "text", text: `Memory "${memoryId}" not found.` }],
43
+ structuredContent: {
44
+ yourAgentId: requestInfo.agentId,
45
+ success: false,
46
+ message: `Memory "${memoryId}" not found.`,
47
+ },
48
+ };
49
+ }
50
+
51
+ // Permission check: own memories or lead can delete swarm-scoped
52
+ const isOwner = memory.agentId === requestInfo.agentId;
53
+ const agent = getAgentById(requestInfo.agentId);
54
+ const isLeadDeletingSwarm = agent?.isLead && memory.scope === "swarm";
55
+
56
+ if (!isOwner && !isLeadDeletingSwarm) {
57
+ return {
58
+ content: [{ type: "text", text: "You don't have permission to delete this memory." }],
59
+ structuredContent: {
60
+ yourAgentId: requestInfo.agentId,
61
+ success: false,
62
+ message:
63
+ "Permission denied. You can only delete your own memories, or swarm memories if you are the lead.",
64
+ },
65
+ };
66
+ }
67
+
68
+ const deleted = store.delete(memoryId);
69
+
70
+ return {
71
+ content: [
72
+ {
73
+ type: "text",
74
+ text: deleted
75
+ ? `Memory "${memoryId}" deleted.`
76
+ : `Failed to delete memory "${memoryId}".`,
77
+ },
78
+ ],
79
+ structuredContent: {
80
+ yourAgentId: requestInfo.agentId,
81
+ success: deleted,
82
+ message: deleted
83
+ ? `Memory "${memoryId}" deleted.`
84
+ : `Failed to delete memory "${memoryId}".`,
85
+ },
86
+ };
87
+ },
88
+ );
89
+ };
@@ -1,6 +1,6 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import * as z from "zod";
3
- import { getMemoryById } from "@/be/db";
3
+ import { getMemoryStore } from "@/be/memory";
4
4
  import { createToolRegistrar } from "@/tools/utils";
5
5
  import { AgentMemorySchema } from "@/types";
6
6
 
@@ -24,7 +24,7 @@ export const registerMemoryGetTool = (server: McpServer) => {
24
24
  }),
25
25
  },
26
26
  async ({ memoryId }, requestInfo, _meta) => {
27
- const memory = getMemoryById(memoryId);
27
+ const memory = getMemoryStore().get(memoryId);
28
28
 
29
29
  if (!memory) {
30
30
  return {
@@ -1,7 +1,9 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import * as z from "zod";
3
- import { getAgentById, listMemoriesByAgent, searchMemoriesByVector } from "@/be/db";
4
- import { getEmbedding } from "@/be/embedding";
3
+ import { getAgentById } from "@/be/db";
4
+ import { getEmbeddingProvider, getMemoryStore } from "@/be/memory";
5
+ import { CANDIDATE_SET_MULTIPLIER } from "@/be/memory/constants";
6
+ import { rerank } from "@/be/memory/reranker";
5
7
  import { createToolRegistrar } from "@/tools/utils";
6
8
  import { AgentMemoryScopeSchema, AgentMemorySourceSchema } from "@/types";
7
9
 
@@ -60,17 +62,21 @@ export const registerMemorySearchTool = (server: McpServer) => {
60
62
  const isLead = agent?.isLead ?? false;
61
63
 
62
64
  // Try vector search first
63
- const queryEmbedding = await getEmbedding(query);
65
+ const provider = getEmbeddingProvider();
66
+ const store = getMemoryStore();
67
+ const queryEmbedding = await provider.embed(query);
64
68
 
65
69
  if (queryEmbedding) {
66
- const results = searchMemoriesByVector(queryEmbedding, requestInfo.agentId, {
70
+ const candidateLimit = limit * CANDIDATE_SET_MULTIPLIER;
71
+ const candidates = store.search(queryEmbedding, requestInfo.agentId, {
67
72
  scope: scope as "agent" | "swarm" | "all",
68
- limit,
73
+ limit: candidateLimit,
69
74
  source,
70
75
  isLead,
71
76
  });
77
+ const ranked = rerank(candidates, { limit });
72
78
 
73
- const mapped = results.map((r) => ({
79
+ const mapped = ranked.map((r) => ({
74
80
  id: r.id,
75
81
  name: r.name,
76
82
  summary: r.summary,
@@ -97,7 +103,7 @@ export const registerMemorySearchTool = (server: McpServer) => {
97
103
  }
98
104
 
99
105
  // Fallback: list recent memories (no OPENAI_API_KEY)
100
- const recent = listMemoriesByAgent(requestInfo.agentId, {
106
+ const recent = store.list(requestInfo.agentId, {
101
107
  scope: scope as "agent" | "swarm" | "all",
102
108
  limit,
103
109
  isLead,
@@ -3,7 +3,6 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import * as z from "zod";
4
4
  import {
5
5
  completeTask,
6
- createMemory,
7
6
  createSessionCost,
8
7
  createTaskExtended,
9
8
  failTask,
@@ -12,10 +11,9 @@ import {
12
11
  getLeadAgent,
13
12
  getTaskById,
14
13
  updateAgentStatusFromCapacity,
15
- updateMemoryEmbedding,
16
14
  updateTaskProgress,
17
15
  } from "@/be/db";
18
- import { getEmbedding, serializeEmbedding } from "@/be/embedding";
16
+ import { getEmbeddingProvider, getMemoryStore } from "@/be/memory";
19
17
  import { resolveTemplate } from "@/prompts/resolver";
20
18
  import { createToolRegistrar } from "@/tools/utils";
21
19
  import { AgentTaskSchema } from "@/types";
@@ -258,17 +256,20 @@ export const registerStoreProgressTool = (server: McpServer) => {
258
256
  // Skip indexing if there's truly no content
259
257
  if (taskContent.length < 30) return;
260
258
 
261
- const memory = createMemory({
262
- agentId: requestInfo.agentId,
259
+ const store = getMemoryStore();
260
+ const provider = getEmbeddingProvider();
261
+
262
+ const memory = store.store({
263
+ agentId: requestInfo.agentId ?? null,
263
264
  content: taskContent,
264
265
  name: `Task: ${result.task!.task.slice(0, 80)}`,
265
266
  scope: "agent",
266
267
  source: "task_completion",
267
268
  sourceTaskId: taskId,
268
269
  });
269
- const embedding = await getEmbedding(taskContent);
270
+ const embedding = await provider.embed(taskContent);
270
271
  if (embedding) {
271
- updateMemoryEmbedding(memory.id, serializeEmbedding(embedding));
272
+ store.updateEmbedding(memory.id, embedding, provider.name);
272
273
  }
273
274
 
274
275
  // Auto-promote high-value completions to swarm memory (P3)
@@ -280,17 +281,17 @@ export const registerStoreProgressTool = (server: McpServer) => {
280
281
 
281
282
  if (shouldShareWithSwarm) {
282
283
  try {
283
- const swarmMemory = createMemory({
284
- agentId: requestInfo.agentId,
284
+ const swarmMemory = store.store({
285
+ agentId: requestInfo.agentId ?? null,
285
286
  scope: "swarm",
286
287
  name: `Shared: ${result.task!.task.slice(0, 80)}`,
287
288
  content: `Task completed by agent ${requestInfo.agentId}:\n\n${taskContent}`,
288
289
  source: "task_completion",
289
290
  sourceTaskId: taskId,
290
291
  });
291
- const swarmEmbedding = await getEmbedding(taskContent);
292
+ const swarmEmbedding = await provider.embed(taskContent);
292
293
  if (swarmEmbedding) {
293
- updateMemoryEmbedding(swarmMemory.id, serializeEmbedding(swarmEmbedding));
294
+ store.updateEmbedding(swarmMemory.id, swarmEmbedding, provider.name);
294
295
  }
295
296
  } catch {
296
297
  // Non-blocking — swarm memory promotion failure is not critical
@@ -30,6 +30,7 @@ export const CORE_TOOLS = new Set([
30
30
  // Memory (used at session start)
31
31
  "memory-search", // recall relevant context
32
32
  "memory-get", // retrieve full memory content
33
+ "memory-delete", // delete own memories
33
34
 
34
35
  // Swarm awareness
35
36
  "get-swarm", // check who's online
package/src/types.ts CHANGED
@@ -592,6 +592,9 @@ export const AgentMemorySchema = z.object({
592
592
  tags: z.array(z.string()),
593
593
  createdAt: z.string(),
594
594
  accessedAt: z.string(),
595
+ expiresAt: z.string().nullable().optional(),
596
+ accessCount: z.number().int().min(0).default(0).optional(),
597
+ embeddingModel: z.string().nullable().optional(),
595
598
  });
596
599
 
597
600
  export type AgentMemoryScope = z.infer<typeof AgentMemoryScopeSchema>;
package/tsconfig.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "compilerOptions": {
3
+ // Environment setup & latest features
4
+ "lib": ["ESNext"],
5
+ "types": ["bun"],
6
+ "target": "ESNext",
7
+ "module": "Preserve",
8
+ "moduleDetection": "force",
9
+ "jsx": "react-jsx",
10
+ "allowJs": true,
11
+
12
+ // Bundler mode
13
+ "moduleResolution": "bundler",
14
+ "allowImportingTsExtensions": true,
15
+
16
+ // Path aliases
17
+ "baseUrl": ".",
18
+ "paths": {
19
+ "@/*": ["src/*"]
20
+ },
21
+ "verbatimModuleSyntax": true,
22
+ "noEmit": true,
23
+
24
+ // Best practices
25
+ "strict": true,
26
+ "skipLibCheck": true,
27
+ "noFallthroughCasesInSwitch": true,
28
+ "noUncheckedIndexedAccess": true,
29
+ "noImplicitOverride": true,
30
+
31
+ // Some stricter flags (disabled by default)
32
+ "noUnusedLocals": false,
33
+ "noUnusedParameters": false,
34
+ "noPropertyAccessFromIndexSignature": false
35
+ },
36
+ "exclude": [
37
+ "ui",
38
+ "new-ui",
39
+ "templates-ui",
40
+ "landing",
41
+ "node_modules",
42
+ "scripts",
43
+ "src/tests",
44
+ "work",
45
+ "logs",
46
+ "plugin",
47
+ "docs-site"
48
+ ]
49
+ }