@gethmy/mcp 2.3.1 → 2.3.3

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 (34) hide show
  1. package/dist/lib/api-client.js +2099 -648
  2. package/dist/lib/config.js +217 -201
  3. package/package.json +9 -5
  4. package/src/memory-cleanup.ts +2 -4
  5. package/dist/lib/__tests__/active-learning.test.js +0 -386
  6. package/dist/lib/__tests__/agent-performance-profiles.test.js +0 -325
  7. package/dist/lib/__tests__/auto-session.test.js +0 -661
  8. package/dist/lib/__tests__/context-assembly.test.js +0 -362
  9. package/dist/lib/__tests__/graph-expansion.test.js +0 -150
  10. package/dist/lib/__tests__/integration-memory-crud.test.js +0 -797
  11. package/dist/lib/__tests__/integration-memory-system.test.js +0 -281
  12. package/dist/lib/__tests__/lifecycle-maintenance.test.js +0 -207
  13. package/dist/lib/__tests__/pattern-detection.test.js +0 -295
  14. package/dist/lib/__tests__/prompt-builder.test.js +0 -418
  15. package/dist/lib/active-learning.js +0 -822
  16. package/dist/lib/auto-session.js +0 -214
  17. package/dist/lib/cli.js +0 -138
  18. package/dist/lib/consolidation.js +0 -303
  19. package/dist/lib/context-assembly.js +0 -884
  20. package/dist/lib/graph-expansion.js +0 -163
  21. package/dist/lib/http.js +0 -175
  22. package/dist/lib/index.js +0 -7
  23. package/dist/lib/lifecycle-maintenance.js +0 -88
  24. package/dist/lib/memory-cleanup.js +0 -455
  25. package/dist/lib/onboard.js +0 -36
  26. package/dist/lib/prompt-builder.js +0 -488
  27. package/dist/lib/remote.js +0 -166
  28. package/dist/lib/server.js +0 -3365
  29. package/dist/lib/skills.js +0 -593
  30. package/dist/lib/tui/agents.js +0 -116
  31. package/dist/lib/tui/docs.js +0 -744
  32. package/dist/lib/tui/setup.js +0 -934
  33. package/dist/lib/tui/theme.js +0 -95
  34. package/dist/lib/tui/writer.js +0 -200
