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
|
@@ -1,275 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
interface SemanticDaemonRequest {
|
|
10
|
-
type?: 'retrieve';
|
|
11
|
-
sessionId?: string;
|
|
12
|
-
prompt?: string;
|
|
13
|
-
topK?: number;
|
|
14
|
-
minScore?: number;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
interface SemanticMemory {
|
|
18
|
-
type: string;
|
|
19
|
-
content: string;
|
|
20
|
-
id?: string;
|
|
21
|
-
score?: number;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
interface SemanticDaemonResponse {
|
|
25
|
-
ok: boolean;
|
|
26
|
-
memories?: SemanticMemory[];
|
|
27
|
-
error?: string;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const SOCKET_PATH = process.env.CLAUDE_MEMORY_SEMANTIC_SOCKET || path.join(
|
|
31
|
-
os.homedir(),
|
|
32
|
-
'.claude-code',
|
|
33
|
-
'memory',
|
|
34
|
-
'semantic-daemon.sock'
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
const IDLE_TIMEOUT_MS = parseInt(process.env.CLAUDE_MEMORY_SEMANTIC_DAEMON_IDLE_MS || '600000');
|
|
38
|
-
const serviceCache = new Map<string, MemoryService>();
|
|
39
|
-
|
|
40
|
-
let server: net.Server | null = null;
|
|
41
|
-
let idleTimer: NodeJS.Timeout | null = null;
|
|
42
|
-
let shuttingDown = false;
|
|
43
|
-
|
|
44
|
-
function scheduleIdleShutdown(): void {
|
|
45
|
-
if (idleTimer) {
|
|
46
|
-
clearTimeout(idleTimer);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
idleTimer = setTimeout(() => {
|
|
50
|
-
shutdown(0).catch(() => {
|
|
51
|
-
process.exit(0);
|
|
52
|
-
});
|
|
53
|
-
}, IDLE_TIMEOUT_MS);
|
|
54
|
-
idleTimer.unref();
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function parseRequest(raw: string): SemanticDaemonRequest {
|
|
58
|
-
try {
|
|
59
|
-
return JSON.parse(raw) as SemanticDaemonRequest;
|
|
60
|
-
} catch {
|
|
61
|
-
return {};
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function isValidRequest(input: SemanticDaemonRequest): input is Required<SemanticDaemonRequest> {
|
|
66
|
-
return input.type === 'retrieve'
|
|
67
|
-
&& typeof input.sessionId === 'string'
|
|
68
|
-
&& input.sessionId.length > 0
|
|
69
|
-
&& typeof input.prompt === 'string'
|
|
70
|
-
&& input.prompt.length > 0
|
|
71
|
-
&& Number.isFinite(input.topK)
|
|
72
|
-
&& Number.isFinite(input.minScore);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function makeErrorResponse(error: unknown): SemanticDaemonResponse {
|
|
76
|
-
return { ok: false, error: error instanceof Error ? error.message : 'unknown daemon error' };
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function isVectorSessionFilterError(error: unknown): boolean {
|
|
80
|
-
const message = error instanceof Error ? error.message.toLowerCase() : '';
|
|
81
|
-
return message.includes('no field named sessionid');
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function getServiceForSession(sessionId: string): MemoryService {
|
|
85
|
-
const projectInfo = getSessionProject(sessionId);
|
|
86
|
-
const key = projectInfo?.projectHash || '__global__';
|
|
87
|
-
|
|
88
|
-
if (serviceCache.has(key)) {
|
|
89
|
-
return serviceCache.get(key)!;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const service = new MemoryService({
|
|
93
|
-
storagePath: projectInfo
|
|
94
|
-
? getProjectStoragePath(projectInfo.projectPath)
|
|
95
|
-
: path.join(os.homedir(), '.claude-code', 'memory'),
|
|
96
|
-
projectHash: projectInfo?.projectHash,
|
|
97
|
-
projectPath: projectInfo?.projectPath,
|
|
98
|
-
readOnly: false,
|
|
99
|
-
embeddingOnly: true,
|
|
100
|
-
analyticsEnabled: false,
|
|
101
|
-
sharedStoreConfig: { enabled: false }
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
serviceCache.set(key, service);
|
|
105
|
-
return service;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
async function handleRequest(raw: string): Promise<SemanticDaemonResponse> {
|
|
109
|
-
const input = parseRequest(raw);
|
|
110
|
-
if (!isValidRequest(input)) {
|
|
111
|
-
return { ok: false, error: 'invalid request' };
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
try {
|
|
115
|
-
const service = getServiceForSession(input.sessionId);
|
|
116
|
-
let result;
|
|
117
|
-
try {
|
|
118
|
-
result = await service.retrieveMemories(input.prompt, {
|
|
119
|
-
topK: input.topK,
|
|
120
|
-
minScore: input.minScore,
|
|
121
|
-
sessionId: input.sessionId,
|
|
122
|
-
intentRewrite: true,
|
|
123
|
-
adaptiveRerank: true,
|
|
124
|
-
projectScopeMode: 'strict'
|
|
125
|
-
});
|
|
126
|
-
} catch (error) {
|
|
127
|
-
if (!isVectorSessionFilterError(error)) {
|
|
128
|
-
throw error;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// LanceDB field-case mismatch can fail sessionId filtering.
|
|
132
|
-
// Retry without session filter and keep project strict scoping.
|
|
133
|
-
result = await service.retrieveMemories(input.prompt, {
|
|
134
|
-
topK: input.topK,
|
|
135
|
-
minScore: input.minScore,
|
|
136
|
-
intentRewrite: true,
|
|
137
|
-
adaptiveRerank: true,
|
|
138
|
-
projectScopeMode: 'strict'
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const memories = result.memories.map((m) => ({
|
|
143
|
-
type: m.event.eventType,
|
|
144
|
-
content: m.event.content,
|
|
145
|
-
id: m.event.id,
|
|
146
|
-
score: m.score
|
|
147
|
-
}));
|
|
148
|
-
|
|
149
|
-
return { ok: true, memories };
|
|
150
|
-
} catch (error) {
|
|
151
|
-
return makeErrorResponse(error);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function createServer(): net.Server {
|
|
156
|
-
return net.createServer({ allowHalfOpen: true }, (socket) => {
|
|
157
|
-
scheduleIdleShutdown();
|
|
158
|
-
socket.setEncoding('utf8');
|
|
159
|
-
|
|
160
|
-
let requestRaw = '';
|
|
161
|
-
|
|
162
|
-
socket.on('data', (chunk) => {
|
|
163
|
-
requestRaw += chunk;
|
|
164
|
-
if (requestRaw.length > 1024 * 1024) {
|
|
165
|
-
socket.end(JSON.stringify({ ok: false, error: 'request too large' }));
|
|
166
|
-
}
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
socket.on('end', async () => {
|
|
170
|
-
const response = await handleRequest(requestRaw);
|
|
171
|
-
socket.end(JSON.stringify(response));
|
|
172
|
-
scheduleIdleShutdown();
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
socket.on('error', () => {
|
|
176
|
-
// Ignore per-socket errors to keep daemon process alive.
|
|
177
|
-
});
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
async function socketInUse(p: string): Promise<boolean> {
|
|
182
|
-
if (!fs.existsSync(p)) return false;
|
|
183
|
-
return new Promise((resolve) => {
|
|
184
|
-
let settled = false;
|
|
185
|
-
const client = net.createConnection(p);
|
|
186
|
-
const done = (alive: boolean) => {
|
|
187
|
-
if (settled) return;
|
|
188
|
-
settled = true;
|
|
189
|
-
client.destroy();
|
|
190
|
-
resolve(alive);
|
|
191
|
-
};
|
|
192
|
-
client.on('connect', () => done(true));
|
|
193
|
-
client.on('error', () => done(false));
|
|
194
|
-
setTimeout(() => done(false), 120).unref();
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
async function listenServer(): Promise<void> {
|
|
199
|
-
const socketDir = path.dirname(SOCKET_PATH);
|
|
200
|
-
if (!fs.existsSync(socketDir)) {
|
|
201
|
-
fs.mkdirSync(socketDir, { recursive: true });
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
if (await socketInUse(SOCKET_PATH)) {
|
|
205
|
-
process.exit(0);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
if (fs.existsSync(SOCKET_PATH)) {
|
|
209
|
-
try {
|
|
210
|
-
fs.unlinkSync(SOCKET_PATH);
|
|
211
|
-
} catch {
|
|
212
|
-
// Ignore stale socket unlink failures.
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
server = createServer();
|
|
217
|
-
|
|
218
|
-
await new Promise<void>((resolve, reject) => {
|
|
219
|
-
if (!server) {
|
|
220
|
-
reject(new Error('daemon server not initialized'));
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
server.once('error', reject);
|
|
225
|
-
server.listen(SOCKET_PATH, () => {
|
|
226
|
-
server?.off('error', reject);
|
|
227
|
-
resolve();
|
|
228
|
-
});
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
async function shutdown(code: number): Promise<void> {
|
|
233
|
-
if (shuttingDown) return;
|
|
234
|
-
shuttingDown = true;
|
|
235
|
-
|
|
236
|
-
if (idleTimer) {
|
|
237
|
-
clearTimeout(idleTimer);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
const closePromises: Promise<void>[] = [];
|
|
241
|
-
for (const service of serviceCache.values()) {
|
|
242
|
-
closePromises.push(service.shutdown().catch(() => undefined));
|
|
243
|
-
}
|
|
244
|
-
await Promise.all(closePromises);
|
|
245
|
-
serviceCache.clear();
|
|
246
|
-
|
|
247
|
-
if (server) {
|
|
248
|
-
await new Promise<void>((resolve) => {
|
|
249
|
-
server?.close(() => resolve());
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
if (fs.existsSync(SOCKET_PATH)) {
|
|
254
|
-
try {
|
|
255
|
-
fs.unlinkSync(SOCKET_PATH);
|
|
256
|
-
} catch {
|
|
257
|
-
// Ignore socket cleanup failure.
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
process.exit(code);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
async function main(): Promise<void> {
|
|
265
|
-
await listenServer();
|
|
266
|
-
scheduleIdleShutdown();
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
process.on('SIGINT', () => { shutdown(0).catch(() => process.exit(0)); });
|
|
270
|
-
process.on('SIGTERM', () => { shutdown(0).catch(() => process.exit(0)); });
|
|
271
|
-
process.on('uncaughtException', () => { shutdown(1).catch(() => process.exit(1)); });
|
|
272
|
-
process.on('unhandledRejection', () => { shutdown(1).catch(() => process.exit(1)); });
|
|
2
|
+
/**
|
|
3
|
+
* Compatibility entrypoint for the Claude semantic daemon hook runtime.
|
|
4
|
+
*
|
|
5
|
+
* Implementation lives in the Claude adapter layer so core stays platform-agnostic.
|
|
6
|
+
*/
|
|
7
|
+
import { main } from '../adapters/claude/hooks/semantic-daemon.js';
|
|
273
8
|
|
|
274
9
|
main().catch(() => {
|
|
275
10
|
process.exit(1);
|
package/src/hooks/session-end.ts
CHANGED
|
@@ -1,84 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Compatibility entrypoint for the Claude session-end hook.
|
|
4
|
+
*
|
|
5
|
+
* Implementation lives in the Claude adapter layer so core stays platform-agnostic.
|
|
5
6
|
*/
|
|
6
|
-
|
|
7
|
-
import { getLightweightMemoryService } from '../services/memory-service.js';
|
|
8
|
-
import type { SessionEndInput } from '../core/types.js';
|
|
9
|
-
|
|
10
|
-
async function main(): Promise<void> {
|
|
11
|
-
// Read input from stdin
|
|
12
|
-
const inputData = await readStdin();
|
|
13
|
-
const input: SessionEndInput = JSON.parse(inputData);
|
|
14
|
-
|
|
15
|
-
// Use lightweight service (SQLite only, no embedder/vector - FAST!)
|
|
16
|
-
const memoryService = getLightweightMemoryService(input.session_id);
|
|
17
|
-
|
|
18
|
-
try {
|
|
19
|
-
// Get session history
|
|
20
|
-
const sessionEvents = await memoryService.getSessionHistory(input.session_id);
|
|
21
|
-
|
|
22
|
-
if (sessionEvents.length > 0) {
|
|
23
|
-
// Generate a simple session summary
|
|
24
|
-
const summary = generateSummary(sessionEvents);
|
|
25
|
-
|
|
26
|
-
// Store session summary
|
|
27
|
-
await memoryService.storeSessionSummary(input.session_id, summary);
|
|
28
|
-
|
|
29
|
-
// End session with summary
|
|
30
|
-
await memoryService.endSession(input.session_id, summary);
|
|
31
|
-
|
|
32
|
-
// Evaluate helpfulness of memory retrievals in this session
|
|
33
|
-
try {
|
|
34
|
-
await memoryService.evaluateSessionHelpfulness(input.session_id);
|
|
35
|
-
} catch { /* non-critical */ }
|
|
36
|
-
|
|
37
|
-
// Process any pending embeddings
|
|
38
|
-
await memoryService.processPendingEmbeddings();
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
console.log(JSON.stringify({}));
|
|
42
|
-
} catch (error) {
|
|
43
|
-
console.error('Memory hook error:', error);
|
|
44
|
-
console.log(JSON.stringify({}));
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Generate a simple session summary from events
|
|
50
|
-
*/
|
|
51
|
-
function generateSummary(events: Array<{ eventType: string; content: string }>): string {
|
|
52
|
-
const userPrompts = events.filter(e => e.eventType === 'user_prompt');
|
|
53
|
-
const responses = events.filter(e => e.eventType === 'agent_response');
|
|
54
|
-
|
|
55
|
-
const parts: string[] = [];
|
|
56
|
-
|
|
57
|
-
parts.push(`Session with ${userPrompts.length} user prompts and ${responses.length} responses.`);
|
|
58
|
-
|
|
59
|
-
// Add first few user prompts as topics
|
|
60
|
-
if (userPrompts.length > 0) {
|
|
61
|
-
parts.push('Topics discussed:');
|
|
62
|
-
for (const prompt of userPrompts.slice(0, 3)) {
|
|
63
|
-
const topic = prompt.content.slice(0, 100).replace(/\n/g, ' ');
|
|
64
|
-
parts.push(`- ${topic}${prompt.content.length > 100 ? '...' : ''}`);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return parts.join('\n');
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function readStdin(): Promise<string> {
|
|
72
|
-
return new Promise((resolve) => {
|
|
73
|
-
let data = '';
|
|
74
|
-
process.stdin.setEncoding('utf8');
|
|
75
|
-
process.stdin.on('data', (chunk) => {
|
|
76
|
-
data += chunk;
|
|
77
|
-
});
|
|
78
|
-
process.stdin.on('end', () => {
|
|
79
|
-
resolve(data);
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
}
|
|
7
|
+
import { main } from '../adapters/claude/hooks/session-end.js';
|
|
83
8
|
|
|
84
9
|
main().catch(console.error);
|
|
@@ -1,78 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Compatibility entrypoint for the Claude session-start hook.
|
|
4
|
+
*
|
|
5
|
+
* Implementation lives in the Claude adapter layer so core stays platform-agnostic.
|
|
5
6
|
*/
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
getLightweightMemoryService,
|
|
9
|
-
registerSession
|
|
10
|
-
} from '../services/memory-service.js';
|
|
11
|
-
import { ensureDaemonRunning } from './semantic-daemon-client.js';
|
|
12
|
-
import type { SessionStartInput, SessionStartOutput } from '../core/types.js';
|
|
13
|
-
|
|
14
|
-
async function main(): Promise<void> {
|
|
15
|
-
// Read input from stdin
|
|
16
|
-
const inputData = await readStdin();
|
|
17
|
-
const input: SessionStartInput = JSON.parse(inputData);
|
|
18
|
-
|
|
19
|
-
// Register session with project path for other hooks to find
|
|
20
|
-
registerSession(input.session_id, input.cwd);
|
|
21
|
-
|
|
22
|
-
// Start semantic daemon in the background (non-blocking) so VectorWorker
|
|
23
|
-
// can process any pending embedding_outbox items immediately.
|
|
24
|
-
ensureDaemonRunning().catch(() => {
|
|
25
|
-
// Ignore - daemon will start on first prompt if needed
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
// Use lightweight service to avoid starting background workers in hook process
|
|
29
|
-
const memoryService = getLightweightMemoryService(input.session_id);
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
// Start session in memory service
|
|
33
|
-
await memoryService.startSession(input.session_id, input.cwd);
|
|
34
|
-
|
|
35
|
-
// Backfill session summaries for recent sessions that ended without Stop hook
|
|
36
|
-
// (crash, force-close, etc.). Run in background - non-blocking.
|
|
37
|
-
memoryService.backfillMissingSummaries(input.session_id, 5).catch(() => {});
|
|
38
|
-
|
|
39
|
-
// Get recent context for this project (now automatically scoped)
|
|
40
|
-
const recentEvents = await memoryService.getRecentEvents(10);
|
|
41
|
-
|
|
42
|
-
let context = '';
|
|
43
|
-
if (recentEvents.length > 0) {
|
|
44
|
-
context = `## Previous Session Context\n\nYou have worked on this project before. Here are some relevant memories:\n\n`;
|
|
45
|
-
for (const event of recentEvents.slice(0, 3)) {
|
|
46
|
-
const date = event.timestamp.toISOString().split('T')[0];
|
|
47
|
-
context += `- **${date}**: ${event.content.slice(0, 150)}...\n`;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const output: SessionStartOutput = { context };
|
|
52
|
-
console.log(JSON.stringify(output));
|
|
53
|
-
} catch (error) {
|
|
54
|
-
console.error('Memory hook error:', error);
|
|
55
|
-
console.log(JSON.stringify({ context: '' }));
|
|
56
|
-
} finally {
|
|
57
|
-
try {
|
|
58
|
-
await memoryService.close();
|
|
59
|
-
} catch {
|
|
60
|
-
// Best-effort cleanup
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function readStdin(): Promise<string> {
|
|
66
|
-
return new Promise((resolve) => {
|
|
67
|
-
let data = '';
|
|
68
|
-
process.stdin.setEncoding('utf8');
|
|
69
|
-
process.stdin.on('data', (chunk) => {
|
|
70
|
-
data += chunk;
|
|
71
|
-
});
|
|
72
|
-
process.stdin.on('end', () => {
|
|
73
|
-
resolve(data);
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
}
|
|
7
|
+
import { main } from '../adapters/claude/hooks/session-start.js';
|
|
77
8
|
|
|
78
9
|
main().catch(console.error);
|
package/src/hooks/stop.ts
CHANGED
|
@@ -1,179 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* Called when agent stops - reads transcript and stores assistant responses
|
|
3
|
+
* Compatibility entrypoint for the Claude stop hook.
|
|
5
4
|
*
|
|
6
|
-
*
|
|
7
|
-
* {
|
|
8
|
-
* session_id, transcript_path, cwd, permission_mode,
|
|
9
|
-
* hook_event_name: "Stop", stop_hook_active
|
|
10
|
-
* }
|
|
11
|
-
*
|
|
12
|
-
* NOTE: Claude Code does NOT send messages in the Stop hook.
|
|
13
|
-
* We read them from the transcript JSONL file instead.
|
|
5
|
+
* Implementation lives in the Claude adapter layer so core stays platform-agnostic.
|
|
14
6
|
*/
|
|
15
|
-
|
|
16
|
-
import * as fs from 'fs';
|
|
17
|
-
import * as readline from 'readline';
|
|
18
|
-
import { getLightweightMemoryService } from '../services/memory-service.js';
|
|
19
|
-
import { applyPrivacyFilter } from '../core/privacy/index.js';
|
|
20
|
-
import { readTurnState, clearTurnState, writeLastAssistantSnippet } from '../core/turn-state.js';
|
|
21
|
-
import type { StopInput, Config } from '../core/types.js';
|
|
22
|
-
|
|
23
|
-
// Default privacy config
|
|
24
|
-
const DEFAULT_PRIVACY_CONFIG: Config['privacy'] = {
|
|
25
|
-
excludePatterns: ['password', 'secret', 'api_key', 'token', 'bearer'],
|
|
26
|
-
anonymize: false,
|
|
27
|
-
privateTags: {
|
|
28
|
-
enabled: true,
|
|
29
|
-
marker: '[PRIVATE]',
|
|
30
|
-
preserveLineCount: false,
|
|
31
|
-
supportedFormats: ['xml']
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Extract assistant text messages from transcript JSONL.
|
|
37
|
-
* Only reads the last N lines to avoid processing entire transcript.
|
|
38
|
-
*/
|
|
39
|
-
async function extractAssistantMessages(transcriptPath: string): Promise<string[]> {
|
|
40
|
-
if (!fs.existsSync(transcriptPath)) return [];
|
|
41
|
-
|
|
42
|
-
const messages: string[] = [];
|
|
43
|
-
|
|
44
|
-
// Read last portion of file (last ~200KB should cover recent messages)
|
|
45
|
-
const stats = fs.statSync(transcriptPath);
|
|
46
|
-
const readStart = Math.max(0, stats.size - 200 * 1024);
|
|
47
|
-
|
|
48
|
-
const stream = fs.createReadStream(transcriptPath, {
|
|
49
|
-
start: readStart,
|
|
50
|
-
encoding: 'utf8'
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
54
|
-
|
|
55
|
-
for await (const line of rl) {
|
|
56
|
-
try {
|
|
57
|
-
const entry = JSON.parse(line);
|
|
58
|
-
|
|
59
|
-
// Only process assistant messages with text content
|
|
60
|
-
if (entry.type !== 'assistant') continue;
|
|
61
|
-
|
|
62
|
-
const content = entry.message?.content;
|
|
63
|
-
if (!Array.isArray(content)) continue;
|
|
64
|
-
|
|
65
|
-
// Extract text blocks from content array
|
|
66
|
-
const textParts = content
|
|
67
|
-
.filter((c: { type: string }) => c.type === 'text')
|
|
68
|
-
.map((c: { text: string }) => c.text)
|
|
69
|
-
.filter(Boolean);
|
|
70
|
-
|
|
71
|
-
if (textParts.length > 0) {
|
|
72
|
-
messages.push(textParts.join('\n'));
|
|
73
|
-
}
|
|
74
|
-
} catch {
|
|
75
|
-
// Skip malformed lines (e.g., partial first line from readStart offset)
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return messages;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async function main(): Promise<void> {
|
|
83
|
-
// Read input from stdin
|
|
84
|
-
const inputData = await readStdin();
|
|
85
|
-
const input: StopInput = JSON.parse(inputData);
|
|
86
|
-
|
|
87
|
-
// Use lightweight service (SQLite only, no embedder/vector - FAST!)
|
|
88
|
-
const memoryService = getLightweightMemoryService(input.session_id);
|
|
89
|
-
|
|
90
|
-
try {
|
|
91
|
-
// Read current turn_id from state file
|
|
92
|
-
const turnId = readTurnState(input.session_id);
|
|
93
|
-
|
|
94
|
-
// Read assistant messages from transcript
|
|
95
|
-
const assistantMessages = await extractAssistantMessages(input.transcript_path);
|
|
96
|
-
|
|
97
|
-
const MIN_AGENT_RESPONSE_LEN = parseInt(
|
|
98
|
-
process.env.CLAUDE_MEMORY_AGENT_RESPONSE_MIN_LEN || '150'
|
|
99
|
-
);
|
|
100
|
-
const lastIdx = assistantMessages.length - 1;
|
|
101
|
-
|
|
102
|
-
// Store each assistant response
|
|
103
|
-
for (let i = 0; i < assistantMessages.length; i++) {
|
|
104
|
-
const text = assistantMessages[i];
|
|
105
|
-
const isLast = i === lastIdx;
|
|
106
|
-
|
|
107
|
-
// Apply privacy filter
|
|
108
|
-
const filterResult = applyPrivacyFilter(text, DEFAULT_PRIVACY_CONFIG);
|
|
109
|
-
let content = filterResult.content;
|
|
110
|
-
|
|
111
|
-
// Truncate very long responses
|
|
112
|
-
if (content.length > 5000) {
|
|
113
|
-
content = content.slice(0, 5000) + '...[truncated]';
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Skip very short responses (likely just tool calls or transition messages)
|
|
117
|
-
// Always store the last message (may be the final answer)
|
|
118
|
-
if (!isLast && content.trim().length < MIN_AGENT_RESPONSE_LEN) continue;
|
|
119
|
-
|
|
120
|
-
await memoryService.storeAgentResponse(
|
|
121
|
-
input.session_id,
|
|
122
|
-
content,
|
|
123
|
-
{
|
|
124
|
-
privacy: filterResult.metadata,
|
|
125
|
-
...(turnId ? { turnId } : {})
|
|
126
|
-
}
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Save last assistant response snippet for next-turn retrieval context enrichment
|
|
131
|
-
if (assistantMessages.length > 0) {
|
|
132
|
-
const lastMessage = assistantMessages[assistantMessages.length - 1];
|
|
133
|
-
writeLastAssistantSnippet(input.session_id, lastMessage);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Clean up turn state file after processing
|
|
137
|
-
clearTurnState(input.session_id);
|
|
138
|
-
|
|
139
|
-
// Evaluate helpfulness of retrieved memories for this session
|
|
140
|
-
try {
|
|
141
|
-
await memoryService.evaluateSessionHelpfulness(input.session_id);
|
|
142
|
-
} catch {
|
|
143
|
-
// non-critical
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Generate session summary from recent events (rule-based, no LLM needed)
|
|
147
|
-
try {
|
|
148
|
-
await memoryService.generateSessionSummary(input.session_id);
|
|
149
|
-
} catch {
|
|
150
|
-
// non-critical
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Embeddings enqueued in SQLite - will be processed by vector worker when server runs
|
|
154
|
-
await memoryService.processPendingEmbeddings();
|
|
155
|
-
|
|
156
|
-
// Output empty (stop hook doesn't return context)
|
|
157
|
-
console.log(JSON.stringify({}));
|
|
158
|
-
} catch (error) {
|
|
159
|
-
if (process.env.CLAUDE_MEMORY_DEBUG) {
|
|
160
|
-
console.error('Stop hook error:', error);
|
|
161
|
-
}
|
|
162
|
-
console.log(JSON.stringify({}));
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function readStdin(): Promise<string> {
|
|
167
|
-
return new Promise((resolve) => {
|
|
168
|
-
let data = '';
|
|
169
|
-
process.stdin.setEncoding('utf8');
|
|
170
|
-
process.stdin.on('data', (chunk) => {
|
|
171
|
-
data += chunk;
|
|
172
|
-
});
|
|
173
|
-
process.stdin.on('end', () => {
|
|
174
|
-
resolve(data);
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
}
|
|
7
|
+
import { main } from '../adapters/claude/hooks/stop.js';
|
|
178
8
|
|
|
179
9
|
main().catch(console.error);
|