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
|
+
import { createHash } from 'crypto';
|
|
2
|
+
|
|
3
|
+
import type { MemoryEvent } from '../types.js';
|
|
4
|
+
import type { MemoryFact, MemoryFactType } from '../model/memory-fact.js';
|
|
5
|
+
|
|
6
|
+
export interface FactDerivationOptions {
|
|
7
|
+
/** Fallback project hash when the source event metadata does not carry scope.project.hash. */
|
|
8
|
+
projectHash?: string;
|
|
9
|
+
/** Optional current time hook for deterministic tests. */
|
|
10
|
+
now?: Date;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const DEFAULT_PROJECT_HASH = 'default';
|
|
14
|
+
const MAX_FACT_TEXT_LENGTH = 600;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Create a stable, rebuild-safe fact id for an event-derived fact.
|
|
18
|
+
*
|
|
19
|
+
* Format intentionally avoids UUID randomness so derived fact stores can be
|
|
20
|
+
* dropped and rebuilt without producing duplicate logical facts.
|
|
21
|
+
*/
|
|
22
|
+
export function makeEventDerivedFactId(
|
|
23
|
+
eventId: string,
|
|
24
|
+
factType: MemoryFactType,
|
|
25
|
+
ordinal = 0
|
|
26
|
+
): string {
|
|
27
|
+
const digest = createHash('sha1')
|
|
28
|
+
.update(`${eventId}:${factType}:${ordinal}`)
|
|
29
|
+
.digest('hex')
|
|
30
|
+
.slice(0, 16);
|
|
31
|
+
|
|
32
|
+
return `fact:event:${eventId}:${factType}:${ordinal}:${digest}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export class FactDeriver {
|
|
36
|
+
deriveFromEvent(event: MemoryEvent, options: FactDerivationOptions = {}): MemoryFact[] {
|
|
37
|
+
const text = this.toFactText(event);
|
|
38
|
+
if (!text) return [];
|
|
39
|
+
|
|
40
|
+
const factType = this.inferFactType(event);
|
|
41
|
+
const now = (options.now ?? new Date()).toISOString();
|
|
42
|
+
const metadata = this.asRecord(event.metadata);
|
|
43
|
+
const projectHash = this.getProjectHash(metadata) ?? options.projectHash ?? DEFAULT_PROJECT_HASH;
|
|
44
|
+
const tags = this.getTags(metadata);
|
|
45
|
+
|
|
46
|
+
return [{
|
|
47
|
+
factId: makeEventDerivedFactId(event.id, factType),
|
|
48
|
+
projectHash,
|
|
49
|
+
factType,
|
|
50
|
+
text,
|
|
51
|
+
derivedFromEventIds: [event.id],
|
|
52
|
+
sourceKind: this.getSourceKind(event),
|
|
53
|
+
confidence: this.getConfidence(event),
|
|
54
|
+
importance: this.getImportance(event),
|
|
55
|
+
tags,
|
|
56
|
+
...(this.getFileRefs(metadata).length > 0 ? { fileRefs: this.getFileRefs(metadata) } : {}),
|
|
57
|
+
createdAt: now,
|
|
58
|
+
updatedAt: now
|
|
59
|
+
}];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private toFactText(event: MemoryEvent): string | null {
|
|
63
|
+
const content = event.content.trim().replace(/\s+/g, ' ');
|
|
64
|
+
if (!content) return null;
|
|
65
|
+
|
|
66
|
+
const clipped = content.length > MAX_FACT_TEXT_LENGTH
|
|
67
|
+
? `${content.slice(0, MAX_FACT_TEXT_LENGTH - 1)}…`
|
|
68
|
+
: content;
|
|
69
|
+
|
|
70
|
+
switch (event.eventType) {
|
|
71
|
+
case 'user_prompt':
|
|
72
|
+
return `User asked: ${clipped}`;
|
|
73
|
+
case 'agent_response':
|
|
74
|
+
return `Assistant responded: ${clipped}`;
|
|
75
|
+
case 'session_summary':
|
|
76
|
+
return `Session summary: ${clipped}`;
|
|
77
|
+
case 'tool_observation': {
|
|
78
|
+
const metadata = this.asRecord(event.metadata);
|
|
79
|
+
const toolName = typeof metadata.toolName === 'string' ? metadata.toolName : 'unknown_tool';
|
|
80
|
+
const success = typeof metadata.success === 'boolean'
|
|
81
|
+
? metadata.success
|
|
82
|
+
: undefined;
|
|
83
|
+
const status = success === undefined ? '' : success ? ' succeeded' : ' failed';
|
|
84
|
+
return `Tool ${toolName}${status}: ${clipped}`;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private inferFactType(event: MemoryEvent): MemoryFactType {
|
|
90
|
+
if (event.eventType === 'tool_observation') return 'tool_observation';
|
|
91
|
+
if (event.eventType === 'session_summary') return 'summary_fact';
|
|
92
|
+
|
|
93
|
+
const content = event.content.toLowerCase();
|
|
94
|
+
if (/\b(decided|decision|선택|결정)\b/.test(content)) return 'decision';
|
|
95
|
+
if (/\b(must|should not|constraint|requirement|제약|필수|금지)\b/.test(content)) return 'constraint';
|
|
96
|
+
if (/\b(todo|task|next|pending|해야|작업|진행)\b/.test(content)) return 'task_state';
|
|
97
|
+
if (/\b(prefer|preference|선호)\b/.test(content)) return 'preference';
|
|
98
|
+
if (/\b(src\/|tests\/|\.ts|\.tsx|\.js|\.py|function|class)\b/.test(content)) return 'code_context';
|
|
99
|
+
|
|
100
|
+
return 'task_state';
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private getSourceKind(event: MemoryEvent): MemoryFact['sourceKind'] {
|
|
104
|
+
switch (event.eventType) {
|
|
105
|
+
case 'user_prompt':
|
|
106
|
+
return 'prompt';
|
|
107
|
+
case 'agent_response':
|
|
108
|
+
case 'session_summary':
|
|
109
|
+
return 'assistant';
|
|
110
|
+
case 'tool_observation':
|
|
111
|
+
return 'tool';
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private getConfidence(event: MemoryEvent): number {
|
|
116
|
+
switch (event.eventType) {
|
|
117
|
+
case 'session_summary':
|
|
118
|
+
return 0.8;
|
|
119
|
+
case 'tool_observation':
|
|
120
|
+
return 0.75;
|
|
121
|
+
case 'agent_response':
|
|
122
|
+
return 0.7;
|
|
123
|
+
case 'user_prompt':
|
|
124
|
+
return 0.65;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private getImportance(event: MemoryEvent): number {
|
|
129
|
+
const metadata = this.asRecord(event.metadata);
|
|
130
|
+
const importance = metadata.importance;
|
|
131
|
+
if (typeof importance === 'number' && Number.isFinite(importance)) {
|
|
132
|
+
return Math.max(0, Math.min(1, importance));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (event.eventType === 'session_summary') return 0.8;
|
|
136
|
+
if (event.eventType === 'tool_observation') return 0.6;
|
|
137
|
+
return 0.5;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private getProjectHash(metadata: Record<string, unknown>): string | undefined {
|
|
141
|
+
const scope = this.asRecord(metadata.scope);
|
|
142
|
+
const project = this.asRecord(scope.project);
|
|
143
|
+
return typeof project.hash === 'string' && project.hash.length > 0
|
|
144
|
+
? project.hash
|
|
145
|
+
: undefined;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private getTags(metadata: Record<string, unknown>): string[] {
|
|
149
|
+
return Array.isArray(metadata.tags)
|
|
150
|
+
? metadata.tags.filter((tag): tag is string => typeof tag === 'string' && tag.length > 0)
|
|
151
|
+
: [];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private getFileRefs(metadata: Record<string, unknown>): string[] {
|
|
155
|
+
const refs = metadata.fileRefs ?? metadata.files;
|
|
156
|
+
return Array.isArray(refs)
|
|
157
|
+
? refs.filter((ref): ref is string => typeof ref === 'string' && ref.length > 0)
|
|
158
|
+
: [];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private asRecord(value: unknown): Record<string, unknown> {
|
|
162
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
163
|
+
? value as Record<string, unknown>
|
|
164
|
+
: {};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function createFactDeriver(): FactDeriver {
|
|
169
|
+
return new FactDeriver();
|
|
170
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { MemoryEvent } from '../types.js';
|
|
2
|
+
|
|
3
|
+
export interface SessionSummaryDerivation {
|
|
4
|
+
text: string;
|
|
5
|
+
metadata: {
|
|
6
|
+
generated: 'rule-based';
|
|
7
|
+
eventCount: number;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const MAX_FIRST_PROMPT_LENGTH = 120;
|
|
12
|
+
const MAX_TOOL_NAMES = 6;
|
|
13
|
+
|
|
14
|
+
export class SummaryDeriver {
|
|
15
|
+
/**
|
|
16
|
+
* Derive the current lightweight rule-based session summary from raw events.
|
|
17
|
+
*
|
|
18
|
+
* The deriver is intentionally pure: callers own persistence and lifecycle
|
|
19
|
+
* orchestration, while this class owns summary text and metadata decisions.
|
|
20
|
+
*/
|
|
21
|
+
deriveSessionSummary(events: MemoryEvent[]): SessionSummaryDerivation | null {
|
|
22
|
+
if (events.length < 3) return null;
|
|
23
|
+
if (events.some((event) => event.eventType === 'session_summary')) return null;
|
|
24
|
+
|
|
25
|
+
const prompts = events.filter((event) => event.eventType === 'user_prompt');
|
|
26
|
+
const toolObservations = events.filter((event) => event.eventType === 'tool_observation');
|
|
27
|
+
const toolNames = Array.from(new Set(
|
|
28
|
+
toolObservations
|
|
29
|
+
.map((event) => this.asRecord(event.metadata).toolName)
|
|
30
|
+
.filter((toolName): toolName is string => typeof toolName === 'string' && toolName.length > 0)
|
|
31
|
+
));
|
|
32
|
+
const errorObservations = toolObservations.filter((event) => this.isErrorObservation(event));
|
|
33
|
+
|
|
34
|
+
const datePart = events[0].timestamp.toISOString().split('T')[0];
|
|
35
|
+
const parts: string[] = [`[${datePart}] ${prompts.length}턴 세션`];
|
|
36
|
+
|
|
37
|
+
if (prompts.length > 0) {
|
|
38
|
+
parts.push(`주요 작업: ${this.firstPromptPreview(prompts[0].content)}`);
|
|
39
|
+
}
|
|
40
|
+
if (toolNames.length > 0) {
|
|
41
|
+
parts.push(`사용 툴: ${toolNames.slice(0, MAX_TOOL_NAMES).join(', ')}`);
|
|
42
|
+
}
|
|
43
|
+
if (errorObservations.length > 0) {
|
|
44
|
+
parts.push(`오류 ${errorObservations.length}건 발생`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
text: parts.join('. '),
|
|
49
|
+
metadata: { generated: 'rule-based', eventCount: events.length }
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private firstPromptPreview(content: string): string {
|
|
54
|
+
return content.slice(0, MAX_FIRST_PROMPT_LENGTH).replace(/\r?\n/g, ' ');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private isErrorObservation(event: MemoryEvent): boolean {
|
|
58
|
+
const metadata = this.asRecord(event.metadata);
|
|
59
|
+
|
|
60
|
+
if (metadata.exitCode !== undefined) {
|
|
61
|
+
return metadata.exitCode !== 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return metadata.success === false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private asRecord(value: unknown): Record<string, unknown> {
|
|
68
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
69
|
+
? value as Record<string, unknown>
|
|
70
|
+
: {};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function createSummaryDeriver(): SummaryDeriver {
|
|
75
|
+
return new SummaryDeriver();
|
|
76
|
+
}
|
package/src/core/embedder.ts
CHANGED
|
@@ -1,152 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import { pipeline, Pipeline } from '@huggingface/transformers';
|
|
7
|
-
|
|
8
|
-
export interface EmbeddingResult {
|
|
9
|
-
vector: number[];
|
|
10
|
-
model: string;
|
|
11
|
-
dimensions: number;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export class Embedder {
|
|
15
|
-
private pipeline: Pipeline | null = null;
|
|
16
|
-
private readonly modelName: string;
|
|
17
|
-
private activeModelName: string;
|
|
18
|
-
private initialized = false;
|
|
19
|
-
|
|
20
|
-
constructor(modelName: string = 'jinaai/jina-embeddings-v5-text-nano-text-matching') {
|
|
21
|
-
this.modelName = modelName;
|
|
22
|
-
this.activeModelName = modelName;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Initialize the embedding pipeline
|
|
27
|
-
*/
|
|
28
|
-
async initialize(): Promise<void> {
|
|
29
|
-
if (this.initialized) return;
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
this.pipeline = await pipeline('feature-extraction', this.modelName);
|
|
33
|
-
this.activeModelName = this.modelName;
|
|
34
|
-
this.initialized = true;
|
|
35
|
-
return;
|
|
36
|
-
} catch (primaryError) {
|
|
37
|
-
const fallbackModel = process.env.CLAUDE_MEMORY_EMBEDDING_FALLBACK_MODEL || 'onnx-community/embeddinggemma-300m-ONNX';
|
|
38
|
-
if (fallbackModel === this.modelName) {
|
|
39
|
-
throw primaryError;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
console.warn(`[Embedder] Primary model failed (${this.modelName}). Falling back to ${fallbackModel}`);
|
|
43
|
-
this.pipeline = await pipeline('feature-extraction', fallbackModel);
|
|
44
|
-
this.activeModelName = fallbackModel;
|
|
45
|
-
this.initialized = true;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// ~4 chars per token; 512 tokens * 4 = 2048, use 2000 to be safe
|
|
50
|
-
private static readonly MAX_CHARS = 2000;
|
|
51
|
-
|
|
52
|
-
private truncate(text: string): string {
|
|
53
|
-
return text.length > Embedder.MAX_CHARS ? text.slice(0, Embedder.MAX_CHARS) : text;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Generate embedding for a single text
|
|
58
|
-
*/
|
|
59
|
-
async embed(text: string): Promise<EmbeddingResult> {
|
|
60
|
-
await this.initialize();
|
|
61
|
-
|
|
62
|
-
if (!this.pipeline) {
|
|
63
|
-
throw new Error('Embedding pipeline not initialized');
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const output = await this.pipeline(this.truncate(text), {
|
|
67
|
-
pooling: 'mean',
|
|
68
|
-
normalize: true,
|
|
69
|
-
truncation: true,
|
|
70
|
-
max_length: 512
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
const vector = Array.from(output.data as Float32Array);
|
|
74
|
-
|
|
75
|
-
return {
|
|
76
|
-
vector,
|
|
77
|
-
model: this.activeModelName,
|
|
78
|
-
dimensions: vector.length
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Generate embeddings for multiple texts in batch
|
|
84
|
-
*/
|
|
85
|
-
async embedBatch(texts: string[]): Promise<EmbeddingResult[]> {
|
|
86
|
-
await this.initialize();
|
|
87
|
-
|
|
88
|
-
if (!this.pipeline) {
|
|
89
|
-
throw new Error('Embedding pipeline not initialized');
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const results: EmbeddingResult[] = [];
|
|
93
|
-
|
|
94
|
-
// Process in batches of 32 for memory efficiency
|
|
95
|
-
const batchSize = 32;
|
|
96
|
-
for (let i = 0; i < texts.length; i += batchSize) {
|
|
97
|
-
const batch = texts.slice(i, i + batchSize);
|
|
98
|
-
|
|
99
|
-
for (const text of batch) {
|
|
100
|
-
const output = await this.pipeline(this.truncate(text), {
|
|
101
|
-
pooling: 'mean',
|
|
102
|
-
normalize: true,
|
|
103
|
-
truncation: true,
|
|
104
|
-
max_length: 512
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
const vector = Array.from(output.data as Float32Array);
|
|
108
|
-
|
|
109
|
-
results.push({
|
|
110
|
-
vector,
|
|
111
|
-
model: this.activeModelName,
|
|
112
|
-
dimensions: vector.length
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return results;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Get embedding dimensions for the current model
|
|
122
|
-
*/
|
|
123
|
-
async getDimensions(): Promise<number> {
|
|
124
|
-
const result = await this.embed('test');
|
|
125
|
-
return result.dimensions;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Check if embedder is ready
|
|
130
|
-
*/
|
|
131
|
-
isReady(): boolean {
|
|
132
|
-
return this.initialized && this.pipeline !== null;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Get model name
|
|
137
|
-
*/
|
|
138
|
-
getModelName(): string {
|
|
139
|
-
return this.activeModelName;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Singleton instance for reuse
|
|
144
|
-
let defaultEmbedder: Embedder | null = null;
|
|
145
|
-
|
|
146
|
-
export function getDefaultEmbedder(): Embedder {
|
|
147
|
-
const envModel = process.env.CLAUDE_MEMORY_EMBEDDING_MODEL;
|
|
148
|
-
if (!defaultEmbedder) {
|
|
149
|
-
defaultEmbedder = new Embedder(envModel || undefined);
|
|
150
|
-
}
|
|
151
|
-
return defaultEmbedder;
|
|
152
|
-
}
|
|
1
|
+
// Compatibility re-export. Embedding is an optional vector extension, but
|
|
2
|
+
// existing core/engine imports still resolve through this path during the
|
|
3
|
+
// strangler migration.
|
|
4
|
+
export * from '../extensions/vector/index.js';
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
export interface EmbeddingMaintenanceEvent {
|
|
5
|
+
id: string;
|
|
6
|
+
content: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface EmbeddingMaintenanceEventStore {
|
|
10
|
+
clearEmbeddingOutbox(): Promise<void>;
|
|
11
|
+
getEventsPage(limit: number, offset: number): Promise<EmbeddingMaintenanceEvent[]>;
|
|
12
|
+
enqueueForEmbedding(eventId: string, content: string): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface EmbeddingMaintenanceVectorStore {
|
|
16
|
+
count(): Promise<number>;
|
|
17
|
+
clearAll(): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface EmbeddingMaintenanceVectorWorker {
|
|
21
|
+
isRunning(): boolean;
|
|
22
|
+
stop(): void;
|
|
23
|
+
start(): void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface EmbeddingMaintenanceFileSystem {
|
|
27
|
+
existsSync(targetPath: string): boolean;
|
|
28
|
+
readFileSync(targetPath: string, encoding: BufferEncoding): string;
|
|
29
|
+
writeFileSync(targetPath: string, content: string): void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface EmbeddingModelMaintenanceOptions {
|
|
33
|
+
autoMigrate?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface EmbeddingModelMaintenanceResult {
|
|
37
|
+
changed: boolean;
|
|
38
|
+
previousModel: string | null;
|
|
39
|
+
currentModel: string;
|
|
40
|
+
enqueued: number;
|
|
41
|
+
reason?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface EmbeddingMaintenanceServiceOptions {
|
|
45
|
+
storagePath: string;
|
|
46
|
+
initialize: () => Promise<void>;
|
|
47
|
+
getEmbeddingModelName: () => string;
|
|
48
|
+
vectorStore: EmbeddingMaintenanceVectorStore;
|
|
49
|
+
eventStore: EmbeddingMaintenanceEventStore;
|
|
50
|
+
getVectorWorker: () => EmbeddingMaintenanceVectorWorker | null;
|
|
51
|
+
fileSystem?: EmbeddingMaintenanceFileSystem;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface EmbeddingMaintenanceService {
|
|
55
|
+
getEmbeddingModelName(): string;
|
|
56
|
+
ensureEmbeddingModelForImport(options?: EmbeddingModelMaintenanceOptions): Promise<EmbeddingModelMaintenanceResult>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const DEFAULT_PAGE_SIZE = 1000;
|
|
60
|
+
|
|
61
|
+
const defaultFileSystem: EmbeddingMaintenanceFileSystem = {
|
|
62
|
+
existsSync: fs.existsSync,
|
|
63
|
+
readFileSync: (targetPath, encoding) => fs.readFileSync(targetPath, encoding),
|
|
64
|
+
writeFileSync: (targetPath, content) => fs.writeFileSync(targetPath, content)
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
class DefaultEmbeddingMaintenanceService implements EmbeddingMaintenanceService {
|
|
68
|
+
private readonly fileSystem: EmbeddingMaintenanceFileSystem;
|
|
69
|
+
|
|
70
|
+
constructor(private readonly options: EmbeddingMaintenanceServiceOptions) {
|
|
71
|
+
this.fileSystem = options.fileSystem ?? defaultFileSystem;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
getEmbeddingModelName(): string {
|
|
75
|
+
return this.options.getEmbeddingModelName();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async ensureEmbeddingModelForImport(
|
|
79
|
+
options?: EmbeddingModelMaintenanceOptions
|
|
80
|
+
): Promise<EmbeddingModelMaintenanceResult> {
|
|
81
|
+
await this.options.initialize();
|
|
82
|
+
|
|
83
|
+
const currentModel = this.getEmbeddingModelName();
|
|
84
|
+
const metaPath = path.join(this.options.storagePath, 'embedding-meta.json');
|
|
85
|
+
const previousModel = this.readPreviousModel(metaPath);
|
|
86
|
+
const vectorCount = await this.options.vectorStore.count();
|
|
87
|
+
const hasExistingVectors = vectorCount > 0;
|
|
88
|
+
|
|
89
|
+
// First-time metadata write (no migration needed unless legacy vectors exist)
|
|
90
|
+
if (!previousModel && !hasExistingVectors) {
|
|
91
|
+
this.fileSystem.writeFileSync(
|
|
92
|
+
metaPath,
|
|
93
|
+
JSON.stringify({ model: currentModel, updatedAt: new Date().toISOString() }, null, 2)
|
|
94
|
+
);
|
|
95
|
+
return { changed: false, previousModel: null, currentModel, enqueued: 0, reason: 'initialized-meta' };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const modelChanged = previousModel !== currentModel;
|
|
99
|
+
const legacyUnknownButVectorsExist = !previousModel && hasExistingVectors;
|
|
100
|
+
|
|
101
|
+
if (!modelChanged && !legacyUnknownButVectorsExist) {
|
|
102
|
+
return { changed: false, previousModel, currentModel, enqueued: 0 };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (options?.autoMigrate === false) {
|
|
106
|
+
return {
|
|
107
|
+
changed: true,
|
|
108
|
+
previousModel,
|
|
109
|
+
currentModel,
|
|
110
|
+
enqueued: 0,
|
|
111
|
+
reason: legacyUnknownButVectorsExist ? 'legacy-vectors-without-meta' : 'model-mismatch'
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const worker = this.options.getVectorWorker();
|
|
116
|
+
const wasRunning = worker?.isRunning() || false;
|
|
117
|
+
if (wasRunning) worker?.stop();
|
|
118
|
+
|
|
119
|
+
await this.options.vectorStore.clearAll();
|
|
120
|
+
await this.options.eventStore.clearEmbeddingOutbox();
|
|
121
|
+
|
|
122
|
+
const enqueued = await this.reenqueueAllEvents();
|
|
123
|
+
|
|
124
|
+
this.fileSystem.writeFileSync(
|
|
125
|
+
metaPath,
|
|
126
|
+
JSON.stringify(
|
|
127
|
+
{
|
|
128
|
+
model: currentModel,
|
|
129
|
+
previousModel,
|
|
130
|
+
migratedAt: new Date().toISOString(),
|
|
131
|
+
enqueued
|
|
132
|
+
},
|
|
133
|
+
null,
|
|
134
|
+
2
|
|
135
|
+
)
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
if (wasRunning) worker?.start();
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
changed: true,
|
|
142
|
+
previousModel,
|
|
143
|
+
currentModel,
|
|
144
|
+
enqueued,
|
|
145
|
+
reason: legacyUnknownButVectorsExist ? 'legacy-vectors-without-meta' : 'model-mismatch'
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private readPreviousModel(metaPath: string): string | null {
|
|
150
|
+
try {
|
|
151
|
+
if (this.fileSystem.existsSync(metaPath)) {
|
|
152
|
+
const parsed = JSON.parse(this.fileSystem.readFileSync(metaPath, 'utf-8')) as { model?: string };
|
|
153
|
+
return parsed?.model || null;
|
|
154
|
+
}
|
|
155
|
+
} catch {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private async reenqueueAllEvents(): Promise<number> {
|
|
163
|
+
let offset = 0;
|
|
164
|
+
let enqueued = 0;
|
|
165
|
+
|
|
166
|
+
while (true) {
|
|
167
|
+
const page = await this.options.eventStore.getEventsPage(DEFAULT_PAGE_SIZE, offset);
|
|
168
|
+
if (page.length === 0) break;
|
|
169
|
+
|
|
170
|
+
for (const event of page) {
|
|
171
|
+
await this.options.eventStore.enqueueForEmbedding(event.id, event.content);
|
|
172
|
+
enqueued += 1;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
offset += page.length;
|
|
176
|
+
if (page.length < DEFAULT_PAGE_SIZE) break;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return enqueued;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function createEmbeddingMaintenanceService(
|
|
184
|
+
options: EmbeddingMaintenanceServiceOptions
|
|
185
|
+
): EmbeddingMaintenanceService {
|
|
186
|
+
return new DefaultEmbeddingMaintenanceService(options);
|
|
187
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export * from './memory-ingest-service.js';
|
|
2
|
+
export * from './memory-query-service.js';
|
|
3
|
+
export * from './memory-engine-services.js';
|
|
4
|
+
export * from './retrieval-orchestrator.js';
|
|
5
|
+
export * from './retrieval-disclosure-service.js';
|
|
6
|
+
export * from './retrieval-analytics-service.js';
|
|
7
|
+
export * from './shared-memory-services.js';
|
|
8
|
+
export * from './endless-memory-services.js';
|
|
9
|
+
export * from './embedding-maintenance-service.js';
|
|
10
|
+
export * from './memory-runtime-service.js';
|
|
11
|
+
export {
|
|
12
|
+
createRetrievalServices
|
|
13
|
+
} from './retrieval-services.js';
|
|
14
|
+
export type {
|
|
15
|
+
CreateRetrieverFn,
|
|
16
|
+
RetrievalEventStore,
|
|
17
|
+
RetrievalServices,
|
|
18
|
+
RetrievalServicesDeps
|
|
19
|
+
} from './retrieval-services.js';
|