claude-memory-layer 1.0.27 → 1.0.28
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.
- package/.env.example +7 -0
- package/AGENTS.md +11 -0
- package/README.md +184 -41
- package/benchmarks/replay/anonymized-real-sessions.json +48 -0
- package/dist/cli/index.js +10097 -6003
- package/dist/cli/index.js.map +4 -4
- package/dist/core/index.js +9745 -5587
- package/dist/core/index.js.map +4 -4
- package/dist/hooks/post-tool-use.js +6545 -5270
- package/dist/hooks/post-tool-use.js.map +4 -4
- package/dist/hooks/semantic-daemon.js +6646 -5354
- package/dist/hooks/semantic-daemon.js.map +4 -4
- package/dist/hooks/session-end.js +6618 -5347
- package/dist/hooks/session-end.js.map +4 -4
- package/dist/hooks/session-start.js +6619 -5354
- package/dist/hooks/session-start.js.map +4 -4
- package/dist/hooks/stop.js +6614 -5325
- package/dist/hooks/stop.js.map +4 -4
- package/dist/hooks/user-prompt-submit.js +6702 -5356
- package/dist/hooks/user-prompt-submit.js.map +4 -4
- package/dist/index.js +13537 -0
- package/dist/index.js.map +7 -0
- package/dist/mcp/index.js +20770 -0
- package/dist/mcp/index.js.map +7 -0
- package/dist/server/api/index.js +6632 -5319
- package/dist/server/api/index.js.map +4 -4
- package/dist/server/index.js +6667 -5340
- package/dist/server/index.js.map +4 -4
- package/dist/services/memory-service.js +6568 -5350
- package/dist/services/memory-service.js.map +4 -4
- package/dist/ui/assets/js/bootstrap.js +244 -0
- package/dist/ui/assets/js/chat.js +373 -0
- package/dist/ui/assets/js/disclosure.js +232 -0
- package/dist/ui/assets/js/modals.js +298 -0
- package/dist/ui/assets/js/overview.js +655 -0
- package/dist/ui/assets/js/state.js +72 -0
- package/dist/ui/assets/js/views.js +468 -0
- package/dist/ui/index.html +43 -1
- package/dist/ui/index.ts +3 -0
- package/dist/ui/style.css +222 -0
- package/docs/ARCHITECTURE_COMPARISON_AND_RECOMMENDATIONS.md +627 -0
- package/docs/HERMES_MEMORY_INGESTION_ANALYSIS.md +440 -0
- package/docs/MEMORY_USEFULNESS_AUDIT.md +371 -0
- package/docs/MEMORY_USEFULNESS_AUDIT_RAW.json +80 -0
- package/docs/MEMSEARCH_PROJECT_STRUCTURE_ANALYSIS.md +333 -0
- package/docs/PRODUCT_VALIDATION_MATRIX.md +82 -0
- package/docs/PROJECT_STRUCTURE_ANALYSIS.md +421 -0
- package/docs/REFACTORING_MILESTONES_AND_ISSUES.md +501 -0
- package/docs/REFACTORING_PLAN_THIN_CORE.md +414 -0
- package/docs/REFERENCE_PROJECT_ANALYSES.md +25 -0
- package/docs/SUPERLOCALMEMORY_PROJECT_STRUCTURE_ANALYSIS.md +452 -0
- package/docs/TARGET_ARCHITECTURE_AND_FOLDER_STRUCTURE.md +446 -0
- package/docs/architecture/comparison-index.md +47 -0
- package/docs/reports/codex-real-data-validation-20260505T040447Z.md +46 -0
- package/package.json +9 -5
- package/scripts/build.ts +25 -8
- package/scripts/generate-session-qrels.ts +126 -0
- package/scripts/replay-retrieval-benchmark.ts +69 -0
- package/specs/thin-core-refactor/context.md +275 -0
- package/specs/thin-core-refactor/plan.md +536 -0
- package/specs/thin-core-refactor/spec.md +465 -0
- package/src/adapters/claude/capture/index.ts +3 -0
- package/src/adapters/claude/context/index.ts +3 -0
- package/src/adapters/claude/hooks/index.ts +21 -0
- package/src/adapters/claude/hooks/post-tool-use.ts +239 -0
- package/src/adapters/claude/hooks/prompt-injection-policy.ts +104 -0
- package/src/adapters/claude/hooks/semantic-daemon-client.ts +209 -0
- package/src/adapters/claude/hooks/semantic-daemon.ts +283 -0
- package/src/adapters/claude/hooks/session-end.ts +59 -0
- package/src/adapters/claude/hooks/session-start.ts +73 -0
- package/src/adapters/claude/hooks/stop.ts +128 -0
- package/src/adapters/claude/hooks/user-prompt-submit.ts +361 -0
- package/src/adapters/claude/index.ts +4 -0
- package/src/adapters/claude/transcript/index.ts +4 -0
- package/src/adapters/claude/transcript/transcript-reader.ts +57 -0
- package/src/adapters/claude/transcript/turn-reconstructor.ts +65 -0
- package/src/apps/cli/claude-settings-hooks.ts +138 -0
- package/src/apps/cli/codex-import-runner.ts +125 -0
- package/src/apps/cli/codex-validation-output.ts +95 -0
- package/src/apps/cli/hermes-import-runner.ts +130 -0
- package/src/apps/cli/hermes-validation-output.ts +91 -0
- package/src/apps/cli/index.ts +1731 -0
- package/src/apps/cli/mcp-install.ts +106 -0
- package/src/apps/cli/retrieval-disclosure-output.ts +196 -0
- package/src/apps/dashboard/assets/js/bootstrap.js +244 -0
- package/src/apps/dashboard/assets/js/chat.js +373 -0
- package/src/apps/dashboard/assets/js/disclosure.js +232 -0
- package/src/apps/dashboard/assets/js/modals.js +298 -0
- package/src/apps/dashboard/assets/js/overview.js +655 -0
- package/src/apps/dashboard/assets/js/state.js +72 -0
- package/src/apps/dashboard/assets/js/views.js +468 -0
- package/src/{ui → apps/dashboard}/index.html +43 -1
- package/src/apps/dashboard/index.ts +3 -0
- package/src/{ui → apps/dashboard}/style.css +222 -0
- package/src/apps/index.ts +5 -0
- package/src/apps/server/api/chat.ts +244 -0
- package/src/apps/server/api/citations.ts +105 -0
- package/src/apps/server/api/events.ts +137 -0
- package/src/apps/server/api/health.ts +53 -0
- package/src/apps/server/api/index.ts +26 -0
- package/src/apps/server/api/projects.ts +74 -0
- package/src/apps/server/api/search.ts +184 -0
- package/src/apps/server/api/sessions.ts +115 -0
- package/src/apps/server/api/stats.ts +723 -0
- package/src/apps/server/api/turns.ts +143 -0
- package/src/apps/server/api/utils.ts +65 -0
- package/src/apps/server/index.ts +111 -0
- package/src/cli/index.ts +2 -1311
- package/src/cli/retrieval-disclosure-output.ts +2 -0
- package/src/compat/index.ts +5 -0
- package/src/core/derive/fact-deriver.ts +170 -0
- package/src/core/derive/index.ts +2 -0
- package/src/core/derive/summary-deriver.ts +76 -0
- package/src/core/embedder.ts +4 -152
- package/src/core/engine/embedding-maintenance-service.ts +187 -0
- package/src/core/engine/endless-memory-services.ts +4 -0
- package/src/core/engine/index.ts +19 -0
- package/src/core/engine/memory-engine-services.ts +170 -0
- package/src/core/engine/memory-ingest-service.ts +317 -0
- package/src/core/engine/memory-query-service.ts +173 -0
- package/src/core/engine/memory-runtime-service.ts +162 -0
- package/src/core/engine/memory-service-composition.ts +231 -0
- package/src/core/engine/retrieval-analytics-service.ts +181 -0
- package/src/core/engine/retrieval-disclosure-service.ts +420 -0
- package/src/core/engine/retrieval-orchestrator.ts +377 -0
- package/src/core/engine/retrieval-services.ts +176 -0
- package/src/core/engine/shared-memory-services.ts +4 -0
- package/src/core/entity-repo.ts +1 -3
- package/src/core/event-store.ts +3 -3
- package/src/core/evidence-aligner.ts +2 -2
- package/src/core/external-market-context.ts +582 -0
- package/src/core/graduation.ts +2 -3
- package/src/core/index.ts +21 -0
- package/src/core/matcher.ts +2 -4
- package/src/core/model/memory-fact.ts +30 -0
- package/src/core/model/memory-rule.ts +14 -0
- package/src/core/model/memory-summary.ts +21 -0
- package/src/core/model/raw-event.ts +28 -0
- package/src/core/model/retrieval-result.ts +35 -0
- package/src/core/privacy/filter.ts +21 -10
- package/src/core/product-validation-matrix.ts +314 -0
- package/src/core/progressive-retriever.ts +1 -2
- package/src/core/registry/project-path.ts +54 -0
- package/src/core/registry/session-registry.ts +69 -0
- package/src/core/replay-evaluator.ts +625 -0
- package/src/core/retrieval-benchmark.ts +117 -0
- package/src/core/retrieval-quality.ts +109 -0
- package/src/core/retriever.ts +53 -15
- package/src/core/session-qrels.ts +360 -0
- package/src/core/shared-event-store.ts +1 -1
- package/src/core/sqlite-event-store.ts +35 -11
- package/src/core/task/blocker-resolver.ts +2 -2
- package/src/core/task/task-resolver.ts +0 -1
- package/src/core/vector-outbox.ts +1 -10
- package/src/core/vector-worker.ts +1 -1
- package/src/extensions/endless-memory/endless-memory-services.ts +350 -0
- package/src/extensions/endless-memory/index.ts +1 -0
- package/src/extensions/index.ts +5 -0
- package/src/extensions/mcp/handlers.ts +960 -0
- package/src/extensions/mcp/index.ts +48 -0
- package/src/extensions/mcp/tools.ts +252 -0
- package/src/extensions/shared-memory/index.ts +1 -0
- package/src/extensions/shared-memory/shared-memory-services.ts +211 -0
- package/src/extensions/vector/embedder.ts +197 -0
- package/src/extensions/vector/index.ts +1 -0
- package/src/hooks/post-tool-use.ts +3 -236
- package/src/hooks/semantic-daemon-client.ts +1 -208
- package/src/hooks/semantic-daemon.ts +6 -271
- package/src/hooks/session-end.ts +4 -79
- package/src/hooks/session-start.ts +4 -73
- package/src/hooks/stop.ts +3 -173
- package/src/hooks/user-prompt-submit.ts +3 -338
- package/src/index.ts +13 -0
- package/src/mcp/handlers.ts +2 -212
- package/src/mcp/index.ts +3 -46
- package/src/mcp/tools.ts +2 -78
- package/src/server/api/chat.ts +2 -244
- package/src/server/api/citations.ts +2 -105
- package/src/server/api/events.ts +2 -137
- package/src/server/api/health.ts +2 -53
- package/src/server/api/index.ts +2 -26
- package/src/server/api/projects.ts +2 -74
- package/src/server/api/search.ts +2 -102
- package/src/server/api/sessions.ts +2 -115
- package/src/server/api/stats.ts +2 -724
- package/src/server/api/turns.ts +2 -143
- package/src/server/api/utils.ts +2 -46
- package/src/server/index.ts +2 -100
- package/src/services/bootstrap-organizer.ts +46 -26
- package/src/services/codex-session-history-importer.ts +521 -29
- package/src/services/hermes-session-history-importer.ts +733 -0
- package/src/services/memory-service-config.ts +36 -0
- package/src/services/memory-service-registry.ts +150 -0
- package/src/services/memory-service.ts +211 -1325
- package/src/services/session-history-importer.ts +58 -14
- package/tests/README.md +23 -0
- package/tests/adapters/claude/claude-semantic-daemon-adapter.test.ts +54 -0
- package/tests/adapters/claude/claude-transcript-reconstructor.test.ts +98 -0
- package/tests/adapters/claude-hook-prompt-injection-policy.test.ts +99 -0
- package/tests/apps/app-layer-boundary.test.ts +48 -0
- package/tests/apps/claude-settings-hooks.test.ts +107 -0
- package/tests/apps/cli-disclosure-output.test.ts +212 -0
- package/tests/apps/codex-import-runner.test.ts +99 -0
- package/tests/apps/codex-validation-output.test.ts +100 -0
- package/tests/apps/hermes-import-runner.test.ts +99 -0
- package/tests/apps/mcp-install-command.test.ts +59 -0
- package/tests/apps/package-build-entrypoints.test.ts +30 -0
- package/tests/apps/search-api-disclosure.test.ts +162 -0
- package/tests/apps/stats-api-lightweight.test.ts +67 -0
- package/tests/apps/ui-disclosure-output.test.ts +140 -0
- package/tests/{bootstrap-organizer.test.ts → core/bootstrap-organizer.test.ts} +1 -1
- package/tests/{canonical-key.test.ts → core/canonical-key.test.ts} +1 -1
- package/tests/core/codex-session-history-importer-validation.test.ts +185 -0
- package/tests/{consolidation-worker.test.ts → core/consolidation-worker.test.ts} +2 -2
- package/tests/core/embedding-maintenance-service.test.ts +282 -0
- package/tests/{evidence-aligner.test.ts → core/evidence-aligner.test.ts} +1 -1
- package/tests/core/external-market-context.test.ts +209 -0
- package/tests/core/fact-deriver.test.ts +79 -0
- package/tests/core/hermes-session-history-importer-validation.test.ts +609 -0
- package/tests/{ingest-interceptor.test.ts → core/ingest-interceptor.test.ts} +1 -1
- package/tests/{markdown-mirror.test.ts → core/markdown-mirror.test.ts} +2 -2
- package/tests/{matcher.test.ts → core/matcher.test.ts} +1 -1
- package/tests/{md-mirror.test.ts → core/md-mirror.test.ts} +2 -2
- package/tests/core/memory-engine-services.test.ts +240 -0
- package/tests/core/memory-ingest-service.test.ts +296 -0
- package/tests/core/memory-query-service.test.ts +129 -0
- package/tests/core/memory-runtime-service.test.ts +201 -0
- package/tests/core/memory-service-composition.test.ts +192 -0
- package/tests/core/memory-service-config.test.ts +41 -0
- package/tests/core/memory-service-facade.test.ts +30 -0
- package/tests/core/memory-service-registry.test.ts +206 -0
- package/tests/core/product-validation-matrix.test.ts +61 -0
- package/tests/core/project-registry.test.ts +78 -0
- package/tests/core/replay-evaluator.test.ts +181 -0
- package/tests/core/retrieval-analytics-service.test.ts +210 -0
- package/tests/core/retrieval-benchmark.test.ts +93 -0
- package/tests/core/retrieval-disclosure-service.test.ts +264 -0
- package/tests/core/retrieval-orchestrator.test.ts +403 -0
- package/tests/core/retrieval-quality.test.ts +31 -0
- package/tests/core/retrieval-services.test.ts +185 -0
- package/tests/{retriever-fallback-chain.test.ts → core/retriever-fallback-chain.test.ts} +3 -3
- package/tests/{retriever-strategy-scope.test.ts → core/retriever-strategy-scope.test.ts} +70 -3
- package/tests/{retriever.memu-adoption.test.ts → core/retriever.memu-adoption.test.ts} +3 -3
- package/tests/core/session-history-importer-filter.test.ts +78 -0
- package/tests/core/session-qrels.test.ts +250 -0
- package/tests/{sqlite-event-store-replication.test.ts → core/sqlite-event-store-replication.test.ts} +36 -1
- package/tests/core/summary-deriver.test.ts +66 -0
- package/tests/extensions/embedder-warning-suppression.test.ts +53 -0
- package/tests/extensions/endless-memory-extension-boundary.test.ts +17 -0
- package/tests/extensions/endless-memory-services.test.ts +325 -0
- package/tests/extensions/mcp-context-tools.test.ts +905 -0
- package/tests/extensions/mcp-extension-boundary.test.ts +21 -0
- package/tests/extensions/mcp-package-build.test.ts +22 -0
- package/tests/extensions/mcp-project-aware-tools.test.ts +102 -0
- package/tests/extensions/shared-memory-extension-boundary.test.ts +24 -0
- package/tests/extensions/shared-memory-services.test.ts +309 -0
- package/tests/extensions/vector-extension-boundary.test.ts +21 -0
- package/.claude/settings.local.json +0 -25
- package/.npm-cache/_cacache/content-v2/sha512/04/76/c098f88dfe584a2b80870bff7421b05d17d3d9ee1027f77772332a22d3f93a9a57101a2855107f6ad82077a818bba912b2bc317f2361b5ddb09ad284d9ce +0 -0
- package/.npm-cache/_cacache/content-v2/sha512/60/25/d2ecd39cfc7cab58351162814be77f935c6d6491c10c3745d456da7ddb2117ffd90c10e53fe3c0f1ed16b403307841543634504398b16ee4e6b6dd8e0c45 +0 -0
- package/.npm-cache/_cacache/index-v5/2b/9a/7f8f40206ed8a2e0a84efaa953ccaed1f5d001e14b931083f2e7a0738007 +0 -2
- package/.npm-cache/_cacache/index-v5/2e/d9/fcfa5c6a6abdc2a3644ab84a95936047298c465a2f47ee03db8f7fe1e946 +0 -3
- package/.npm-cache/_cacache/index-v5/a9/42/e519633356d12d3d2f19da66a8301016d496c8f5c3e0554124aaa62dc043 +0 -2
- package/.npm-cache/_logs/2026-02-26T12_04_52_729Z-debug-0.log +0 -256
- package/.npm-cache/_logs/2026-02-26T12_05_36_835Z-debug-0.log +0 -18
- package/.npm-cache/_logs/2026-02-26T12_05_45_982Z-debug-0.log +0 -32
- package/.npm-cache/_logs/2026-02-26T12_05_48_515Z-debug-0.log +0 -260
- package/.npm-cache/_logs/2026-02-26T12_05_53_567Z-debug-0.log +0 -69
- package/.npm-cache/_update-notifier-last-checked +0 -0
- package/bootstrap-kb/decisions/decisions.md +0 -244
- package/bootstrap-kb/glossary/glossary.md +0 -46
- package/bootstrap-kb/modules/.claude-plugin.md +0 -22
- package/bootstrap-kb/modules/agents.md.md +0 -15
- package/bootstrap-kb/modules/claude.md.md +0 -15
- package/bootstrap-kb/modules/context.md.md +0 -15
- package/bootstrap-kb/modules/docs.md +0 -18
- package/bootstrap-kb/modules/handoff.md.md +0 -15
- package/bootstrap-kb/modules/package-lock.json.md +0 -15
- package/bootstrap-kb/modules/package.json.md +0 -15
- package/bootstrap-kb/modules/plan.md.md +0 -15
- package/bootstrap-kb/modules/readme.md.md +0 -15
- package/bootstrap-kb/modules/scripts.md +0 -26
- package/bootstrap-kb/modules/spec.md.md +0 -15
- package/bootstrap-kb/modules/specs.md +0 -20
- package/bootstrap-kb/modules/src.md +0 -51
- package/bootstrap-kb/modules/tests.md +0 -42
- package/bootstrap-kb/modules/tsconfig.json.md +0 -15
- package/bootstrap-kb/modules/vitest.config.ts.md +0 -15
- package/bootstrap-kb/overview/overview.md +0 -40
- package/bootstrap-kb/sources/manifest.json +0 -950
- package/bootstrap-kb/sources/manifest.md +0 -227
- package/bootstrap-kb/timeline/timeline.md +0 -57
- package/claude-memory-layer-1.0.14.tgz +0 -0
- package/d.sh +0 -3
- package/deploy.sh +0 -3
- package/dist/ui/app.js +0 -2101
- package/memory/.claude-plugin/commands/2026-02-25.md +0 -263
- package/memory/_index.md +0 -419
- package/memory/agent_response/uncategorized/2026-02-26.md +0 -176
- package/memory/agent_response/uncategorized/2026-03-03.md +0 -14
- package/memory/agent_response/uncategorized/2026-03-04.md +0 -1421
- package/memory/agent_response/uncategorized/2026-03-05.md +0 -157
- package/memory/default/uncategorized/2026-02-25.md +0 -4839
- package/memory/session_summary/uncategorized/2026-02-26.md +0 -13
- package/memory/session_summary/uncategorized/2026-03-03.md +0 -5
- package/memory/session_summary/uncategorized/2026-03-04.md +0 -50
- package/memory/specs/20260207-dashboard-upgrade/2026-02-25.md +0 -142
- package/memory/specs/citations-system/2026-02-25.md +0 -1121
- package/memory/specs/endless-mode/2026-02-25.md +0 -1392
- package/memory/specs/entity-edge-model/2026-02-25.md +0 -1263
- package/memory/specs/evidence-aligner-v2/2026-02-25.md +0 -1028
- package/memory/specs/mcp-desktop-integration/2026-02-25.md +0 -1334
- package/memory/specs/post-tool-use-hook/2026-02-25.md +0 -1164
- package/memory/specs/private-tags/2026-02-25.md +0 -1057
- package/memory/specs/progressive-disclosure/2026-02-25.md +0 -1436
- package/memory/specs/task-entity-system/2026-02-25.md +0 -924
- package/memory/specs/vector-outbox-v2/2026-02-25.md +0 -1510
- package/memory/specs/web-viewer-ui/2026-02-25.md +0 -1709
- package/memory/tool_observation/uncategorized/2026-02-26.md +0 -209
- package/memory/tool_observation/uncategorized/2026-03-03.md +0 -21
- package/memory/tool_observation/uncategorized/2026-03-04.md +0 -1033
- package/memory/tool_observation/uncategorized/2026-03-05.md +0 -33
- package/memory/user_prompt/uncategorized/2026-02-26.md +0 -25
- package/memory/user_prompt/uncategorized/2026-03-04.md +0 -634
- package/memory/user_prompt/uncategorized/2026-03-05.md +0 -6
- package/specs/optional-duckdb/context.md +0 -77
- package/specs/optional-duckdb/plan.md +0 -142
- package/specs/optional-duckdb/spec.md +0 -35
- package/src/ui/app.js +0 -2101
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory Engine Services Bundle
|
|
3
|
+
*
|
|
4
|
+
* Owns construction and wiring for storage-backed engine services so
|
|
5
|
+
* MemoryService can stay focused on public facade/lifecycle behavior.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
|
|
11
|
+
import { Embedder, getDefaultEmbedder } from '../embedder.js';
|
|
12
|
+
import type { EventStore } from '../event-store.js';
|
|
13
|
+
import { createGraduationPipeline, type GraduationPipeline } from '../graduation.js';
|
|
14
|
+
import { getDefaultMatcher, type Matcher } from '../matcher.js';
|
|
15
|
+
import { MarkdownMirror } from '../md-mirror.js';
|
|
16
|
+
import type { Retriever } from '../retriever.js';
|
|
17
|
+
import { SQLiteEventStore } from '../sqlite-event-store.js';
|
|
18
|
+
import type { ToolObservationPayload } from '../types.js';
|
|
19
|
+
import { VectorStore } from '../vector-store.js';
|
|
20
|
+
import { MemoryIngestService } from './memory-ingest-service.js';
|
|
21
|
+
import { MemoryQueryService } from './memory-query-service.js';
|
|
22
|
+
import {
|
|
23
|
+
createRetrievalServices,
|
|
24
|
+
type RetrievalAnalyticsService,
|
|
25
|
+
type RetrievalDisclosureService,
|
|
26
|
+
type RetrievalDisclosureSharedStore,
|
|
27
|
+
type RetrievalEventStore,
|
|
28
|
+
type RetrievalOrchestrator,
|
|
29
|
+
type RetrievalServices,
|
|
30
|
+
type RetrievalServicesDeps
|
|
31
|
+
} from './retrieval-services.js';
|
|
32
|
+
|
|
33
|
+
export interface MemoryEngineServicesOptions {
|
|
34
|
+
storagePath: string;
|
|
35
|
+
readOnly: boolean;
|
|
36
|
+
embeddingModel?: string;
|
|
37
|
+
cwd?: string;
|
|
38
|
+
initialize: () => Promise<void>;
|
|
39
|
+
getProjectHash: () => string | null;
|
|
40
|
+
getProjectPath?: () => string | null;
|
|
41
|
+
hasSharedStore: () => boolean;
|
|
42
|
+
sharedStore?: RetrievalDisclosureSharedStore;
|
|
43
|
+
createToolObservationEmbedding: (payload: ToolObservationPayload) => string;
|
|
44
|
+
factories?: MemoryEngineServicesFactories;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface MemoryEngineServicesFactories {
|
|
48
|
+
createSQLiteEventStore?: (
|
|
49
|
+
dbPath: string,
|
|
50
|
+
options: { readonly: boolean; markdownMirrorRoot: string }
|
|
51
|
+
) => SQLiteEventStore;
|
|
52
|
+
createVectorStore?: (vectorsPath: string) => VectorStore;
|
|
53
|
+
createEmbedder?: (model: string) => Embedder;
|
|
54
|
+
getDefaultEmbedder?: () => Embedder;
|
|
55
|
+
getDefaultMatcher?: () => Matcher;
|
|
56
|
+
createMarkdownMirror?: (cwd: string) => MarkdownMirror;
|
|
57
|
+
createGraduationPipeline?: (eventStore: EventStore) => GraduationPipeline;
|
|
58
|
+
createRetrievalServices?: (deps: RetrievalServicesDeps) => RetrievalServices;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface MemoryEngineServices {
|
|
62
|
+
storagePath: string;
|
|
63
|
+
sqliteStore: SQLiteEventStore;
|
|
64
|
+
vectorStore: VectorStore;
|
|
65
|
+
embedder: Embedder;
|
|
66
|
+
matcher: Matcher;
|
|
67
|
+
retriever: Retriever;
|
|
68
|
+
retrievalOrchestrator: RetrievalOrchestrator;
|
|
69
|
+
retrievalDisclosureService: RetrievalDisclosureService;
|
|
70
|
+
retrievalAnalyticsService: RetrievalAnalyticsService;
|
|
71
|
+
graduation: GraduationPipeline;
|
|
72
|
+
mdMirror: MarkdownMirror;
|
|
73
|
+
ingestService: MemoryIngestService;
|
|
74
|
+
queryService: MemoryQueryService;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function createMemoryEngineServices(options: MemoryEngineServicesOptions): MemoryEngineServices {
|
|
78
|
+
const factories = options.factories ?? {};
|
|
79
|
+
const storagePath = options.storagePath;
|
|
80
|
+
|
|
81
|
+
if (!options.readOnly && !fs.existsSync(storagePath)) {
|
|
82
|
+
fs.mkdirSync(storagePath, { recursive: true });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const sqliteStore = (factories.createSQLiteEventStore ?? defaultCreateSQLiteEventStore)(
|
|
86
|
+
path.join(storagePath, 'events.sqlite'),
|
|
87
|
+
{
|
|
88
|
+
readonly: options.readOnly,
|
|
89
|
+
markdownMirrorRoot: storagePath
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
const vectorStore = (factories.createVectorStore ?? defaultCreateVectorStore)(
|
|
93
|
+
path.join(storagePath, 'vectors')
|
|
94
|
+
);
|
|
95
|
+
const embeddingModel = options.embeddingModel || process.env.CLAUDE_MEMORY_EMBEDDING_MODEL;
|
|
96
|
+
const embedder = embeddingModel
|
|
97
|
+
? (factories.createEmbedder ?? defaultCreateEmbedder)(embeddingModel)
|
|
98
|
+
: (factories.getDefaultEmbedder ?? getDefaultEmbedder)();
|
|
99
|
+
const matcher = (factories.getDefaultMatcher ?? getDefaultMatcher)();
|
|
100
|
+
const mdMirror = (factories.createMarkdownMirror ?? defaultCreateMarkdownMirror)(
|
|
101
|
+
options.cwd ?? process.cwd()
|
|
102
|
+
);
|
|
103
|
+
const graduation = (factories.createGraduationPipeline ?? defaultCreateGraduationPipeline)(
|
|
104
|
+
sqliteStore as unknown as EventStore
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
const retrievalServices = (factories.createRetrievalServices ?? createRetrievalServices)({
|
|
108
|
+
initialize: options.initialize,
|
|
109
|
+
eventStore: sqliteStore as unknown as RetrievalEventStore,
|
|
110
|
+
vectorStore,
|
|
111
|
+
embedder,
|
|
112
|
+
matcher,
|
|
113
|
+
getProjectHash: options.getProjectHash,
|
|
114
|
+
hasSharedStore: options.hasSharedStore,
|
|
115
|
+
sharedStore: options.sharedStore
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const ingestService = new MemoryIngestService({
|
|
119
|
+
initialize: options.initialize,
|
|
120
|
+
eventStore: sqliteStore,
|
|
121
|
+
markdownMirror: mdMirror,
|
|
122
|
+
createToolEmbedding: options.createToolObservationEmbedding,
|
|
123
|
+
getProjectHash: options.getProjectHash,
|
|
124
|
+
getProjectPath: options.getProjectPath
|
|
125
|
+
});
|
|
126
|
+
const queryService = new MemoryQueryService(
|
|
127
|
+
() => sqliteStore.initialize(),
|
|
128
|
+
sqliteStore,
|
|
129
|
+
{ vectorStore, graduation }
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
storagePath,
|
|
134
|
+
sqliteStore,
|
|
135
|
+
vectorStore,
|
|
136
|
+
embedder,
|
|
137
|
+
matcher,
|
|
138
|
+
retriever: retrievalServices.retriever,
|
|
139
|
+
retrievalOrchestrator: retrievalServices.retrievalOrchestrator,
|
|
140
|
+
retrievalDisclosureService: retrievalServices.retrievalDisclosureService,
|
|
141
|
+
retrievalAnalyticsService: retrievalServices.retrievalAnalyticsService,
|
|
142
|
+
graduation,
|
|
143
|
+
mdMirror,
|
|
144
|
+
ingestService,
|
|
145
|
+
queryService
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function defaultCreateSQLiteEventStore(
|
|
150
|
+
dbPath: string,
|
|
151
|
+
options: { readonly: boolean; markdownMirrorRoot: string }
|
|
152
|
+
): SQLiteEventStore {
|
|
153
|
+
return new SQLiteEventStore(dbPath, options);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function defaultCreateVectorStore(vectorsPath: string): VectorStore {
|
|
157
|
+
return new VectorStore(vectorsPath);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function defaultCreateEmbedder(model: string): Embedder {
|
|
161
|
+
return new Embedder(model);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function defaultCreateMarkdownMirror(cwd: string): MarkdownMirror {
|
|
165
|
+
return new MarkdownMirror(cwd);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function defaultCreateGraduationPipeline(eventStore: EventStore): GraduationPipeline {
|
|
169
|
+
return createGraduationPipeline(eventStore);
|
|
170
|
+
}
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IngestInterceptor,
|
|
3
|
+
IngestInterceptorRegistry,
|
|
4
|
+
mergeHierarchicalMetadata
|
|
5
|
+
} from '../ingest-interceptor.js';
|
|
6
|
+
import { normalizeTags } from '../tag-taxonomy.js';
|
|
7
|
+
import { createSummaryDeriver, type SummaryDeriver } from '../derive/summary-deriver.js';
|
|
8
|
+
import type { AppendResult, MemoryEvent, MemoryEventInput, ToolObservationPayload } from '../types.js';
|
|
9
|
+
|
|
10
|
+
interface SessionRecord {
|
|
11
|
+
id: string;
|
|
12
|
+
startedAt?: Date;
|
|
13
|
+
endedAt?: Date;
|
|
14
|
+
projectPath?: string;
|
|
15
|
+
summary?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface SessionUpsertStore {
|
|
19
|
+
upsertSession(session: SessionRecord): Promise<void>;
|
|
20
|
+
getSessionEvents(sessionId: string): Promise<MemoryEvent[]>;
|
|
21
|
+
getSessionsWithoutSummary(currentSessionId: string, limit?: number): Promise<string[]>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface IngestEventStore extends SessionUpsertStore {
|
|
25
|
+
append(input: MemoryEventInput): Promise<AppendResult>;
|
|
26
|
+
enqueueForEmbedding(eventId: string, content: string): Promise<unknown>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface IngestMarkdownMirror {
|
|
30
|
+
append(event: MemoryEventInput, eventId?: string): Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type IngestOperation = 'user_prompt' | 'agent_response' | 'session_summary' | 'tool_observation';
|
|
34
|
+
|
|
35
|
+
export interface MemoryIngestServiceOptions {
|
|
36
|
+
initialize: () => Promise<void>;
|
|
37
|
+
eventStore: IngestEventStore;
|
|
38
|
+
markdownMirror: IngestMarkdownMirror;
|
|
39
|
+
createToolEmbedding: (payload: ToolObservationPayload) => string;
|
|
40
|
+
getProjectHash?: () => string | null;
|
|
41
|
+
getProjectPath?: () => string | null;
|
|
42
|
+
summaryDeriver?: SummaryDeriver;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Thin-core ingest service for session lifecycle and event writes.
|
|
47
|
+
*
|
|
48
|
+
* Owns the ingest normalization/interceptor/append pipeline so the public
|
|
49
|
+
* MemoryService facade can delegate ingest behavior without coordinating
|
|
50
|
+
* storage-side effects itself.
|
|
51
|
+
*/
|
|
52
|
+
export class MemoryIngestService {
|
|
53
|
+
private readonly initialize: () => Promise<void>;
|
|
54
|
+
private readonly eventStore: IngestEventStore;
|
|
55
|
+
private readonly markdownMirror: IngestMarkdownMirror;
|
|
56
|
+
private readonly createToolEmbedding: (payload: ToolObservationPayload) => string;
|
|
57
|
+
private readonly getProjectHash: () => string | null;
|
|
58
|
+
private readonly getProjectPath: () => string | null;
|
|
59
|
+
private readonly summaryDeriver: SummaryDeriver;
|
|
60
|
+
private readonly ingestInterceptors = new IngestInterceptorRegistry();
|
|
61
|
+
|
|
62
|
+
constructor(options: MemoryIngestServiceOptions) {
|
|
63
|
+
this.initialize = options.initialize;
|
|
64
|
+
this.eventStore = options.eventStore;
|
|
65
|
+
this.markdownMirror = options.markdownMirror;
|
|
66
|
+
this.createToolEmbedding = options.createToolEmbedding;
|
|
67
|
+
this.getProjectHash = options.getProjectHash ?? (() => null);
|
|
68
|
+
this.getProjectPath = options.getProjectPath ?? (() => null);
|
|
69
|
+
this.summaryDeriver = options.summaryDeriver ?? createSummaryDeriver();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
registerIngestBefore(interceptor: IngestInterceptor): () => void {
|
|
73
|
+
return this.ingestInterceptors.registerBefore(interceptor);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
registerIngestAfter(interceptor: IngestInterceptor): () => void {
|
|
77
|
+
return this.ingestInterceptors.registerAfter(interceptor);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
registerIngestOnError(interceptor: IngestInterceptor): () => void {
|
|
81
|
+
return this.ingestInterceptors.registerOnError(interceptor);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async startSession(sessionId: string, projectPath?: string): Promise<void> {
|
|
85
|
+
await this.initialize();
|
|
86
|
+
|
|
87
|
+
await this.eventStore.upsertSession({
|
|
88
|
+
id: sessionId,
|
|
89
|
+
startedAt: new Date(),
|
|
90
|
+
projectPath
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async endSession(sessionId: string, summary?: string): Promise<void> {
|
|
95
|
+
await this.initialize();
|
|
96
|
+
|
|
97
|
+
await this.eventStore.upsertSession({
|
|
98
|
+
id: sessionId,
|
|
99
|
+
endedAt: new Date(),
|
|
100
|
+
summary
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async storeUserPrompt(
|
|
105
|
+
sessionId: string,
|
|
106
|
+
content: string,
|
|
107
|
+
metadata?: Record<string, unknown>
|
|
108
|
+
): Promise<AppendResult> {
|
|
109
|
+
return this.ingestEvent({
|
|
110
|
+
operation: 'user_prompt',
|
|
111
|
+
input: {
|
|
112
|
+
eventType: 'user_prompt',
|
|
113
|
+
sessionId,
|
|
114
|
+
timestamp: new Date(),
|
|
115
|
+
content,
|
|
116
|
+
metadata
|
|
117
|
+
},
|
|
118
|
+
embeddingContent: content
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async storeAgentResponse(
|
|
123
|
+
sessionId: string,
|
|
124
|
+
content: string,
|
|
125
|
+
metadata?: Record<string, unknown>
|
|
126
|
+
): Promise<AppendResult> {
|
|
127
|
+
return this.ingestEvent({
|
|
128
|
+
operation: 'agent_response',
|
|
129
|
+
input: {
|
|
130
|
+
eventType: 'agent_response',
|
|
131
|
+
sessionId,
|
|
132
|
+
timestamp: new Date(),
|
|
133
|
+
content,
|
|
134
|
+
metadata
|
|
135
|
+
},
|
|
136
|
+
embeddingContent: content
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async storeSessionSummary(
|
|
141
|
+
sessionId: string,
|
|
142
|
+
summary: string,
|
|
143
|
+
metadata?: Record<string, unknown>
|
|
144
|
+
): Promise<AppendResult> {
|
|
145
|
+
return this.ingestEvent({
|
|
146
|
+
operation: 'session_summary',
|
|
147
|
+
input: {
|
|
148
|
+
eventType: 'session_summary',
|
|
149
|
+
sessionId,
|
|
150
|
+
timestamp: new Date(),
|
|
151
|
+
content: summary,
|
|
152
|
+
metadata
|
|
153
|
+
},
|
|
154
|
+
embeddingContent: summary
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Backfill session summaries for recent sessions that are missing them.
|
|
160
|
+
* Called from session-start hook to catch sessions that ended without Stop hook.
|
|
161
|
+
*/
|
|
162
|
+
async backfillMissingSummaries(currentSessionId: string, limit = 5): Promise<void> {
|
|
163
|
+
await this.initialize();
|
|
164
|
+
|
|
165
|
+
const recentSessionIds = await this.eventStore.getSessionsWithoutSummary(currentSessionId, limit);
|
|
166
|
+
for (const sessionId of recentSessionIds) {
|
|
167
|
+
try {
|
|
168
|
+
await this.generateSessionSummary(sessionId);
|
|
169
|
+
} catch {
|
|
170
|
+
// non-critical backfill path
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Generate a rule-based session summary from stored events.
|
|
177
|
+
* Skips short sessions and sessions that already contain a summary event.
|
|
178
|
+
*/
|
|
179
|
+
async generateSessionSummary(sessionId: string): Promise<void> {
|
|
180
|
+
await this.initialize();
|
|
181
|
+
|
|
182
|
+
const events = await this.eventStore.getSessionEvents(sessionId);
|
|
183
|
+
const summary = this.summaryDeriver.deriveSessionSummary(events);
|
|
184
|
+
if (!summary) return;
|
|
185
|
+
|
|
186
|
+
await this.storeSessionSummary(sessionId, summary.text, summary.metadata);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async storeToolObservation(
|
|
190
|
+
sessionId: string,
|
|
191
|
+
payload: ToolObservationPayload
|
|
192
|
+
): Promise<AppendResult> {
|
|
193
|
+
const content = JSON.stringify(payload);
|
|
194
|
+
const turnId = (payload.metadata as Record<string, unknown> | undefined)?.turnId;
|
|
195
|
+
|
|
196
|
+
return this.ingestEvent({
|
|
197
|
+
operation: 'tool_observation',
|
|
198
|
+
input: {
|
|
199
|
+
eventType: 'tool_observation',
|
|
200
|
+
sessionId,
|
|
201
|
+
timestamp: new Date(),
|
|
202
|
+
content,
|
|
203
|
+
metadata: {
|
|
204
|
+
toolName: payload.toolName,
|
|
205
|
+
success: payload.success,
|
|
206
|
+
...(typeof turnId === 'string' && turnId.length > 0 ? { turnId } : {})
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
embeddingContent: this.createToolEmbedding(payload)
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
private async ingestEvent(options: {
|
|
214
|
+
operation: IngestOperation;
|
|
215
|
+
input: MemoryEventInput;
|
|
216
|
+
embeddingContent?: string;
|
|
217
|
+
}): Promise<AppendResult> {
|
|
218
|
+
const normalizedInput = this.normalizeInput(options.operation, options.input);
|
|
219
|
+
|
|
220
|
+
await this.ingestInterceptors.run('before', {
|
|
221
|
+
operation: options.operation,
|
|
222
|
+
sessionId: normalizedInput.sessionId,
|
|
223
|
+
event: normalizedInput
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
try {
|
|
227
|
+
const result = await this.eventStore.append(normalizedInput);
|
|
228
|
+
if (result.success === false) {
|
|
229
|
+
await this.ingestInterceptors.run('error', {
|
|
230
|
+
operation: options.operation,
|
|
231
|
+
sessionId: normalizedInput.sessionId,
|
|
232
|
+
event: normalizedInput,
|
|
233
|
+
error: new Error(result.error)
|
|
234
|
+
});
|
|
235
|
+
return result;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (!result.isDuplicate) {
|
|
239
|
+
if (options.embeddingContent) {
|
|
240
|
+
await this.eventStore.enqueueForEmbedding(result.eventId, options.embeddingContent);
|
|
241
|
+
}
|
|
242
|
+
try {
|
|
243
|
+
await this.markdownMirror.append(normalizedInput, result.eventId);
|
|
244
|
+
} catch {
|
|
245
|
+
// non-breaking markdown mirror write
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
await this.ingestInterceptors.run('after', {
|
|
250
|
+
operation: options.operation,
|
|
251
|
+
sessionId: normalizedInput.sessionId,
|
|
252
|
+
event: normalizedInput
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
return result;
|
|
256
|
+
} catch (error) {
|
|
257
|
+
const normalizedError = error instanceof Error ? error : new Error(String(error));
|
|
258
|
+
await this.ingestInterceptors.run('error', {
|
|
259
|
+
operation: options.operation,
|
|
260
|
+
sessionId: normalizedInput.sessionId,
|
|
261
|
+
event: normalizedInput,
|
|
262
|
+
error: normalizedError
|
|
263
|
+
});
|
|
264
|
+
throw error;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
private normalizeInput(operation: IngestOperation, input: MemoryEventInput): MemoryEventInput {
|
|
269
|
+
const projectHash = this.getProjectHash();
|
|
270
|
+
const projectPath = this.getProjectPath();
|
|
271
|
+
const normalizedInput: MemoryEventInput = {
|
|
272
|
+
...input,
|
|
273
|
+
metadata: mergeHierarchicalMetadata(
|
|
274
|
+
{
|
|
275
|
+
ingest: {
|
|
276
|
+
operation,
|
|
277
|
+
pipeline: 'default',
|
|
278
|
+
ts: new Date().toISOString()
|
|
279
|
+
},
|
|
280
|
+
...(projectHash
|
|
281
|
+
? {
|
|
282
|
+
scope: {
|
|
283
|
+
project: {
|
|
284
|
+
hash: projectHash,
|
|
285
|
+
...(projectPath ? { path: projectPath } : {})
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
tags: [`proj:${projectHash}`]
|
|
289
|
+
}
|
|
290
|
+
: {})
|
|
291
|
+
},
|
|
292
|
+
input.metadata
|
|
293
|
+
)
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
if (projectHash && normalizedInput.metadata) {
|
|
297
|
+
const meta = normalizedInput.metadata as Record<string, unknown>;
|
|
298
|
+
const currentTags = Array.isArray(meta.tags)
|
|
299
|
+
? meta.tags.filter((x): x is string => typeof x === 'string')
|
|
300
|
+
: [];
|
|
301
|
+
const projectTag = `proj:${projectHash}`;
|
|
302
|
+
if (!currentTags.includes(projectTag)) {
|
|
303
|
+
meta.tags = [...currentTags, projectTag];
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (normalizedInput.metadata) {
|
|
308
|
+
const meta = normalizedInput.metadata as Record<string, unknown>;
|
|
309
|
+
const normalizedTags = normalizeTags(meta.tags);
|
|
310
|
+
if (normalizedTags.length > 0) {
|
|
311
|
+
meta.tags = normalizedTags;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return normalizedInput;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import type { MemoryEvent } from '../types.js';
|
|
2
|
+
|
|
3
|
+
interface RankedKeywordResult {
|
|
4
|
+
event: MemoryEvent;
|
|
5
|
+
rank: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface MemorySessionTurn {
|
|
9
|
+
turnId: string;
|
|
10
|
+
events: MemoryEvent[];
|
|
11
|
+
startedAt: Date;
|
|
12
|
+
promptPreview: string;
|
|
13
|
+
eventCount: number;
|
|
14
|
+
toolCount: number;
|
|
15
|
+
hasResponse: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface MemoryOutboxStats {
|
|
19
|
+
embedding: { pending: number; processing: number; failed: number; total: number };
|
|
20
|
+
vector: { pending: number; processing: number; failed: number; total: number };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface MemoryStats {
|
|
24
|
+
totalEvents: number;
|
|
25
|
+
vectorCount: number;
|
|
26
|
+
levelStats: Array<{ level: string; count: number }>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface QueryStore {
|
|
30
|
+
keywordSearch(query: string, topK: number): Promise<RankedKeywordResult[]>;
|
|
31
|
+
getSessionEvents(sessionId: string): Promise<MemoryEvent[]>;
|
|
32
|
+
getRecentEvents(limit: number): Promise<MemoryEvent[]>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface QueryMaintenanceStore extends QueryStore {
|
|
36
|
+
rebuildFtsIndex(): Promise<number>;
|
|
37
|
+
getOutboxStats(): Promise<MemoryOutboxStats>;
|
|
38
|
+
getEventsByLevel(level: string, options?: { limit?: number; offset?: number }): Promise<MemoryEvent[]>;
|
|
39
|
+
getEventLevel(eventId: string): Promise<string | null>;
|
|
40
|
+
getSessionTurns(sessionId: string, options?: { limit?: number; offset?: number }): Promise<MemorySessionTurn[]>;
|
|
41
|
+
getEventsByTurn(turnId: string): Promise<MemoryEvent[]>;
|
|
42
|
+
countSessionTurns(sessionId: string): Promise<number>;
|
|
43
|
+
backfillTurnIds(): Promise<number>;
|
|
44
|
+
deleteSessionEvents(sessionId: string): Promise<number>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface MemoryQueryServiceDeps {
|
|
48
|
+
vectorStore: { count(): Promise<number> };
|
|
49
|
+
graduation: { getStats(): Promise<Array<{ level: string; count: number }>> };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Thin-core query service for lightweight read and maintenance paths.
|
|
54
|
+
*
|
|
55
|
+
* Higher-level retrieval orchestration lives in RetrievalOrchestrator;
|
|
56
|
+
* this service keeps storage-backed read models and maintenance delegates separate.
|
|
57
|
+
*/
|
|
58
|
+
export class MemoryQueryService {
|
|
59
|
+
constructor(
|
|
60
|
+
private readonly initialize: () => Promise<void>,
|
|
61
|
+
private readonly queryStore: QueryStore,
|
|
62
|
+
private readonly deps?: MemoryQueryServiceDeps
|
|
63
|
+
) {}
|
|
64
|
+
|
|
65
|
+
async keywordSearch(
|
|
66
|
+
query: string,
|
|
67
|
+
options?: { topK?: number; minScore?: number }
|
|
68
|
+
): Promise<Array<{ event: MemoryEvent; score: number }>> {
|
|
69
|
+
await this.initialize();
|
|
70
|
+
|
|
71
|
+
const results = await this.queryStore.keywordSearch(query, options?.topK ?? 10);
|
|
72
|
+
if (results.length === 0) return [];
|
|
73
|
+
|
|
74
|
+
const maxRank = Math.min(...results.map((r) => r.rank), -0.001);
|
|
75
|
+
const minRank = Math.max(...results.map((r) => r.rank), -1000);
|
|
76
|
+
const rankRange = maxRank - minRank || 1;
|
|
77
|
+
|
|
78
|
+
return results
|
|
79
|
+
.map((r) => ({
|
|
80
|
+
event: r.event,
|
|
81
|
+
score: 1 - (r.rank - minRank) / rankRange
|
|
82
|
+
}))
|
|
83
|
+
.filter((r) => !options?.minScore || r.score >= options.minScore);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async getSessionHistory(sessionId: string): Promise<MemoryEvent[]> {
|
|
87
|
+
await this.initialize();
|
|
88
|
+
return this.queryStore.getSessionEvents(sessionId);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async getRecentEvents(limit: number = 100): Promise<MemoryEvent[]> {
|
|
92
|
+
await this.initialize();
|
|
93
|
+
return this.queryStore.getRecentEvents(limit);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async rebuildFtsIndex(): Promise<number> {
|
|
97
|
+
await this.initialize();
|
|
98
|
+
return this.getMaintenanceStore('rebuildFtsIndex').rebuildFtsIndex();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async getOutboxStats(): Promise<MemoryOutboxStats> {
|
|
102
|
+
await this.initialize();
|
|
103
|
+
return this.getMaintenanceStore('getOutboxStats').getOutboxStats();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async getStats(): Promise<MemoryStats> {
|
|
107
|
+
await this.initialize();
|
|
108
|
+
|
|
109
|
+
const deps = this.getStatsDeps();
|
|
110
|
+
const recentEvents = await this.queryStore.getRecentEvents(10000);
|
|
111
|
+
const vectorCount = await deps.vectorStore.count();
|
|
112
|
+
const levelStats = await deps.graduation.getStats();
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
totalEvents: recentEvents.length,
|
|
116
|
+
vectorCount,
|
|
117
|
+
levelStats
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async getEventsByLevel(level: string, options?: { limit?: number; offset?: number }): Promise<MemoryEvent[]> {
|
|
122
|
+
await this.initialize();
|
|
123
|
+
return this.getMaintenanceStore('getEventsByLevel').getEventsByLevel(level, options);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async getEventLevel(eventId: string): Promise<string | null> {
|
|
127
|
+
await this.initialize();
|
|
128
|
+
return this.getMaintenanceStore('getEventLevel').getEventLevel(eventId);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async getSessionTurns(
|
|
132
|
+
sessionId: string,
|
|
133
|
+
options?: { limit?: number; offset?: number }
|
|
134
|
+
): Promise<MemorySessionTurn[]> {
|
|
135
|
+
await this.initialize();
|
|
136
|
+
return this.getMaintenanceStore('getSessionTurns').getSessionTurns(sessionId, options);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async getEventsByTurn(turnId: string): Promise<MemoryEvent[]> {
|
|
140
|
+
await this.initialize();
|
|
141
|
+
return this.getMaintenanceStore('getEventsByTurn').getEventsByTurn(turnId);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async countSessionTurns(sessionId: string): Promise<number> {
|
|
145
|
+
await this.initialize();
|
|
146
|
+
return this.getMaintenanceStore('countSessionTurns').countSessionTurns(sessionId);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async backfillTurnIds(): Promise<number> {
|
|
150
|
+
await this.initialize();
|
|
151
|
+
return this.getMaintenanceStore('backfillTurnIds').backfillTurnIds();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async deleteSessionEvents(sessionId: string): Promise<number> {
|
|
155
|
+
await this.initialize();
|
|
156
|
+
return this.getMaintenanceStore('deleteSessionEvents').deleteSessionEvents(sessionId);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private getMaintenanceStore(method: keyof QueryMaintenanceStore): QueryMaintenanceStore {
|
|
160
|
+
const store = this.queryStore as QueryStore & Partial<QueryMaintenanceStore>;
|
|
161
|
+
if (typeof store[method] !== 'function') {
|
|
162
|
+
throw new Error(`MemoryQueryService requires queryStore.${String(method)}() for this operation`);
|
|
163
|
+
}
|
|
164
|
+
return store as QueryMaintenanceStore;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private getStatsDeps(): MemoryQueryServiceDeps {
|
|
168
|
+
if (!this.deps) {
|
|
169
|
+
throw new Error('MemoryQueryService requires vectorStore and graduation dependencies for getStats()');
|
|
170
|
+
}
|
|
171
|
+
return this.deps;
|
|
172
|
+
}
|
|
173
|
+
}
|