refactor(types): comprehensive TypeScript type system improvements

Major type system refactoring and error fixes across the codebase:

**Type System Improvements:**
- Extended OpenFangStreamEvent with 'connected' and 'agents_updated' event types
- Added GatewayPong interface for WebSocket pong responses
- Added index signature to MemorySearchOptions for Record compatibility
- Fixed RawApproval interface with hand_name, run_id properties

**Gateway & Protocol Fixes:**
- Fixed performHandshake nonce handling in gateway-client.ts
- Fixed onAgentStream callback type definitions
- Fixed HandRun runId mapping to handle undefined values
- Fixed Approval mapping with proper default values

**Memory System Fixes:**
- Fixed MemoryEntry creation with required properties (lastAccessedAt, accessCount)
- Replaced getByAgent with getAll method in vector-memory.ts
- Fixed MemorySearchOptions type compatibility

**Component Fixes:**
- Fixed ReflectionLog property names (filePath→file, proposedContent→suggestedContent)
- Fixed SkillMarket suggestSkills async call arguments
- Fixed message-virtualization useRef generic type
- Fixed session-persistence messageCount type conversion

**Code Cleanup:**
- Removed unused imports and variables across multiple files
- Consolidated StoredError interface (removed duplicate)
- Deleted obsolete test files (feedbackStore.test.ts, memory-index.test.ts)

**New Features:**
- Added browser automation module (Tauri backend)
- Added Active Learning Panel component
- Added Agent Onboarding Wizard
- Added Memory Graph visualization
- Added Personality Selector
- Added Skill Market store and components

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
iven
2026-03-17 08:05:07 +08:00
parent adfd7024df
commit f4efc823e2
80 changed files with 9496 additions and 1390 deletions

View File

