claude-memory-layer 1.0.27 → 1.0.29
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 +374 -49
- 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 +12 -5
- package/scripts/build.ts +25 -8
- package/scripts/generate-session-qrels.ts +126 -0
- package/scripts/postinstall-embedding-backend.cjs +142 -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/postinstall-embedding-backend.test.ts +167 -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
|
@@ -1,344 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* Called when user submits a prompt - retrieves relevant memories.
|
|
3
|
+
* Compatibility entrypoint for the Claude user-prompt-submit hook.
|
|
5
4
|
*
|
|
6
|
-
*
|
|
7
|
-
* - keyword (default-fast): SQLite FTS5 only, no ML model (~10ms)
|
|
8
|
-
* - semantic: vector search via long-running semantic daemon (~15-20ms warm)
|
|
9
|
-
* - hybrid: semantic first, keyword fallback (default)
|
|
10
|
-
*
|
|
11
|
-
* The semantic daemon keeps the embedding model in memory across hook invocations,
|
|
12
|
-
* avoiding per-request model initialization (~730ms cold start).
|
|
13
|
-
*
|
|
14
|
-
* Turn Grouping: Generates a turn_id and persists it to a state file
|
|
15
|
-
* so PostToolUse and Stop hooks can associate their events with this turn.
|
|
5
|
+
* Implementation lives in the Claude adapter layer so core stays platform-agnostic.
|
|
16
6
|
*/
|
|
17
|
-
|
|
18
|
-
import { randomUUID } from 'crypto';
|
|
19
|
-
import * as fs from 'fs';
|
|
20
|
-
import * as path from 'path';
|
|
21
|
-
import * as os from 'os';
|
|
22
|
-
import { getLightweightMemoryService } from '../services/memory-service.js';
|
|
23
|
-
import { writeTurnState, readLastAssistantSnippet } from '../core/turn-state.js';
|
|
24
|
-
import { retrieveSemanticMemories } from './semantic-daemon-client.js';
|
|
25
|
-
import type { UserPromptSubmitInput, UserPromptSubmitOutput } from '../core/types.js';
|
|
26
|
-
|
|
27
|
-
// Configuration
|
|
28
|
-
const MAX_MEMORIES = parseInt(process.env.CLAUDE_MEMORY_MAX_COUNT || '5');
|
|
29
|
-
// Tuned default for noise/recall balance on shopping_assistant-like corpus
|
|
30
|
-
const BASE_MIN_SCORE = parseFloat(process.env.CLAUDE_MEMORY_MIN_SCORE || '0.4');
|
|
31
|
-
const FALLBACK_MIN_SCORE = parseFloat(process.env.CLAUDE_MEMORY_FALLBACK_MIN_SCORE || '0.3');
|
|
32
|
-
const ENABLE_SEARCH = process.env.CLAUDE_MEMORY_SEARCH !== 'false';
|
|
33
|
-
const RETRIEVAL_MODE = (process.env.CLAUDE_MEMORY_RETRIEVAL_MODE || 'hybrid') as 'keyword' | 'semantic' | 'hybrid';
|
|
34
|
-
const SEMANTIC_TIMEOUT_MS = parseInt(process.env.CLAUDE_MEMORY_SEMANTIC_TIMEOUT_MS || '2000');
|
|
35
|
-
const ADHERENCE_INTERVAL_TURNS = parseInt(process.env.CLAUDE_MEMORY_ADHERENCE_INTERVAL_TURNS || '3');
|
|
36
|
-
|
|
37
|
-
const ADHERENCE_STATE_DIR = path.join(os.homedir(), '.claude-code', 'memory');
|
|
38
|
-
|
|
39
|
-
interface AdherenceState {
|
|
40
|
-
sessionId: string;
|
|
41
|
-
turnCount: number;
|
|
42
|
-
lastCheckedTurn: number;
|
|
43
|
-
lastPrompt: string;
|
|
44
|
-
lastReason?: string;
|
|
45
|
-
updatedAt: string;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Determine if a prompt is worth storing as a memory.
|
|
50
|
-
* Filters slash commands, very short inputs, and trivial patterns.
|
|
51
|
-
*/
|
|
52
|
-
function shouldStorePrompt(prompt: string): boolean {
|
|
53
|
-
const trimmed = prompt.trim();
|
|
54
|
-
if (trimmed.startsWith('/')) return false;
|
|
55
|
-
if (trimmed.length < 15) return false;
|
|
56
|
-
if (!/[a-zA-Z가-힣]{2,}/.test(trimmed)) return false;
|
|
57
|
-
return true;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
function getDynamicMinScore(prompt: string): number {
|
|
62
|
-
const len = prompt.trim().length;
|
|
63
|
-
if (len <= 20) return Math.min(0.55, BASE_MIN_SCORE + 0.1); // short query → stricter
|
|
64
|
-
if (len >= 80) return Math.max(0.3, BASE_MIN_SCORE - 0.05); // long query → slightly looser
|
|
65
|
-
return BASE_MIN_SCORE;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function formatMemoryContext(items: Array<{ type: string; content: string }>): string {
|
|
69
|
-
if (items.length === 0) return '';
|
|
70
|
-
const lines = items.map((m) => {
|
|
71
|
-
const preview = m.content.length > 300 ? m.content.substring(0, 300) + '...' : m.content;
|
|
72
|
-
return `- [${m.type}] ${preview}`;
|
|
73
|
-
});
|
|
74
|
-
return `💡 **Related memories found:**\n\n${lines.join('\n\n')}`;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function getAdherenceStatePath(sessionId: string): string {
|
|
78
|
-
return path.join(ADHERENCE_STATE_DIR, `.adherence-state-${sessionId}.json`);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function readAdherenceState(sessionId: string): AdherenceState {
|
|
82
|
-
try {
|
|
83
|
-
const filePath = getAdherenceStatePath(sessionId);
|
|
84
|
-
if (!fs.existsSync(filePath)) {
|
|
85
|
-
return {
|
|
86
|
-
sessionId,
|
|
87
|
-
turnCount: 0,
|
|
88
|
-
lastCheckedTurn: 0,
|
|
89
|
-
lastPrompt: '',
|
|
90
|
-
lastReason: 'init',
|
|
91
|
-
updatedAt: new Date().toISOString()
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const data = fs.readFileSync(filePath, 'utf8');
|
|
96
|
-
const parsed = JSON.parse(data) as AdherenceState;
|
|
97
|
-
if (parsed.sessionId !== sessionId) throw new Error('session mismatch');
|
|
98
|
-
return parsed;
|
|
99
|
-
} catch {
|
|
100
|
-
return {
|
|
101
|
-
sessionId,
|
|
102
|
-
turnCount: 0,
|
|
103
|
-
lastCheckedTurn: 0,
|
|
104
|
-
lastPrompt: '',
|
|
105
|
-
lastReason: 'init',
|
|
106
|
-
updatedAt: new Date().toISOString()
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function writeAdherenceState(state: AdherenceState): void {
|
|
112
|
-
try {
|
|
113
|
-
if (!fs.existsSync(ADHERENCE_STATE_DIR)) {
|
|
114
|
-
fs.mkdirSync(ADHERENCE_STATE_DIR, { recursive: true });
|
|
115
|
-
}
|
|
116
|
-
const filePath = getAdherenceStatePath(state.sessionId);
|
|
117
|
-
const tempPath = filePath + '.tmp';
|
|
118
|
-
fs.writeFileSync(tempPath, JSON.stringify(state));
|
|
119
|
-
fs.renameSync(tempPath, filePath);
|
|
120
|
-
} catch {
|
|
121
|
-
// non-critical
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function hasWriteIntent(prompt: string): boolean {
|
|
126
|
-
return /(fix|refactor|implement|change|modify|edit|update|rewrite|patch|create|add|remove|delete|버그|수정|리팩터|구현|추가|삭제|개선)/i.test(prompt);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function tokenize(text: string): string[] {
|
|
130
|
-
const stopwords = new Set(['the', 'and', 'for', 'with', 'that', 'this', 'from', 'have', 'what', 'when', 'where', 'how', 'why', '그리고', '그리고요', '이거', '그거', '해주세요', '해줘', '좀', '에서', '으로', '하는', '해']);
|
|
131
|
-
return text
|
|
132
|
-
.toLowerCase()
|
|
133
|
-
.replace(/[^a-z0-9가-힣\s]/g, ' ')
|
|
134
|
-
.split(/\s+/)
|
|
135
|
-
.filter((w) => w.length >= 2 && !stopwords.has(w));
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function isTopicShift(currentPrompt: string, lastPrompt: string): boolean {
|
|
139
|
-
if (!lastPrompt || lastPrompt.length < 10) return false;
|
|
140
|
-
const a = new Set(tokenize(currentPrompt));
|
|
141
|
-
const b = new Set(tokenize(lastPrompt));
|
|
142
|
-
if (a.size === 0 || b.size === 0) return false;
|
|
143
|
-
|
|
144
|
-
let intersection = 0;
|
|
145
|
-
for (const token of a) {
|
|
146
|
-
if (b.has(token)) intersection++;
|
|
147
|
-
}
|
|
148
|
-
const union = a.size + b.size - intersection;
|
|
149
|
-
const similarity = union > 0 ? intersection / union : 0;
|
|
150
|
-
return similarity < 0.2;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function shouldRunAdherenceCheck(turnCount: number, prompt: string, state: AdherenceState): { run: boolean; reason: string } {
|
|
154
|
-
if (turnCount === 1) return { run: true, reason: 'first-turn' };
|
|
155
|
-
if (hasWriteIntent(prompt)) return { run: true, reason: 'write-intent' };
|
|
156
|
-
if (isTopicShift(prompt, state.lastPrompt)) return { run: true, reason: 'topic-shift' };
|
|
157
|
-
if (turnCount - state.lastCheckedTurn >= ADHERENCE_INTERVAL_TURNS) return { run: true, reason: 'interval' };
|
|
158
|
-
return { run: false, reason: 'skip' };
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function logAdherenceDecision(sessionId: string, turn: number, run: boolean, reason: string): void {
|
|
162
|
-
if (!process.env.CLAUDE_MEMORY_DEBUG) return;
|
|
163
|
-
const mode = run ? 'enforced' : 'skipped';
|
|
164
|
-
console.error(`[adherence] session=${sessionId} turn=${turn} mode=${mode} reason=${reason}`);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
async function main(): Promise<void> {
|
|
168
|
-
// Read input from stdin
|
|
169
|
-
const inputData = await readStdin();
|
|
170
|
-
const input: UserPromptSubmitInput = JSON.parse(inputData);
|
|
171
|
-
|
|
172
|
-
// Generate a new turn_id for this user prompt
|
|
173
|
-
// This groups the prompt with subsequent tool calls and the final agent response
|
|
174
|
-
const turnId = randomUUID();
|
|
175
|
-
|
|
176
|
-
// Persist turn state so PostToolUse and Stop hooks can read it
|
|
177
|
-
writeTurnState(input.session_id, turnId);
|
|
178
|
-
|
|
179
|
-
// Use lightweight service (SQLite only, no embedder/vector - FAST!)
|
|
180
|
-
const memoryService = getLightweightMemoryService(input.session_id);
|
|
181
|
-
|
|
182
|
-
try {
|
|
183
|
-
let context = '';
|
|
184
|
-
|
|
185
|
-
const adherenceState = readAdherenceState(input.session_id);
|
|
186
|
-
const currentTurn = adherenceState.turnCount + 1;
|
|
187
|
-
const adherenceDecision = shouldRunAdherenceCheck(currentTurn, input.prompt, adherenceState);
|
|
188
|
-
logAdherenceDecision(input.session_id, currentTurn, adherenceDecision.run, adherenceDecision.reason);
|
|
189
|
-
|
|
190
|
-
// On first turn of a new session, backfill helpfulness for sessions
|
|
191
|
-
// that ended without Stop hook (crash, force-close, etc.)
|
|
192
|
-
if (currentTurn === 1) {
|
|
193
|
-
memoryService.evaluatePendingSessions(input.session_id).catch(() => {});
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Store only non-trivial prompts (skip /commands, short inputs)
|
|
197
|
-
if (shouldStorePrompt(input.prompt)) {
|
|
198
|
-
await memoryService.storeUserPrompt(
|
|
199
|
-
input.session_id,
|
|
200
|
-
input.prompt,
|
|
201
|
-
{
|
|
202
|
-
turnId,
|
|
203
|
-
adherence: {
|
|
204
|
-
checked: adherenceDecision.run,
|
|
205
|
-
reason: adherenceDecision.reason,
|
|
206
|
-
turn: currentTurn
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// Search strategy: turn-1 always enforce adherence check,
|
|
213
|
-
// then adaptively enforce on write-intent/topic-shift/interval
|
|
214
|
-
const isSlashCommand = input.prompt.trimStart().startsWith('/');
|
|
215
|
-
if (ENABLE_SEARCH && !isSlashCommand && input.prompt.length > 10 && adherenceDecision.run) {
|
|
216
|
-
const minScore = getDynamicMinScore(input.prompt);
|
|
217
|
-
let mergedMemories: Array<{ type: string; content: string; id?: string; score?: number }> = [];
|
|
218
|
-
|
|
219
|
-
// On turn 2+, enrich the retrieval query with the previous assistant response
|
|
220
|
-
// so short/ambiguous follow-ups ("그거 고쳐줘") resolve correctly.
|
|
221
|
-
const lastSnippet = currentTurn > 1 ? readLastAssistantSnippet(input.session_id) : null;
|
|
222
|
-
const retrievalQuery = lastSnippet
|
|
223
|
-
? `${lastSnippet}\n\n${input.prompt}`
|
|
224
|
-
: input.prompt;
|
|
225
|
-
|
|
226
|
-
const canUseSemantic = RETRIEVAL_MODE === 'semantic' || RETRIEVAL_MODE === 'hybrid';
|
|
227
|
-
if (canUseSemantic) {
|
|
228
|
-
try {
|
|
229
|
-
mergedMemories = await retrieveSemanticMemories(
|
|
230
|
-
{
|
|
231
|
-
sessionId: input.session_id,
|
|
232
|
-
prompt: retrievalQuery,
|
|
233
|
-
topK: MAX_MEMORIES,
|
|
234
|
-
minScore
|
|
235
|
-
},
|
|
236
|
-
SEMANTIC_TIMEOUT_MS
|
|
237
|
-
);
|
|
238
|
-
} catch {
|
|
239
|
-
// Semantic retrieval is best-effort; fallback below handles the rest
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
const shouldUseKeywordFallback =
|
|
244
|
-
RETRIEVAL_MODE === 'keyword' ||
|
|
245
|
-
RETRIEVAL_MODE === 'hybrid' ||
|
|
246
|
-
mergedMemories.length === 0;
|
|
247
|
-
|
|
248
|
-
if (shouldUseKeywordFallback && mergedMemories.length < MAX_MEMORIES) {
|
|
249
|
-
let results = await memoryService.keywordSearch(retrievalQuery, {
|
|
250
|
-
topK: MAX_MEMORIES,
|
|
251
|
-
minScore
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
// recall rescue: if nothing found at tuned threshold, retry with fallback floor
|
|
255
|
-
if (results.length === 0 && FALLBACK_MIN_SCORE < minScore) {
|
|
256
|
-
results = await memoryService.keywordSearch(retrievalQuery, {
|
|
257
|
-
topK: MAX_MEMORIES,
|
|
258
|
-
minScore: FALLBACK_MIN_SCORE
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
const existingIds = new Set(mergedMemories.map((m) => m.id).filter(Boolean));
|
|
263
|
-
for (const r of results) {
|
|
264
|
-
if (existingIds.has(r.event.id)) continue;
|
|
265
|
-
mergedMemories.push({
|
|
266
|
-
type: r.event.eventType,
|
|
267
|
-
content: r.event.content,
|
|
268
|
-
id: r.event.id,
|
|
269
|
-
score: r.score
|
|
270
|
-
});
|
|
271
|
-
if (mergedMemories.length >= MAX_MEMORIES) break;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
if (mergedMemories.length > 0) {
|
|
276
|
-
// Increment access count for found memories
|
|
277
|
-
const eventIds = mergedMemories.map((m) => m.id).filter((v): v is string => Boolean(v));
|
|
278
|
-
if (eventIds.length > 0) {
|
|
279
|
-
await memoryService.incrementMemoryAccess(eventIds);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// Record each retrieval for helpfulness tracking
|
|
283
|
-
for (const m of mergedMemories) {
|
|
284
|
-
if (!m.id) continue;
|
|
285
|
-
try {
|
|
286
|
-
await memoryService.recordRetrieval(
|
|
287
|
-
m.id,
|
|
288
|
-
input.session_id,
|
|
289
|
-
m.score ?? minScore,
|
|
290
|
-
input.prompt
|
|
291
|
-
);
|
|
292
|
-
} catch { /* non-critical */ }
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
context = formatMemoryContext(mergedMemories);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// Record query-level trace for dashboard stats (retrieval_traces table)
|
|
299
|
-
const allCandidateIds = mergedMemories.map((m) => m.id).filter((v): v is string => Boolean(v));
|
|
300
|
-
try {
|
|
301
|
-
await memoryService.recordQueryTrace({
|
|
302
|
-
sessionId: input.session_id,
|
|
303
|
-
queryText: retrievalQuery,
|
|
304
|
-
strategy: RETRIEVAL_MODE,
|
|
305
|
-
candidateEventIds: allCandidateIds,
|
|
306
|
-
selectedEventIds: allCandidateIds,
|
|
307
|
-
confidence: mergedMemories.length > 0 ? 'medium' : 'none'
|
|
308
|
-
});
|
|
309
|
-
} catch { /* non-critical */ }
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
writeAdherenceState({
|
|
313
|
-
sessionId: input.session_id,
|
|
314
|
-
turnCount: currentTurn,
|
|
315
|
-
lastCheckedTurn: adherenceDecision.run ? currentTurn : adherenceState.lastCheckedTurn,
|
|
316
|
-
lastPrompt: input.prompt,
|
|
317
|
-
lastReason: adherenceDecision.reason,
|
|
318
|
-
updatedAt: new Date().toISOString()
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
const output: UserPromptSubmitOutput = { context };
|
|
322
|
-
console.log(JSON.stringify(output));
|
|
323
|
-
} catch (error) {
|
|
324
|
-
if (process.env.CLAUDE_MEMORY_DEBUG) {
|
|
325
|
-
console.error('Memory hook error:', error);
|
|
326
|
-
}
|
|
327
|
-
console.log(JSON.stringify({ context: '' }));
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
function readStdin(): Promise<string> {
|
|
332
|
-
return new Promise((resolve) => {
|
|
333
|
-
let data = '';
|
|
334
|
-
process.stdin.setEncoding('utf8');
|
|
335
|
-
process.stdin.on('data', (chunk) => {
|
|
336
|
-
data += chunk;
|
|
337
|
-
});
|
|
338
|
-
process.stdin.on('end', () => {
|
|
339
|
-
resolve(data);
|
|
340
|
-
});
|
|
341
|
-
});
|
|
342
|
-
}
|
|
7
|
+
import { main } from '../adapters/claude/hooks/user-prompt-submit.js';
|
|
343
8
|
|
|
344
9
|
main().catch(console.error);
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public package entrypoint.
|
|
3
|
+
*
|
|
4
|
+
* Keep this file lightweight: consumers importing `claude-memory-layer` should
|
|
5
|
+
* receive the core/service APIs described by package.json `main` without having
|
|
6
|
+
* to import CLI or hook executables.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export * from './core/index.js';
|
|
10
|
+
export * from './services/memory-service.js';
|
|
11
|
+
export * from './services/memory-service-config.js';
|
|
12
|
+
export * from './services/memory-service-registry.js';
|
|
13
|
+
export * from './services/codex-session-history-importer.js';
|
package/src/mcp/handlers.ts
CHANGED
|
@@ -1,212 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
3
|
-
* Implementation of tool calls
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { getDefaultMemoryService } from '../services/memory-service.js';
|
|
7
|
-
import { generateCitationId } from '../core/citation-generator.js';
|
|
8
|
-
|
|
9
|
-
interface ToolResult {
|
|
10
|
-
content: Array<{ type: string; text: string }>;
|
|
11
|
-
isError?: boolean;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export async function handleToolCall(
|
|
15
|
-
name: string,
|
|
16
|
-
args: Record<string, unknown>
|
|
17
|
-
): Promise<ToolResult> {
|
|
18
|
-
try {
|
|
19
|
-
const memoryService = getDefaultMemoryService();
|
|
20
|
-
await memoryService.initialize();
|
|
21
|
-
|
|
22
|
-
switch (name) {
|
|
23
|
-
case 'mem-search':
|
|
24
|
-
return await handleMemSearch(args);
|
|
25
|
-
|
|
26
|
-
case 'mem-timeline':
|
|
27
|
-
return await handleMemTimeline(args);
|
|
28
|
-
|
|
29
|
-
case 'mem-details':
|
|
30
|
-
return await handleMemDetails(args);
|
|
31
|
-
|
|
32
|
-
case 'mem-stats':
|
|
33
|
-
return await handleMemStats();
|
|
34
|
-
|
|
35
|
-
default:
|
|
36
|
-
return {
|
|
37
|
-
content: [{ type: 'text', text: `Unknown tool: ${name}` }],
|
|
38
|
-
isError: true
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
} catch (error) {
|
|
42
|
-
return {
|
|
43
|
-
content: [{ type: 'text', text: `Error: ${(error as Error).message}` }],
|
|
44
|
-
isError: true
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async function handleMemSearch(args: Record<string, unknown>): Promise<ToolResult> {
|
|
50
|
-
const query = args.query as string;
|
|
51
|
-
const topK = Math.min((args.topK as number) || 5, 20);
|
|
52
|
-
|
|
53
|
-
const memoryService = getDefaultMemoryService();
|
|
54
|
-
const result = await memoryService.retrieveMemories(query, {
|
|
55
|
-
topK,
|
|
56
|
-
sessionId: args.sessionId as string
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
const lines: string[] = [
|
|
60
|
-
'## Memory Search Results',
|
|
61
|
-
'',
|
|
62
|
-
`Found ${result.memories.length} relevant memories:`,
|
|
63
|
-
''
|
|
64
|
-
];
|
|
65
|
-
|
|
66
|
-
for (let i = 0; i < result.memories.length; i++) {
|
|
67
|
-
const m = result.memories[i];
|
|
68
|
-
const citationId = generateCitationId(m.event.id);
|
|
69
|
-
const date = m.event.timestamp.toISOString().split('T')[0];
|
|
70
|
-
const preview = m.event.content.slice(0, 100) + (m.event.content.length > 100 ? '...' : '');
|
|
71
|
-
|
|
72
|
-
lines.push(`### ${i + 1}. [mem:${citationId}] (score: ${m.score.toFixed(2)})`);
|
|
73
|
-
lines.push(`**Type**: ${m.event.eventType} | **Date**: ${date}`);
|
|
74
|
-
lines.push(`> ${preview}`);
|
|
75
|
-
lines.push('');
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
lines.push('---');
|
|
79
|
-
lines.push('*Use `mem-details` with IDs for full content.*');
|
|
80
|
-
|
|
81
|
-
return {
|
|
82
|
-
content: [{ type: 'text', text: lines.join('\n') }]
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
async function handleMemTimeline(args: Record<string, unknown>): Promise<ToolResult> {
|
|
87
|
-
const ids = args.ids as string[];
|
|
88
|
-
const windowSize = (args.windowSize as number) || 3;
|
|
89
|
-
|
|
90
|
-
const memoryService = getDefaultMemoryService();
|
|
91
|
-
const recentEvents = await memoryService.getRecentEvents(10000);
|
|
92
|
-
|
|
93
|
-
const lines: string[] = [
|
|
94
|
-
'## Timeline Context',
|
|
95
|
-
''
|
|
96
|
-
];
|
|
97
|
-
|
|
98
|
-
for (const targetId of ids) {
|
|
99
|
-
// Find the target event
|
|
100
|
-
const targetEvent = recentEvents.find(e =>
|
|
101
|
-
e.id === targetId || generateCitationId(e.id) === targetId
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
if (!targetEvent) {
|
|
105
|
-
lines.push(`Event ${targetId} not found.`);
|
|
106
|
-
continue;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Get session events
|
|
110
|
-
const sessionEvents = recentEvents
|
|
111
|
-
.filter(e => e.sessionId === targetEvent.sessionId)
|
|
112
|
-
.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
113
|
-
|
|
114
|
-
const eventIndex = sessionEvents.findIndex(e => e.id === targetEvent.id);
|
|
115
|
-
const start = Math.max(0, eventIndex - windowSize);
|
|
116
|
-
const end = Math.min(sessionEvents.length, eventIndex + windowSize + 1);
|
|
117
|
-
|
|
118
|
-
lines.push(`### Session: ${targetEvent.sessionId.slice(0, 8)}`);
|
|
119
|
-
lines.push('');
|
|
120
|
-
|
|
121
|
-
for (let i = start; i < end; i++) {
|
|
122
|
-
const e = sessionEvents[i];
|
|
123
|
-
const isTarget = e.id === targetEvent.id;
|
|
124
|
-
const marker = isTarget ? '**→**' : ' ';
|
|
125
|
-
const time = e.timestamp.toLocaleTimeString();
|
|
126
|
-
const preview = e.content.slice(0, 60) + (e.content.length > 60 ? '...' : '');
|
|
127
|
-
const citationId = generateCitationId(e.id);
|
|
128
|
-
|
|
129
|
-
lines.push(`${marker} ${time} [${citationId}] ${e.eventType}: ${preview}`);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
lines.push('');
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return {
|
|
136
|
-
content: [{ type: 'text', text: lines.join('\n') }]
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
async function handleMemDetails(args: Record<string, unknown>): Promise<ToolResult> {
|
|
141
|
-
const ids = args.ids as string[];
|
|
142
|
-
|
|
143
|
-
const memoryService = getDefaultMemoryService();
|
|
144
|
-
const recentEvents = await memoryService.getRecentEvents(10000);
|
|
145
|
-
|
|
146
|
-
const lines: string[] = [];
|
|
147
|
-
|
|
148
|
-
for (const targetId of ids) {
|
|
149
|
-
const event = recentEvents.find(e =>
|
|
150
|
-
e.id === targetId || generateCitationId(e.id) === targetId
|
|
151
|
-
);
|
|
152
|
-
|
|
153
|
-
if (!event) {
|
|
154
|
-
lines.push(`## Event ${targetId} not found.`);
|
|
155
|
-
lines.push('');
|
|
156
|
-
continue;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const citationId = generateCitationId(event.id);
|
|
160
|
-
const date = event.timestamp.toISOString();
|
|
161
|
-
|
|
162
|
-
lines.push(`## Memory: [mem:${citationId}]`);
|
|
163
|
-
lines.push('');
|
|
164
|
-
lines.push(`**Session**: ${event.sessionId}`);
|
|
165
|
-
lines.push(`**Type**: ${event.eventType}`);
|
|
166
|
-
lines.push(`**Date**: ${date}`);
|
|
167
|
-
lines.push('');
|
|
168
|
-
lines.push('**Content**:');
|
|
169
|
-
lines.push('```');
|
|
170
|
-
lines.push(event.content);
|
|
171
|
-
lines.push('```');
|
|
172
|
-
lines.push('');
|
|
173
|
-
lines.push('---');
|
|
174
|
-
lines.push('');
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return {
|
|
178
|
-
content: [{ type: 'text', text: lines.join('\n') }]
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
async function handleMemStats(): Promise<ToolResult> {
|
|
183
|
-
const memoryService = getDefaultMemoryService();
|
|
184
|
-
const stats = await memoryService.getStats();
|
|
185
|
-
const recentEvents = await memoryService.getRecentEvents(10000);
|
|
186
|
-
|
|
187
|
-
const uniqueSessions = new Set(recentEvents.map(e => e.sessionId));
|
|
188
|
-
|
|
189
|
-
const lines: string[] = [
|
|
190
|
-
'## Memory Statistics',
|
|
191
|
-
'',
|
|
192
|
-
`- **Total Events**: ${stats.totalEvents}`,
|
|
193
|
-
`- **Total Vectors**: ${stats.vectorCount}`,
|
|
194
|
-
`- **Sessions**: ${uniqueSessions.size}`,
|
|
195
|
-
'',
|
|
196
|
-
'### Events by Type',
|
|
197
|
-
''
|
|
198
|
-
];
|
|
199
|
-
|
|
200
|
-
const eventsByType = recentEvents.reduce((acc, e) => {
|
|
201
|
-
acc[e.eventType] = (acc[e.eventType] || 0) + 1;
|
|
202
|
-
return acc;
|
|
203
|
-
}, {} as Record<string, number>);
|
|
204
|
-
|
|
205
|
-
for (const [type, count] of Object.entries(eventsByType)) {
|
|
206
|
-
lines.push(`- ${type}: ${count}`);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
return {
|
|
210
|
-
content: [{ type: 'text', text: lines.join('\n') }]
|
|
211
|
-
};
|
|
212
|
-
}
|
|
1
|
+
// Compatibility re-export for the MCP extension tool handlers.
|
|
2
|
+
export * from '../extensions/mcp/handlers.js';
|
package/src/mcp/index.ts
CHANGED
|
@@ -1,47 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
8
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
9
|
-
import {
|
|
10
|
-
ListToolsRequestSchema,
|
|
11
|
-
CallToolRequestSchema
|
|
12
|
-
} from '@modelcontextprotocol/sdk/types.js';
|
|
13
|
-
|
|
14
|
-
import { tools } from './tools.js';
|
|
15
|
-
import { handleToolCall } from './handlers.js';
|
|
16
|
-
|
|
17
|
-
const server = new Server(
|
|
18
|
-
{
|
|
19
|
-
name: 'claude-memory-layer-mcp',
|
|
20
|
-
version: '1.0.0'
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
capabilities: {
|
|
24
|
-
tools: {}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
// Tool listing handler
|
|
30
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
31
|
-
return { tools };
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
// Tool call handler
|
|
35
|
-
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
36
|
-
const { name, arguments: args } = request.params;
|
|
37
|
-
return handleToolCall(name, args || {});
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
// Start server
|
|
41
|
-
async function main() {
|
|
42
|
-
const transport = new StdioServerTransport();
|
|
43
|
-
await server.connect(transport);
|
|
44
|
-
console.error('claude-memory-layer MCP server started');
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
main().catch(console.error);
|
|
2
|
+
// Compatibility entrypoint. MCP is an optional extension, but existing package
|
|
3
|
+
// and local paths still resolve through src/mcp during the strangler migration.
|
|
4
|
+
import '../extensions/mcp/index.js';
|