@@ -1,281 +0,0 @@
1
- /**
2
- * Integration tests for the Intelligent Agent Memory System.
3
- *
4
- * These tests exercise the MCP tools end-to-end via the Harmony API.
5
- * Prerequisites:
6
- * 1. `supabase db push` (migration applied)
7
- * 2. MCP server configured with valid API key
8
- * 3. Active workspace and project set
9
- *
10
- * Run with: bun test packages/mcp-server/src/__tests__/integration-memory-system.test.ts
11
- *
12
- * These tests create real entities and clean them up afterwards.
13
- * If a test fails mid-way, you may need to manually delete leftover entities.
14
- */
15
- import { afterAll, describe, expect, test } from "bun:test";
16
- import { getClient } from "../api-client.js";
17
- import { getActiveProjectId, getActiveWorkspaceId, isConfigured, loadConfig, } from "../config.js";
18
- // Track entities created during tests for cleanup
19
- const createdEntityIds = [];
20
- // Skip all tests if MCP server isn't configured
21
- const configured = (() => {
22
- try {
23
- loadConfig();
24
- return isConfigured() && !!getActiveWorkspaceId();
25
- }
26
- catch {
27
- return false;
28
- }
29
- })();
30
- // Check if the tier migration has been applied by testing a create
31
- const migrationApplied = await (async () => {
32
- if (!configured)
33
- return false;
34
- try {
35
- const client = getClient();
36
- const result = await client.createMemoryEntity({
37
- workspace_id: getActiveWorkspaceId(),
38
- project_id: getActiveProjectId() || undefined,
39
- type: "context",
40
- scope: getActiveProjectId() ? "project" : "workspace",
41
- memory_tier: "draft",
42
- title: "[TEST] Migration check - delete me",
43
- content: "Checking if memory_tier column exists.",
44
- agent_identifier: "test-runner",
45
- });
46
- const entity = result.entity;
47
- const hasTier = entity.memory_tier === "draft";
48
- // Clean up probe entity
49
- if (entity.id) {
50
- await client.deleteMemoryEntity(entity.id).catch(() => { });
51
- }
52
- return hasTier;
53
- }
54
- catch {
55
- return false;
56
- }
57
- })();
58
- const describeIf = configured ? describe : describe.skip;
59
- const describeIfMigrated = configured && migrationApplied ? describe : describe.skip;
60
- // Cleanup: delete all test entities
61
- afterAll(async () => {
62
- if (!configured || createdEntityIds.length === 0)
63
- return;
64
- const client = getClient();
65
- for (const id of createdEntityIds) {
66
- try {
67
- await client.deleteMemoryEntity(id);
68
- }
69
- catch {
70
- // Entity may already be deleted
71
- }
72
- }
73
- });
74
- describeIfMigrated("Memory Tier System (integration)", () => {
75
- test("create entity with explicit tier", async () => {
76
- const client = getClient();
77
- const workspaceId = getActiveWorkspaceId();
78
- const projectId = getActiveProjectId() || undefined;
79
- const result = await client.createMemoryEntity({
80
- workspace_id: workspaceId,
81
- project_id: projectId,
82
- type: "context",
83
- scope: projectId ? "project" : "workspace",
84
- memory_tier: "draft",
85
- title: "[TEST] Draft memory tier test",
86
- content: "This is a test draft memory.",
87
- tags: ["test", "integration"],
88
- agent_identifier: "test-runner",
89
- });
90
- const entity = result.entity;
91
- expect(entity.id).toBeTruthy();
92
- expect(entity.memory_tier).toBe("draft");
93
- createdEntityIds.push(entity.id);
94
- });
95
- test("create entity with default tier based on type", async () => {
96
- const client = getClient();
97
- const workspaceId = getActiveWorkspaceId();
98
- const projectId = getActiveProjectId() || undefined;
99
- // 'lesson' type should default to 'episode' tier
100
- const result = await client.createMemoryEntity({
101
- workspace_id: workspaceId,
102
- project_id: projectId,
103
- type: "lesson",
104
- scope: projectId ? "project" : "workspace",
105
- title: "[TEST] Lesson tier default",
106
- content: "Testing that lessons default to episode tier.",
107
- tags: ["test", "integration"],
108
- agent_identifier: "test-runner",
109
- });
110
- const entity = result.entity;
111
- expect(entity.id).toBeTruthy();
112
- expect(entity.memory_tier).toBe("episode");
113
- createdEntityIds.push(entity.id);
114
- });
115
- test("create pattern defaults to reference tier", async () => {
116
- const client = getClient();
117
- const workspaceId = getActiveWorkspaceId();
118
- const projectId = getActiveProjectId() || undefined;
119
- const result = await client.createMemoryEntity({
120
- workspace_id: workspaceId,
121
- project_id: projectId,
122
- type: "pattern",
123
- scope: projectId ? "project" : "workspace",
124
- title: "[TEST] Pattern tier default",
125
- content: "Testing that patterns default to reference tier.",
126
- tags: ["test", "integration"],
127
- agent_identifier: "test-runner",
128
- });
129
- const entity = result.entity;
130
- expect(entity.memory_tier).toBe("reference");
131
- createdEntityIds.push(entity.id);
132
- });
133
- test("update entity tier via promote", async () => {
134
- const client = getClient();
135
- const workspaceId = getActiveWorkspaceId();
136
- const projectId = getActiveProjectId() || undefined;
137
- // Create a draft
138
- const createResult = await client.createMemoryEntity({
139
- workspace_id: workspaceId,
140
- project_id: projectId,
141
- type: "context",
142
- scope: projectId ? "project" : "workspace",
143
- memory_tier: "draft",
144
- title: "[TEST] Promotion test draft",
145
- content: "Draft that will be promoted.",
146
- tags: ["test", "integration"],
147
- agent_identifier: "test-runner",
148
- });
149
- const entityId = createResult.entity
150
- .id;
151
- createdEntityIds.push(entityId);
152
- // Promote to episode
153
- const updateResult = await client.updateMemoryEntity(entityId, {
154
- memory_tier: "episode",
155
- metadata: {
156
- promoted_from_tier: "draft",
157
- promotion_reason: "test promotion",
158
- promoted_at: new Date().toISOString(),
159
- },
160
- });
161
- const updated = updateResult.entity;
162
- expect(updated.memory_tier).toBe("episode");
163
- });
164
- });
165
- describeIf("Context Assembly (integration)", () => {
166
- test("search returns entities with tier info", async () => {
167
- const client = getClient();
168
- const workspaceId = getActiveWorkspaceId();
169
- const projectId = getActiveProjectId() || undefined;
170
- // Create a searchable entity
171
- const createResult = await client.createMemoryEntity({
172
- workspace_id: workspaceId,
173
- project_id: projectId,
174
- type: "solution",
175
- scope: projectId ? "project" : "workspace",
176
- memory_tier: "reference",
177
- title: "[TEST] Unique search term xyzzy42",
178
- content: "A solution for testing context assembly search.",
179
- confidence: 0.95,
180
- tags: ["test", "context-assembly"],
181
- agent_identifier: "test-runner",
182
- });
183
- const entityId = createResult.entity
184
- .id;
185
- createdEntityIds.push(entityId);
186
- // Search for it
187
- const searchResult = await client.searchMemoryEntities(workspaceId, "xyzzy42", { project_id: projectId, limit: 5 });
188
- expect(searchResult.entities.length).toBeGreaterThan(0);
189
- const found = searchResult.entities.find((e) => e.id === entityId);
190
- expect(found).toBeTruthy();
191
- expect(found.memory_tier).toBe("reference");
192
- });
193
- });
194
- describeIf("Graph Walk (integration)", () => {
195
- test("create entities with relations and traverse", async () => {
196
- const client = getClient();
197
- const workspaceId = getActiveWorkspaceId();
198
- const projectId = getActiveProjectId() || undefined;
199
- const scope = projectId ? "project" : "workspace";
200
- // Create two related entities
201
- const errorResult = await client.createMemoryEntity({
202
- workspace_id: workspaceId,
203
- project_id: projectId,
204
- type: "error",
205
- scope,
206
- memory_tier: "reference",
207
- title: "[TEST] Graph walk error",
208
- content: "An error for graph walk testing.",
209
- tags: ["test", "graph-walk"],
210
- agent_identifier: "test-runner",
211
- });
212
- const errorId = errorResult.entity
213
- .id;
214
- createdEntityIds.push(errorId);
215
- const solutionResult = await client.createMemoryEntity({
216
- workspace_id: workspaceId,
217
- project_id: projectId,
218
- type: "solution",
219
- scope,
220
- memory_tier: "reference",
221
- title: "[TEST] Graph walk solution",
222
- content: "A solution for graph walk testing.",
223
- tags: ["test", "graph-walk"],
224
- agent_identifier: "test-runner",
225
- });
226
- const solutionId = solutionResult.entity
227
- .id;
228
- createdEntityIds.push(solutionId);
229
- // Create relation
230
- const relResult = await client.createMemoryRelation({
231
- source_id: errorId,
232
- target_id: solutionId,
233
- relation_type: "resolved_by",
234
- confidence: 1.0,
235
- });
236
- expect(relResult.relation.id).toBeTruthy();
237
- // Verify relations via getRelatedEntities
238
- const related = await client.getRelatedEntities(errorId);
239
- expect(related.outgoing.length).toBeGreaterThan(0);
240
- // API returns relations with embedded target entity, not flat target_id
241
- const outRel = related.outgoing.find((r) => {
242
- const rel = r;
243
- const target = rel.target;
244
- return target?.id === solutionId || rel.target_id === solutionId;
245
- });
246
- expect(outRel).toBeTruthy();
247
- });
248
- });
249
- describeIf("Vault Index with Tiers (integration)", () => {
250
- test("vault index includes tier information", async () => {
251
- const client = getClient();
252
- const workspaceId = getActiveWorkspaceId();
253
- const projectId = getActiveProjectId() || undefined;
254
- let result;
255
- try {
256
- result = await client.getVaultIndex({
257
- workspace_id: workspaceId,
258
- project_id: projectId,
259
- limit: 10,
260
- });
261
- }
262
- catch (e) {
263
- // Endpoint may not be deployed yet - skip gracefully
264
- const msg = e instanceof Error ? e.message : String(e);
265
- if (msg.includes("Unknown endpoint")) {
266
- console.log("Skipping: vault index endpoint not deployed yet");
267
- return;
268
- }
269
- throw e;
270
- }
271
- expect(result.entities).toBeDefined();
272
- // If there are entities, check they have tier info
273
- if (result.entities.length > 0) {
274
- const first = result.entities[0];
275
- // memory_tier might be present if migration is applied
276
- if (first.memory_tier !== undefined) {
277
- expect(["draft", "episode", "reference"]).toContain(first.memory_tier);
278
- }
279
- }
280
- });
281
- });
@@ -1,207 +0,0 @@
1
- /**
2
- * Unit tests for automatic lifecycle maintenance.
3
- *
4
- * Run with: bun test packages/mcp-server/src/__tests__/lifecycle-maintenance.test.ts
5
- */
6
- import { describe, expect, mock, test } from "bun:test";
7
- import { runLifecycleMaintenance } from "../lifecycle-maintenance.js";
8
- function daysAgo(days) {
9
- return new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
10
- }
11
- function makeMockClient(entities) {
12
- const deletedIds = [];
13
- const updatedEntities = [];
14
- return {
15
- client: {
16
- listMemoryEntities: mock(async () => ({
17
- entities,
18
- count: entities.length,
19
- })),
20
- deleteMemoryEntity: mock(async (id) => {
21
- deletedIds.push(id);
22
- return { success: true };
23
- }),
24
- updateMemoryEntity: mock(async (id, updates) => {
25
- updatedEntities.push({ id, updates });
26
- return { entity: { id, ...updates } };
27
- }),
28
- },
29
- deletedIds,
30
- updatedEntities,
31
- };
32
- }
33
- describe("runLifecycleMaintenance", () => {
34
- test("archives low-confidence entities", async () => {
35
- const { client, deletedIds } = makeMockClient([
36
- {
37
- id: "low-conf-1",
38
- title: "Bad memory",
39
- memory_tier: "draft",
40
- confidence: 0.1,
41
- access_count: 0,
42
- last_accessed_at: daysAgo(5),
43
- created_at: daysAgo(10),
44
- },
45
- ]);
46
- const result = await runLifecycleMaintenance(client, "ws-1");
47
- expect(result.archived).toBe(1);
48
- expect(deletedIds).toContain("low-conf-1");
49
- });
50
- test("prunes stale drafts older than 30 days with low decay", async () => {
51
- const { client, deletedIds } = makeMockClient([
52
- {
53
- id: "stale-draft-1",
54
- title: "Old draft",
55
- memory_tier: "draft",
56
- confidence: 0.5,
57
- access_count: 0,
58
- last_accessed_at: daysAgo(35),
59
- created_at: daysAgo(35),
60
- },
61
- ]);
62
- const result = await runLifecycleMaintenance(client, "ws-1");
63
- expect(result.pruned).toBe(1);
64
- expect(deletedIds).toContain("stale-draft-1");
65
- });
66
- test("does NOT prune recent drafts", async () => {
67
- const { client, deletedIds } = makeMockClient([
68
- {
69
- id: "fresh-draft",
70
- title: "New draft",
71
- memory_tier: "draft",
72
- confidence: 0.5,
73
- access_count: 1,
74
- last_accessed_at: daysAgo(2),
75
- created_at: daysAgo(5),
76
- },
77
- ]);
78
- const result = await runLifecycleMaintenance(client, "ws-1");
79
- expect(result.pruned).toBe(0);
80
- expect(result.archived).toBe(0);
81
- expect(deletedIds).toHaveLength(0);
82
- });
83
- test("auto-promotes eligible draft to episode", async () => {
84
- const { client, updatedEntities } = makeMockClient([
85
- {
86
- id: "promote-me",
87
- title: "Well-used draft",
88
- memory_tier: "draft",
89
- confidence: 0.85,
90
- access_count: 6,
91
- last_accessed_at: daysAgo(0),
92
- created_at: daysAgo(3),
93
- },
94
- ]);
95
- const result = await runLifecycleMaintenance(client, "ws-1");
96
- expect(result.promoted).toBe(1);
97
- expect(updatedEntities[0].id).toBe("promote-me");
98
- expect(updatedEntities[0].updates.memory_tier).toBe("episode");
99
- });
100
- test("auto-promotes eligible episode to reference", async () => {
101
- const { client, updatedEntities } = makeMockClient([
102
- {
103
- id: "promote-ep",
104
- title: "Proven episode",
105
- memory_tier: "episode",
106
- confidence: 0.95,
107
- access_count: 12,
108
- last_accessed_at: daysAgo(1),
109
- created_at: daysAgo(10),
110
- },
111
- ]);
112
- const result = await runLifecycleMaintenance(client, "ws-1");
113
- expect(result.promoted).toBe(1);
114
- expect(updatedEntities[0].updates.memory_tier).toBe("reference");
115
- });
116
- test("flags stale entities for review", async () => {
117
- const { client, updatedEntities } = makeMockClient([
118
- {
119
- id: "stale-ep",
120
- title: "Forgotten episode",
121
- memory_tier: "episode",
122
- confidence: 0.6,
123
- access_count: 2,
124
- last_accessed_at: daysAgo(100),
125
- created_at: daysAgo(120),
126
- },
127
- ]);
128
- const result = await runLifecycleMaintenance(client, "ws-1");
129
- expect(result.reviewed).toBe(1);
130
- expect(updatedEntities[0].id).toBe("stale-ep");
131
- expect(updatedEntities[0].updates.metadata).toHaveProperty("needs_review", true);
132
- });
133
- test("does not flag frequently accessed stale entities", async () => {
134
- const { client, updatedEntities } = makeMockClient([
135
- {
136
- id: "old-but-used",
137
- title: "Still relevant",
138
- memory_tier: "episode",
139
- confidence: 0.7,
140
- access_count: 10,
141
- last_accessed_at: daysAgo(100),
142
- created_at: daysAgo(120),
143
- },
144
- ]);
145
- const result = await runLifecycleMaintenance(client, "ws-1");
146
- expect(result.reviewed).toBe(0);
147
- expect(updatedEntities).toHaveLength(0);
148
- });
149
- test("handles empty entity list", async () => {
150
- const { client } = makeMockClient([]);
151
- const result = await runLifecycleMaintenance(client, "ws-1");
152
- expect(result.archived).toBe(0);
153
- expect(result.pruned).toBe(0);
154
- expect(result.promoted).toBe(0);
155
- expect(result.reviewed).toBe(0);
156
- });
157
- test("handles API errors gracefully", async () => {
158
- const client = {
159
- listMemoryEntities: mock(async () => {
160
- throw new Error("API down");
161
- }),
162
- };
163
- const result = await runLifecycleMaintenance(client, "ws-1");
164
- expect(result.errors).toBe(0); // Early return, no entity-level errors
165
- expect(result.archived).toBe(0);
166
- });
167
- test("processes mixed entities correctly", async () => {
168
- const { client, deletedIds, updatedEntities } = makeMockClient([
169
- // Should be archived (low confidence)
170
- {
171
- id: "archive-me",
172
- title: "Low conf",
173
- memory_tier: "draft",
174
- confidence: 0.2,
175
- access_count: 1,
176
- last_accessed_at: daysAgo(5),
177
- created_at: daysAgo(10),
178
- },
179
- // Should be promoted (draft→episode)
180
- {
181
- id: "promote-me",
182
- title: "Good draft",
183
- memory_tier: "draft",
184
- confidence: 0.85,
185
- access_count: 7,
186
- last_accessed_at: daysAgo(0),
187
- created_at: daysAgo(5),
188
- },
189
- // Should be left alone (healthy reference)
190
- {
191
- id: "leave-me",
192
- title: "Healthy ref",
193
- memory_tier: "reference",
194
- confidence: 0.95,
195
- access_count: 20,
196
- last_accessed_at: daysAgo(1),
197
- created_at: daysAgo(60),
198
- },
199
- ]);
200
- const result = await runLifecycleMaintenance(client, "ws-1");
201
- expect(result.archived).toBe(1);
202
- expect(result.promoted).toBe(1);
203
- expect(deletedIds).toContain("archive-me");
204
- expect(updatedEntities[0].id).toBe("promote-me");
205
- expect(updatedEntities[0].updates.memory_tier).toBe("episode");
206
- });
207
- });