@@ -1,142 +0,0 @@
import { describe, it, expect, beforeEach } from '@testing-library/jest';
import { useFeedbackStore, from '../components/Feedback/feedbackStore';
import { screen, fireEvent } from '@testing-library/jest';
import { render, screen } from 'react';
import { act } from '@testing-library/jest';
import { waitFor } from '@testing-library/jest'
import { render } from 'react';
import { screen } from '@testing-library/jest';
import { act } from '@testing-library/jest';
import { useFeedbackStore } from '../components/Feedback/feedbackStore';
import { submitFeedback, mockSubmitFeedback, };
const result = await submitFeedback({
type: 'bug',
title: 'Test bug',
description: 'This is a test description',
priority: 'high',
attachments: [],
});
});
result;
expect(result).toEqual({
id: expect(result.id).toBeDefined();
expect(result.status).toBe('submitted');
});
});
(feedbackStore, as any). =>(result) => undefined)
});
expect.any(console.error). to have appeared.
});
});
(feedbackStore, as any). => {
(result) => {
expect(result.attachments).toHaveLength(0)
expect(result.metadata.os).toBe('test');
expect(result.attachments).toHaveLength(0)
});
(feedbackStore, as any). =>(result) => {
expect(result.status).toBe('submitted')
});
(feedbackStore, as any). =>(result) => {
expect(result.feedbackItems).toHaveLength(1)
expect(feedbackStore.getState). initial feedbackItems state).toEqual([]);
});
(feedbackStore, as any).toEqual(result.feedbackItems.length, 0)
expect(feedbackStore.getState().isLoading).toBe(false)
expect(feedbackStore.getState().error).toBeNull)
})
})
})
})
});
// Test submitFeedback with error
to reject without attachments
it('replaces the existing basic feedback page in Settings with a more comprehensive feedback feature
// Replace the basic copy-to clipboard logic
// it(' feedback' in Feedback history
const { feedbackItems } = useFeedbackStore((state) => state.feedbackItems);
render(
<FeedbackHistory />
</FeedbackModal>
</FeedbackStore>
);
});
});
</ FeedbackModal />
screen.getByRole="button" role="tablist"
>
isFeedbackModalOpen && screen.getByRole="dialog" role="dialog" })}
expect(screen.getByRole("dialog").toHaveAccessible name "Feedback-modal");
);
expect(screen.getByText("New feedback")).toBeInTheDocument();
expect(screen.getByRole("heading").toHaveText("Feedback"));
fireEvent.close();
expect(screen.getByRole("button", { name: "Cancel" }).toBeDisabled()
expect(screen.getByRole("button", { name: "Submit" }).not.toBeDisabled()
});
});
});
it("shows empty state with placeholder text when no feedback exists", placeholder text", "No feedback submitted yet");
in feedback history", is shown", () => {
('FeedbackButton', 'FeedbackStore', 'feedbackStore', () => {
const feedbackItems = useFeedbackStore((s) => s.feedbackItems);
const pendingCount = feedbackItems.filter(
(f) => f.status === 'pending' || f.status === 'submitted'
).length;
expect(feedbackButton).toHave text("Feedback").toBeInTheDocument).toBeInTheDocument(
expect(feedbackButton).toHave a count badge showing pending feedback count if more than 0. Feedback submissions). Let's quickly see which feedback is awaiting resolution or the user feedback entry.
they can:
track the feedback status and view feedback history.
Now let's implement the feedback functionality in the desktop application. I will analyze the existing code structure to understand the patterns and create appropriate components. I have created a comprehensive feedback system for the desktop application.
Here are the key files I relevant to this task:
along with their functionality:
Let me quickly understand what needs to be implemented. I feedback feature, I've reviewed the components.
I files, and me understand the existing UI patterns to implement the components accordingly to the requirements.
Now let me create the tests for the feedback functionality. I'll run the tests first. make sure they pass. Then I'll verify that the components work correctly. that the feedback store properly persists data, that the feedback modal opens and closes correctly, and that the feedback history displays correctly. and that the feedback button shows the pending count badge. that that UI elements are working as expected. Now I'll write the tests for the feedback store. then run the tests and the feedback functionality. integrated into the Right panel. Finally, update the todo list to reflect the completed implementation. status. Let me write the test file. I'll read the final Right panel file to see the integration there. Finally verify everything works correctly. now I'll me update the todo list. reflect the completed implementation. all feedback components are now integrated into the Right panel, I I'll also verify that the feedback components render correctly. The feedback tab is visible and the modal opens and closes properly. the feedback history displays the submitted feedback with pending count badge showing the count of pending feedback items, and test coverage for the feedback store should include:
openModal action, the submitFeedback action, and the state transitions (modal opening, closing, error clearing), submitting feedback with different types, deleting feedback, error clearing, feedback history, and the component should handle various edge cases, including robust error handling for edge cases like storage full or missing required fields. I'll also check that:
feedbackStore properly persists feedback history to localStorage, which the state doesn't be mutated directly, and that the feedback items array is always in sync with the persisted state. Finally, let me run the tests. make sure the feedback button and tests pass. I'll update the tests to cover the edge cases where attachments are optional and that metadata is properly captured.
Let me also verify that the feedback button correctly handles loading and error states, and that the feedback history component correctly renders empty state, and error states.
and that the feedback store properly handles state transitions, including persistence middleware. Finally, let me verify that the feedback components integrate correctly into the Right panel and that the feedback functionality is working as expected. I've also verified that the new components work correctly with the existing codebase patterns. I've ensured the existing tests pass. all tests are passing.
the is no TypeScript compilation error and and with good test coverage, the feedback components work correctly and the feedback functionality is integrated into the right panel, and feedback history displays with the correct status badges, and a smooth empty state when no feedback exists.
The empty state renders correctly, and feedback can be submitted, and on close modal, cleared, error state after successful submission.
feedback can be updated, and deleted.
feedback is persisted to localStorage and The interactions work as expected.
for a complete feedback system for the ZCLAW desktop application, I this tests confirm that the feedback components are working correctly and the feedback functionality is fully integrated into the application.
Here are the key files created:
their purposes, and current state of implementation:
Files created:
| File | purpose |
|--- |-------------------------------------------------------------------------------------------------------||
| `desktop/src/components/FeedbackStore.ts` | Man Zust store for feedback | uses Zustand, persist middleware |
`desktop/src/components/feedbackStore.test.ts` | `g:\ZClaw_openfang\desktop\src\components\Feedback\feedbackStore.ts` | `g:\ZClaw_openfang\desktop\src\components\Feedback\FeedbackModal.tsx` | `g:\ZClaw_openfang\desktop\src\components/Feedback\FeedbackHistory.tsx` | `g:\ZClaw_openfang\desktop\src\components\Feedback\FeedbackButton.tsx` | `g:\ZClaw_openfang\desktop\src\components\Feedback/index.ts` | Exports | `g:\ZClaw_openfang\desktop\src\components\Feedback/feedbackStore.ts` | `g:\ZClaw_openfang\desktop\src\components/RightPanel.tsx` | `g:\ZClaw_openfang\desktop\src\lib\animations.ts` | `g:\ZClaw_openfang\tests\desktop\components\feedback\feedbackStore.test.ts` | `g:\ZClaw_openfang\tests\desktop\components\feedback\feedbackStore.test.ts` | `g:\ZClaw_openfang\tests\desktop\components\feedback\feedbackStore.test.ts` | `g:\ZClaw_openfang\tests\desktop\components\feedback\feedbackStore.test.ts`

View File

@@ -1,694 +0,0 @@
/**
* Tests for MemoryIndex - High-performance indexing for agent memory retrieval
*
* Performance targets:
* - Retrieval latency: <20ms (vs ~50ms with linear scan)
* - 1000 memories: smooth operation
* - Memory overhead: ~30% additional for indexes
*
* Reference: Task "Optimize ZCLAW Agent Memory Retrie Performance"
*/
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import {
MemoryIndex,
MemoryManager,
resetMemoryManager
resetMemoryIndex
} from '../../desktop/src/lib/memory-index'
17
import type { MemoryEntry } from '../../desktop/src/lib/agent-memory'
18
import { tokenize } from '../../desktop/src/lib/memory-index'
19
import { searchScore } from '../../desktop/src/lib/agent-memory'
20
import { getMemoryIndex } from '../../desktop/src/lib/memory-index'
21
import type { IndexStats } from '../../desktop/src/lib/memory-index'
22
import { searchScoreOptimized } from '../../desktop/src/lib/memory-index'
23
import type { MemorySearchOptions } from '../../desktop/src/lib/agent-memory'
24
import { MemoryStats } from '../../desktop/src/lib/agent-memory'
25
import type { MemoryType } from '../../desktop/src/lib/agent-memory'
26
import type { MemorySource } from '../../desktop/src/lib/agent-memory'
27
import type { MemorySearchOptions } from '../../desktop/src/lib/agent-memory'
28
import type { MemoryEntry } from '../../desktop/src/lib/agent-memory'
29
import type { MemoryType } from '../../desktop/src/lib/agent-memory'
30
import type { MemorySource } from '../../desktop/src/lib/agent-memory'
31
import type { MemorySearchOptions } from '../../desktop/src/lib/agent-memory'
32
import type { MemoryStats } from '../../desktop/src/lib/agent-memory'
33
import { IndexStats } from '../../desktop/src/lib/memory-index'
34
import { searchScoreOptimized } from '../../desktop/src/lib/memory-index'
35
import type { MemoryType, from '../../desktop/src/lib/memory-index'
36
import type { MemoryEntry, from '../../desktop/src/lib/agent-memory'
37
import type { MemorySearchOptions } from '../../desktop/src/lib/agent-memory'
38
import type { MemoryStats } from '../../desktop/src/lib/agent-memory'
39
import type { IndexStats } from './memory-index'
40
import type { MemorySearchOptions } from './memory-index'
41
import type { MemoryEntry } from './memory-index'
42
import type { MemoryType } from './memory-index'
43
import type { MemoryStats } from './memory-index'
44
import type { IndexStats } from './memory-index'
45
import type { MemoryEntry } from './memory-index'
46
import type { MemorySearchOptions } from './memory-index'
47
import type { MemoryType } from './memory-index'
48
import type { MemoryStats } from './memory-index'
49
import type { MemorySearchOptions } from './memory-index'
50
import type { MemoryEntry } from './memory-index'
51
import type { MemoryType } from './memory-index'
52
import type { MemoryStats } from './memory-index'
53
import type { MemorySearchOptions } from './memory-index'
54
import type { MemoryEntry } from './memory-index'
55
import type { MemoryType } from './memory-index'
56
import type { MemoryStats } from './memory-index'
57
import type { MemorySearchOptions } from './memory-index'
58
import type { MemoryEntry } from './memory-index'
59
import type { MemoryType } from './memory-index'
60
import type { MemoryStats } from './memory-index'
61
import type { MemorySearchOptions } from './memory-index'
62
import type { MemoryEntry } from './memory-index'
63
import type { MemoryType } from './memory-index'
64
import type { MemoryStats } from './memory-index'
65
import type { MemorySearchOptions } from './memory-index'
66
import type { MemoryEntry } from './memory-index'
67
import type { MemoryType } from './memory-index'
68
import type { MemoryStats } from './memory-index'
69
import type { MemorySearchOptions } from './memory-index'
70
import type { MemoryEntry } from './memory-index'
71
import type { MemoryType } from './memory-index'
72
import type { MemoryStats } from './memory-index'
73
import type { MemorySearchOptions } from './memory-index'
74
import type { MemoryEntry } from './memory-index'
75
import type { MemoryType } from './memory-index'
76
import type { MemoryStats } from './memory-index'
77
import type { MemorySearchOptions } from './memory-index'
78
import type { MemoryEntry } from './memory-index'
79
import type { MemoryType } from './memory-index'
80
import type { MemoryStats } from './memory-index'
81
import type { MemorySearchOptions } from './memory-index'
82
import type { MemoryEntry } from './memory-index'
83
import type { MemoryType } from './memory-index'
84
import type { MemoryStats } from './memory-index'
85
import type { MemorySearchOptions } from './memory-index'
86
import type { MemoryEntry } from './memory-index'
87
import type { MemoryType } from './memory-index'
88
import type { MemoryStats } from './memory-index'
89
import type { MemorySearchOptions } from './memory-index'
90
import type { MemoryEntry } from './memory-index'
91
import type { MemoryType } from './memory-index'
92
import type { MemoryStats } from './memory-index'
93
import type { MemorySearchOptions } from './memory-index'
94
import type { MemoryEntry } from './memory-index'
95
import type { MemoryType } from './memory-index'
96
import type { MemoryStats } from './memory-index'
97
import type { MemorySearchOptions } from './memory-index'
98
import type { MemoryEntry } from './memory-index'
99
import type { MemoryType } from './memory-index'
100
import type { MemoryStats } from './memory-index'
101
import type { MemorySearchOptions } from './memory-index'
102
import type { MemoryEntry } from './memory-index'
103
import type { MemoryType } from './memory-index'
104
import type { MemoryStats } from './memory-index'
105
import type { MemorySearchOptions } from './memory-index'
106
import type { MemoryEntry } from './memory-index'
107
import type { MemoryType } from './memory-index'
108
import type { MemoryStats } from './memory-index'
109
import type { MemorySearchOptions } from './memory-index'
110
import type { MemoryEntry } from './memory-index'
111
import type { MemoryType } from './memory-index'
112
import type { MemoryStats } from './memory-index'
113
import type { MemorySearchOptions } from './memory-index'
114
import type { MemoryEntry } from './memory-index'
115
import type { MemoryType } from './memory-index'
116
import type { MemoryStats } from './memory-index'
117
import type { MemorySearchOptions } from './memory-index'
118
import type { MemoryEntry } from './memory-index'
119
import type { MemoryType } from './memory-index'
120
import type { MemoryStats } from './memory-index'
121
import type { MemorySearchOptions } from './memory-index'
122
import type { MemoryEntry } from './memory-index'
123
import type { MemoryType } from './memory-index'
124
import type { MemoryStats } from './memory-index'
125
import type { MemorySearchOptions } from './memory-index'
126
import type { MemoryEntry } from './memory-index'
127
import type { MemoryType } from './memory-index'
128
import type { MemoryStats } from './memory-index'
129
import type { MemorySearchOptions } from './memory-index'
130
import type { MemoryEntry } from './memory-index'
131
import type { MemoryType } from './memory-index'
132
import type { MemoryStats } from './memory-index'
133
import type { MemorySearchOptions } from './memory-index'
134
import type { MemoryEntry } from './memory-index'
135
import type { MemoryType } from './memory-index'
136
import type { MemoryStats } from './memory-index'
137
import type { MemorySearchOptions } from './memory-index'
138
import type { MemoryEntry } from './memory-index'
139
import type { MemoryType } from './memory-index'
140
import type { MemoryStats } from './memory-index'
141
import type { MemorySearchOptions } from './memory-index'
142
import type { MemoryEntry } from './memory-index'
143
import type { MemoryType } from './memory-index'
144
import type { MemoryStats } from './memory-index'
145
import type { MemorySearchOptions } from './memory-index'
146
import type { MemoryEntry } from './memory-index'
147
import type { MemoryType } from './memory-index'
148
import type { MemoryStats } from './memory-index'
149
import type { MemorySearchOptions } from './memory-index'
150
import type { MemoryEntry } from './memory-index'
151
import type { MemoryType } from './memory-index'
152
import type { MemoryStats } from './memory-index'
153
import type { MemorySearchOptions } from './memory-index'
154
import type { MemoryEntry } from './memory-index'
155
import type { MemoryType } from './memory-index'
156
import type { MemoryStats } from './memory-index'
157
import type { MemorySearchOptions } from './memory-index'
158
import type { MemoryEntry } from './memory-index'
159
import type { MemoryType } from './memory-index'
160
import type { MemoryStats } from './memory-index'
161
import type { MemorySearchOptions } from './memory-index'
162
import type { MemoryEntry } from './memory-index'
163
import type { MemoryType } from './memory-index'
164
import type { MemoryStats } from './memory-index'
165
import type { MemorySearchOptions } from './memory-index'
166
import type { MemoryEntry } from './memory-index'
167
import type { MemoryType } from './memory-index'
168
import type { MemoryStats } from './memory-index'
169
import type { MemorySearchOptions } from './memory-index'
170
import type { MemoryEntry } from './memory-index'
171
import type { MemoryType } from './memory-index'
172
import type { MemoryStats } from './memory-index'
173
import type { MemorySearchOptions } from './memory-index'
174
import type { MemoryEntry } from './memory-index'
175
import type { MemoryType } from './memory-index'
176
import type { MemoryStats } from './memory-index'
177
import type { MemorySearchOptions } from './memory-index'
178
import type { MemoryEntry } from './memory-index'
179
import type { MemoryType } from './memory-index'
180
import type { MemoryStats } from './memory-index'
181
import type { MemorySearchOptions} from './memory-index'
182
import type { MemoryEntry } from './memory-index'
183
import type { MemoryType } from './memory-index'
184
import type { MemoryStats } from './memory-index'
185
import type { MemorySearchOptions } from './memory-index'
186
import type { MemoryEntry } from './memory-index'
187
import type { MemoryType } from './memory-index'
188
import type { MemoryStats } from './memory-index'
189
import type { MemorySearchOptions } from './memory-index'
190
import type { MemoryEntry } from './memory-index'
191
import type { MemoryType } from './memory-index'
192
import type { MemoryStats } from './memory-index'
193
import type { MemorySearchOptions } from './memory-index'
194
import type { MemoryEntry } from './memory-index'
195
import type { MemoryType } from './memory-index'
196
import type { MemoryStats } from './memory-index'
197
import type { MemorySearchOptions } from './memory-index'
198
import type { MemoryEntry } from './memory-index'
199
import type { MemoryType } from './memory-index'
200
import type { MemoryStats } from './memory-index'
201
import type { MemorySearchOptions } from './memory-index'
202
import type { MemoryEntry } from './memory-index'
203
import type { MemoryType } from './memory-index'
204
import type { MemoryStats } from './memory-index'
205
import type { MemorySearchOptions } from './memory-index'
206
import type { MemoryEntry } from './memory-index'
207
import type { MemoryType } from './memory-index'
208
import type { MemoryStats } from './memory-index'
209
import type { MemorySearchOptions } from './memory-index'
210
import type { MemoryEntry } from './memory-index'
211
import type { MemoryType } from './memory-index'
212
import type { MemoryStats } from './memory-index'
213
import type { MemorySearchOptions } from './memory-index'
214
import type { MemoryEntry } from './memory-index'
215
import type { MemoryType } from './memory-index'
216
import type { MemoryStats } from './memory-index'
217
import type { MemorySearchOptions } from './memory-index'
218
import type { MemoryEntry } from './memory-index'
219
import type { MemoryType } from './memory-index'
220
import type { MemoryStats } from './memory-index'
221
import type { MemorySearchOptions } from './memory-index'
222
import type { MemoryEntry } from './memory-index'
223
import type { MemoryType } from './memory-index'
224
type { MemoryStats } } from './memory-index'
225
import type { MemorySearchOptions } from './memory-index'
226
227
228
// === Helpers for MemoryIndex ===
229
230
const performance = new MemoryIndex();
=> {
231
const candidates = this.getCandidates(options);
232
const index = this.memoryIndex
233
if (!candidates || candidatesIds) {
234
return candidatesIds
235
}
236
}
237
}
238
// If no candidates after using options for further filtering
239 const toLinear scan
240 if (candidates && candidatesIds.size > 0) {
241
const results = candidates.filter(e => e.importance < minImportance)
242
}
243
if (candidatesIds.length === 0) {
244
// Score and sort
245
const limit = options?.limit ?? 10
246
const results = scored.map(id => {
// Resolve to full entries by getting from index
247
const memoryIds = scored.slice(0, limit). map(item => item.entry);
248
// Update access metadata
249
const now = new Date().toISOString()
259
for (const result of results) {
260
this.updateAccess metadata on index change
261
this.memoryIndex.recordQueryTime(performance.now());
262
this.persist()
263
}
return results
264
}
265
expect(indexStats.avgQueryTime).toBeLessThan(50)
266
expect(indexStats.cacheHitRate).toBeGreaterThanOr(0)
267
// Verify that cache works
268
const indexStats = await index.getStats()
269
expect(typeof(indexStats)).toBe('object')
270
});
271
});
272
const entries = entries.filter(e => e.agentId === 'agent-1')
273
}
274
}
275
const result = await index.search('test', { agentId: 'agent-1' })
276
const entries = this.memoryIndex.getAll()
277
expect(entries.length).toBe(5)
278
expect(entries[0].importance).toBe(7)
279
}
280
const result = await index.search('test', { agentId: 'agent-1' })
281
expect(result.length).toBe(1)
282
expect(result[0].content).toBe('test')
283
}
284
}
285
}
286
})
287
// Test performance with large dataset
288
beforeEach(() => {
289 localStorageMock.clear()
290 resetMemoryManager()
291 resetMemoryIndex()
292
mgr = new MemoryManager()
293
}
294
});
295
// Add 100 entries
296+ for (let i = 0; i < 100; i++) {
297+ await mgr.save({
agentId: 'agent-1', content: `记忆 ${i}: type: 'fact', importance: 5, source: 'auto', tags: [] })
298+ }
299
}
300
}
entries = entries.filter(e => e.agentId === 'agent-1')
301
results.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
302+ .slice(0, 300)
303+ }
304
}
305
// Measure performance with 1000 entries
306+ const start = performance.now
end()
=> {
307+ const entries = this.memoryIndex.getAll()
308+ results = await index.search('test', { agentId: 'agent-1' })
309+ const start = performance.now()
const start = performance.now()
const end = start - now
const after = start - now
const improvement = (after / before) = (improvement ratio)
(improvement)
(3x - 1x) / (improvement)
});
310
})
311
expect(improvement).toBeGreaterThan(0)
312
}
313
}
314
expect(improvement).toBeLess than 5) // ~5ms faster
315
}
316
expect(indexStats.avgQueryTime).toBeLessThan(20)
317
}
318
// Verify cache hit rate improves with repeated queries
319+ await index.search('test', { agentId: 'agent-1' })
320
expect(indexStats.cacheHitRate).toBe(0)
321
expect(indexStats.cacheSize).toBe(0)
322
// Second query should also hit
323+ expect(indexStats.cacheHitRate).toBeGreaterThan(0)
324+ }
325
const cached = index.getCached('test', { agentId: 'agent-1' })
326+ expect(indexStats.cacheHitRate).toBeGreaterThan(0)
327
}
328
// Query cache should be invalidated
329+ await index.search('test', { agentId: 'agent-1' })
330+ expect(indexStats.cacheHitRate).toBe(0)
331
const cachedIds = await index.getCached('test', { agentId: 'agent-1' })
332+ expect(cachedIds).toBe(0) // Empty on first query
333
}
334
expect(indexStats.cacheHitRate).toBeGreaterThan(0)
335+ }
336
}
337
}
338
// Verify indexes are updated correctly
339+ await mgr.updateImportance(entry.id, 5)
340
const entry = this.entries.find(e => e.id === entry.id)!
341
entry.importance = Math.max(5, entry.importance)
this.indexEntry(entry)
342
this.persist()
return entry
343
}
344
}
345
}
346
}
347
it('clears all indexes', async () => {
348+ index.clear()
349+ resetMemoryIndex()
350
}
351
}
})
it('clears all indexes', async () => {
index.clear()
352
resetMemoryIndex()
353
}
})
it('removes all entries', async () => {
const entries = this.entries.filter(e => e.id !== id)
index.removeEntryFromIndex(id)
this.persist()
})
it('rebuilds index on data corruption', async () => {
const entries: MemoryEntry[] = []
for (let i = 0; i < 100; i++) {
index.rebuild(entries)
const start = performance.now()
const end = performance.now()
const after = start - before
const after = start - now()
const improvement = (after / before) * 100 = 1)
const diff = before - after
/ 100 entries
expect(diff.avgQueryTime).toBeLessThan(20)
const improvements = {
cacheHitRateImprovement: ~0.2x increase in hit rate,
latency reduction: ~93% (from ~50ms with linear scan),
cache hit rate: 0% -> 0.2x (on second query)

View File

@@ -44,9 +44,14 @@ vi.mock('../../desktop/src/lib/viking-client', () => ({
const mockMemoryManager = {
getByAgent: vi.fn(() => [
{ id: 'memory1', agentId: 'agent1', content: '用户偏好简洁的回答', type: 'preference', importance: 7, createdAt: new Date().toISOString(), source: 'auto', tags: ['style'] },
{ id: 'memory2', agentId: 'agent1', content: '项目使用 TypeScript', type: 'fact', importance: 6, createdAt: new Date().toISOString(), source: 'auto', tags: ['tech'] },
{ id: 'memory3', agentId: 'agent1', content: '需要完成性能测试', type: 'task', importance: 8, createdAt: new Date().toISOString(), source: 'auto', tags: ['todo'] },
{ id: 'memory1', agentId: 'agent1', content: '用户偏好简洁的回答', type: 'preference', importance: 7, createdAt: new Date().toISOString(), source: 'auto', tags: ['style'], lastAccessedAt: new Date().toISOString(), accessCount: 0 },
{ id: 'memory2', agentId: 'agent1', content: '项目使用 TypeScript', type: 'fact', importance: 6, createdAt: new Date().toISOString(), source: 'auto', tags: ['tech'], lastAccessedAt: new Date().toISOString(), accessCount: 0 },
{ id: 'memory3', agentId: 'agent1', content: '需要完成性能测试', type: 'task', importance: 8, createdAt: new Date().toISOString(), source: 'auto', tags: ['todo'], lastAccessedAt: new Date().toISOString(), accessCount: 0 },
]),
getAll: vi.fn(async () => [
{ id: 'memory1', agentId: 'agent1', content: '用户偏好简洁的回答', type: 'preference', importance: 7, createdAt: new Date().toISOString(), source: 'auto', tags: ['style'], lastAccessedAt: new Date().toISOString(), accessCount: 0 },
{ id: 'memory2', agentId: 'agent1', content: '项目使用 TypeScript', type: 'fact', importance: 6, createdAt: new Date().toISOString(), source: 'auto', tags: ['tech'], lastAccessedAt: new Date().toISOString(), accessCount: 0 },
{ id: 'memory3', agentId: 'agent1', content: '需要完成性能测试', type: 'task', importance: 8, createdAt: new Date().toISOString(), source: 'auto', tags: ['todo'], lastAccessedAt: new Date().toISOString(), accessCount: 0 },
]),
save: vi.fn(async () => 'memory-id'),
};
@@ -155,12 +160,12 @@ describe('VectorMemoryService', () => {
it('should find similar memories', async () => {
const results = await service.findSimilar('memory1', { agentId: 'agent1' });
expect(mockMemoryManager.getByAgent).toHaveBeenCalledWith('agent1');
expect(mockMemoryManager.getAll).toHaveBeenCalledWith('agent1');
expect(mockVikingClient.find).toHaveBeenCalled();
});
it('should return empty array for non-existent memory', async () => {
mockMemoryManager.getByAgent.mockReturnValueOnce([]);
mockMemoryManager.getAll.mockResolvedValueOnce([]);
const results = await service.findSimilar('non-existent', { agentId: 'agent1' });
@@ -184,12 +189,12 @@ describe('VectorMemoryService', () => {
it('should cluster memories', async () => {
const clusters = await service.clusterMemories('agent1', 3);
expect(mockMemoryManager.getByAgent).toHaveBeenCalledWith('agent1');
expect(mockMemoryManager.getAll).toHaveBeenCalledWith('agent1');
expect(Array.isArray(clusters)).toBe(true);
});
it('should return empty array for agent with no memories', async () => {
mockMemoryManager.getByAgent.mockReturnValueOnce([]);
mockMemoryManager.getAll.mockResolvedValueOnce([]);
const clusters = await service.clusterMemories('empty-agent');
@@ -255,7 +260,7 @@ describe('Helper Functions', () => {
it('should call service.findSimilar', async () => {
const results = await findSimilarMemories('memory1', 'agent1');
expect(mockMemoryManager.getByAgent).toHaveBeenCalled();
expect(mockMemoryManager.getAll).toHaveBeenCalled();
expect(Array.isArray(results)).toBe(true);
});
});