feat: Add memory service and code indexer (Phase 9)#62
Open
Finfinder wants to merge 7 commits into
Open
Conversation
| } | ||
|
|
||
| function extractMarkdownTags(content: string): string[] { | ||
| const match = /(?:^|\n)\s*tags\s*:\s*([^\n\r;]{1,120})/i.exec(content); |
There was a problem hiding this comment.
Pull request overview
This PR introduces Phase 9 “agent memory + code retrieval” capabilities by adding two new packages—@agentdeck/memory-service and @agentdeck/code-indexer—and wiring them into the desktop app via new IPC channels, preload API additions, and a UI dialog for memory-conflict review. It also expands test coverage and updates build/test configuration to support the new modules (including a dedicated migrations test config).
Changes:
- Added
@agentdeck/memory-service(memory read/list, propose/apply edits, conflict reporting, redaction, and a SQLite-backed local store). - Added
@agentdeck/code-indexer(tree-sitter-based chunking, indexing, retrieval, and rebuild support). - Added Phase 9 IPC channels/handlers, preload API wiring (partial), UI
MemoryReviewDialog, and multiple new unit tests + Vitest config adjustments.
Reviewed changes
Copilot reviewed 48 out of 50 changed files in this pull request and generated 32 comments.
Show a summary per file
| File | Description |
|---|---|
| vitest.migrations.config.ts | Adds a dedicated Vitest config for running SQLite migrations tests in Node. |
| vitest.config.ts | Updates unit-test config with new aliases, mocks, exclusions, and coverage settings for Phase 9. |
| tsconfig.json | Adds TS project references for the new packages. |
| tsconfig.base.json | Adds TS path aliases for the new packages. |
| tests/unit/tool-router-deep-coverage.test.ts | Adds deep-coverage tests for ToolRouter, including memory-service tool paths. |
| tests/unit/settings-service.test.ts | Adds validation/defaulting tests for theme settings read/write. |
| tests/unit/redaction.test.ts | Adds redaction unit tests for memory-service redaction logic. |
| tests/unit/redaction-more.test.ts | Adds additional redaction pattern tests. |
| tests/unit/phase9-type-guards.test.ts | Adds tests for newly introduced Phase 9 type guards. |
| tests/unit/permission-broker.test.ts | Updates expected classification count for new tools. |
| tests/unit/memory-service.test.ts | Adds basic MemoryService unit tests (creation/diff/metadata extraction). |
| tests/unit/memory-review-dialog.test.tsx | Adds UI tests for the new memory conflict dialog. |
| tests/unit/local-store.test.ts | Adds tests for local-store constants/embedding helpers. |
| tests/unit/local-store-migrations.test.ts | Adds integration-like tests for SQLite schema/migrations. |
| tests/unit/event-log-service.test.ts | Expands event-log sanitization tests (JWT, connection strings, filePath sanitization, etc.). |
| tests/unit/code-indexer.test.ts | Adds tests for code-indexer utility functions. |
| tests/unit/code-indexer-errors.test.ts | Adds error-path tests for indexing/rebuild/retrieve edge cases. |
| tests/unit/code-indexer-class.test.ts | Adds broader CodeIndexer tests including store integration. |
| tests/unit/code-indexer-chunking.test.ts | Adds chunking tests (tree-sitter and fallback chunking). |
| tests/unit/app-coverage-deep.test.tsx | Adjusts deep-coverage tests in App to match updated handler types/usages. |
| tests/mocks/node-sqlite.ts | Adds a node:sqlite mock so Vitest can bundle tests. |
| packages/workbench/src/MemoryReviewDialog.tsx | Adds a dialog component to review memory conflicts in the UI. |
| packages/workbench/src/App.tsx | Wires memory-conflict events into the UI and renders MemoryReviewDialog. |
| packages/shared/src/ipc.ts | Adds Phase 9 IPC channels, types, and guards (memory + code-indexer). |
| packages/shared/src/index.ts | Re-exports Phase 9 types and guards from shared. |
| packages/services/src/tool-router.ts | Adds ToolRouter support for propose/apply memory-change tools and event logging integration. |
| packages/services/src/phase9-exports.ts | Adds services-level re-exports for Phase 9 APIs. |
| packages/services/src/permission-broker.ts | Adds tool classifications for proposeMemoryChange and applyMemoryChange. |
| packages/services/src/index.ts | Exposes Phase 9 creation helpers from services. |
| packages/services/src/event-log-service.ts | Adds message/filePath sanitization plus expanded diff sanitization patterns. |
| packages/memory-service/tsconfig.json | Adds TS config for the memory-service package. |
| packages/memory-service/src/redaction.ts | Implements secret redaction used by memory/local-store flows. |
| packages/memory-service/src/memory-service.ts | Implements MemoryService (read/list/propose/apply edits, conflict detection). |
| packages/memory-service/src/local-store.ts | Implements SQLite local store with migrations, embeddings, chunk storage/search. |
| packages/memory-service/src/index.ts | Exports memory-service/local-store/redaction public surface. |
| packages/memory-service/package.json | Declares the @agentdeck/memory-service package. |
| packages/code-indexer/tsconfig.json | Adds TS config for the code-indexer package. |
| packages/code-indexer/src/utils.ts | Implements language detection and deterministic hashing helpers. |
| packages/code-indexer/src/index.ts | Exports the code-indexer public surface. |
| packages/code-indexer/src/code-indexer.ts | Implements CodeIndexer (index/rebuild/retrieve + optional store/memory-service integration). |
| packages/code-indexer/src/chunking.ts | Implements tree-sitter chunking and line-based fallback chunking. |
| packages/code-indexer/package.json | Declares the @agentdeck/code-indexer package. |
| packages/agent-runtime/src/session-broker.ts | Adds event-message sanitization before runtime events are stored/emitted. |
| package.json | Adds new scripts and dependencies needed for Phase 9. |
| package-lock.json | Locks new dependencies and local package links. |
| eslint.config.js | Ignores the migrations Vitest config in linting. |
| electron.vite.config.ts | Adds alias entries for the new packages in Electron Vite config. |
| CHANGELOG.md | Documents Phase 9 additions under Unreleased. |
| apps/desktop/src/preload/index.ts | Adds preload API methods for memory apply/conflict handling (partial Phase 9). |
| apps/desktop/src/main/index.ts | Registers Phase 9 IPC handlers and initializes local-store/memory/indexer services. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+131
to
+134
| // No conflict — apply directly | ||
| const newContent = this.extractNewContent(proposal.patch); | ||
| const entry = await this.write(proposal.scope, proposal.filePath, newContent); | ||
| return { status: "ok", entry }; |
Comment on lines
+179
to
+183
| async write(scope: MemoryScope, filePath: string, content: string): Promise<MemoryEntry> { | ||
| await mkdir(dirname(filePath), { recursive: true }); | ||
| await writeFile(filePath, redactSecrets(content), 'utf8'); | ||
| return this.describeEntry(scope, filePath, content); | ||
| } |
Comment on lines
+49
to
+53
| async ensureScope(scope: MemoryScope): Promise<string> { | ||
| const scopeDir = join(this.baseDir, 'memories', scope); | ||
| await mkdir(scopeDir, { recursive: true }); | ||
| return scopeDir; | ||
| } |
Comment on lines
+71
to
+79
| async list(scope?: MemoryScope): Promise<ListMemoryFilesResult> { | ||
| try { | ||
| const root = scope === undefined ? join(this.baseDir, 'memories') : join(this.baseDir, 'memories', scope); | ||
| const entries = await collectMarkdownEntries(root, scope); | ||
| return { status: 'ok', entries }; | ||
| } catch (error) { | ||
| return { status: 'error', code: 'UNKNOWN', message: String(error) }; | ||
| } | ||
| } |
Comment on lines
+18
to
+21
| } | ||
|
|
||
| return sanitized.trimStart(); | ||
| } |
Comment on lines
+1465
to
+1473
| ipcMain.handle(IPC_CHANNELS.rebuildCodeIndex, async (_event, roots: unknown) => { | ||
| try { | ||
| const rootArray = Array.isArray(roots) ? roots as string[] : []; | ||
| const result = await codeIndexer.rebuildIndex(rootArray); | ||
| return { status: 'ok', chunks: result.chunks, stats: result.stats }; | ||
| } catch (error) { | ||
| return { status: 'error', code: 'UNKNOWN', message: String(error) }; | ||
| } | ||
| }); |
Comment on lines
+1393
to
+1402
| ipcMain.handle(IPC_CHANNELS.proposeMemoryChange, async (_event, edit: unknown) => { | ||
| try { | ||
| if (!isRecord(edit) || typeof edit.filePath !== 'string' || typeof edit.text !== 'string') { | ||
| return { status: 'error', code: 'INVALID_INPUT', message: 'Invalid edit payload.' }; | ||
| } | ||
| return await memoryService.proposeEdit({ | ||
| scope: edit.scope as MemoryScope, | ||
| filePath: edit.filePath, | ||
| text: edit.text | ||
| }); |
Comment on lines
+1393
to
+1402
| ipcMain.handle(IPC_CHANNELS.proposeMemoryChange, async (_event, edit: unknown) => { | ||
| try { | ||
| if (!isRecord(edit) || typeof edit.filePath !== 'string' || typeof edit.text !== 'string') { | ||
| return { status: 'error', code: 'INVALID_INPUT', message: 'Invalid edit payload.' }; | ||
| } | ||
| return await memoryService.proposeEdit({ | ||
| scope: edit.scope as MemoryScope, | ||
| filePath: edit.filePath, | ||
| text: edit.text | ||
| }); |
Comment on lines
+436
to
+440
| listChunks(filters?: SearchEmbeddingFilters): readonly IndexChunk[] { | ||
| const { where, params } = buildChunkFilters(filters); | ||
| const rows = this.db.prepare(`select * from index_chunks ${where} order by file_path, start_line, id`).all(...(params as unknown as SQLInputValue[])) as ChunkRow[]; | ||
| return rows.map(rowToIndexChunk); | ||
| } |
Comment on lines
+568
to
+571
| if (filters?.folders !== undefined && filters.folders.length > 0) { | ||
| clauses.push(`(c.metadata_json is null or c.metadata_json like ?)`); | ||
| params.push(...filters.folders.map(folder => `%${escapeLike(folder)}%`)); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Test plan
pm test\ and verify all tests pass
pm run lint\ and verify no lint errors
pm run typecheck\ and verify no type errors