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,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local Embedding Generator using @huggingface/transformers
|
|
3
|
+
* AXIOMMIND Principle 7: Standard JSON format for vectors
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface EmbeddingResult {
|
|
7
|
+
vector: number[];
|
|
8
|
+
model: string;
|
|
9
|
+
dimensions: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type FeatureExtractionPipelineFactory = (
|
|
13
|
+
task: 'feature-extraction',
|
|
14
|
+
model: string
|
|
15
|
+
) => Promise<NonNullable<Embedder['pipeline']>>;
|
|
16
|
+
|
|
17
|
+
export class Embedder {
|
|
18
|
+
private pipeline: ((input: string, options?: Record<string, unknown>) => Promise<{ data: Float32Array }>) | null = null;
|
|
19
|
+
private readonly modelName: string;
|
|
20
|
+
private activeModelName: string;
|
|
21
|
+
private initialized = false;
|
|
22
|
+
|
|
23
|
+
constructor(modelName: string = 'jinaai/jina-embeddings-v5-text-nano-text-matching') {
|
|
24
|
+
this.modelName = modelName;
|
|
25
|
+
this.activeModelName = modelName;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Initialize the embedding pipeline
|
|
30
|
+
*/
|
|
31
|
+
async initialize(): Promise<void> {
|
|
32
|
+
if (this.initialized) return;
|
|
33
|
+
|
|
34
|
+
const pipeline = await withSuppressedKnownTransformersWarnings(() => loadTransformersPipeline());
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
this.pipeline = await withSuppressedKnownTransformersWarnings(() => pipeline('feature-extraction', this.modelName));
|
|
38
|
+
this.activeModelName = this.modelName;
|
|
39
|
+
this.initialized = true;
|
|
40
|
+
return;
|
|
41
|
+
} catch (primaryError) {
|
|
42
|
+
const fallbackModel = process.env.CLAUDE_MEMORY_EMBEDDING_FALLBACK_MODEL || 'onnx-community/embeddinggemma-300m-ONNX';
|
|
43
|
+
if (fallbackModel === this.modelName) {
|
|
44
|
+
throw primaryError;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
console.warn(`[Embedder] Primary model failed (${this.modelName}). Falling back to ${fallbackModel}`);
|
|
48
|
+
this.pipeline = await withSuppressedKnownTransformersWarnings(() => pipeline('feature-extraction', fallbackModel));
|
|
49
|
+
this.activeModelName = fallbackModel;
|
|
50
|
+
this.initialized = true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ~4 chars per token; 512 tokens * 4 = 2048, use 2000 to be safe
|
|
55
|
+
private static readonly MAX_CHARS = 2000;
|
|
56
|
+
|
|
57
|
+
private truncate(text: string): string {
|
|
58
|
+
return text.length > Embedder.MAX_CHARS ? text.slice(0, Embedder.MAX_CHARS) : text;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Generate embedding for a single text
|
|
63
|
+
*/
|
|
64
|
+
async embed(text: string): Promise<EmbeddingResult> {
|
|
65
|
+
await this.initialize();
|
|
66
|
+
|
|
67
|
+
if (!this.pipeline) {
|
|
68
|
+
throw new Error('Embedding pipeline not initialized');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const output = await this.pipeline(this.truncate(text), {
|
|
72
|
+
pooling: 'mean',
|
|
73
|
+
normalize: true,
|
|
74
|
+
truncation: true,
|
|
75
|
+
max_length: 512
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const vector = Array.from(output.data);
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
vector,
|
|
82
|
+
model: this.activeModelName,
|
|
83
|
+
dimensions: vector.length
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Generate embeddings for multiple texts in batch
|
|
89
|
+
*/
|
|
90
|
+
async embedBatch(texts: string[]): Promise<EmbeddingResult[]> {
|
|
91
|
+
await this.initialize();
|
|
92
|
+
|
|
93
|
+
if (!this.pipeline) {
|
|
94
|
+
throw new Error('Embedding pipeline not initialized');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const results: EmbeddingResult[] = [];
|
|
98
|
+
|
|
99
|
+
// Process in batches of 32 for memory efficiency
|
|
100
|
+
const batchSize = 32;
|
|
101
|
+
for (let i = 0; i < texts.length; i += batchSize) {
|
|
102
|
+
const batch = texts.slice(i, i + batchSize);
|
|
103
|
+
|
|
104
|
+
for (const text of batch) {
|
|
105
|
+
const output = await this.pipeline(this.truncate(text), {
|
|
106
|
+
pooling: 'mean',
|
|
107
|
+
normalize: true,
|
|
108
|
+
truncation: true,
|
|
109
|
+
max_length: 512
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const vector = Array.from(output.data);
|
|
113
|
+
|
|
114
|
+
results.push({
|
|
115
|
+
vector,
|
|
116
|
+
model: this.activeModelName,
|
|
117
|
+
dimensions: vector.length
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return results;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get embedding dimensions for the current model
|
|
127
|
+
*/
|
|
128
|
+
async getDimensions(): Promise<number> {
|
|
129
|
+
const result = await this.embed('test');
|
|
130
|
+
return result.dimensions;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Check if embedder is ready
|
|
135
|
+
*/
|
|
136
|
+
isReady(): boolean {
|
|
137
|
+
return this.initialized && this.pipeline !== null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Get model name
|
|
142
|
+
*/
|
|
143
|
+
getModelName(): string {
|
|
144
|
+
return this.activeModelName;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Singleton instance for reuse
|
|
149
|
+
let defaultEmbedder: Embedder | null = null;
|
|
150
|
+
|
|
151
|
+
export function getDefaultEmbedder(): Embedder {
|
|
152
|
+
const envModel = process.env.CLAUDE_MEMORY_EMBEDDING_MODEL;
|
|
153
|
+
if (!defaultEmbedder) {
|
|
154
|
+
defaultEmbedder = new Embedder(envModel || undefined);
|
|
155
|
+
}
|
|
156
|
+
return defaultEmbedder;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
let transformersWarningSuppressionDepth = 0;
|
|
160
|
+
let originalConsoleWarn: typeof console.warn | null = null;
|
|
161
|
+
|
|
162
|
+
export async function withSuppressedKnownTransformersWarnings<T>(fn: () => Promise<T>): Promise<T> {
|
|
163
|
+
if (transformersWarningSuppressionDepth === 0) {
|
|
164
|
+
originalConsoleWarn = console.warn;
|
|
165
|
+
console.warn = (...args: unknown[]) => {
|
|
166
|
+
const message = args.map(String).join(' ');
|
|
167
|
+
if (isKnownBenignTransformersWarning(message)) return;
|
|
168
|
+
(originalConsoleWarn ?? console.warn)(...args);
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
transformersWarningSuppressionDepth += 1;
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
return await fn();
|
|
175
|
+
} finally {
|
|
176
|
+
transformersWarningSuppressionDepth -= 1;
|
|
177
|
+
if (transformersWarningSuppressionDepth === 0 && originalConsoleWarn) {
|
|
178
|
+
console.warn = originalConsoleWarn;
|
|
179
|
+
originalConsoleWarn = null;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function isKnownBenignTransformersWarning(message: string): boolean {
|
|
185
|
+
return message.includes('Unknown model class "eurobert"') ||
|
|
186
|
+
message.includes('dtype not specified for "model"');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async function loadTransformersPipeline(): Promise<FeatureExtractionPipelineFactory> {
|
|
190
|
+
// Keep @huggingface/transformers lazy so importing MemoryService or pure
|
|
191
|
+
// adapter helpers does not eagerly dlopen onnxruntime native bindings.
|
|
192
|
+
const dynamicImport = new Function('specifier', 'return import(specifier)') as (
|
|
193
|
+
specifier: string
|
|
194
|
+
) => Promise<{ pipeline: unknown }>;
|
|
195
|
+
const transformers = await dynamicImport('@huggingface/transformers');
|
|
196
|
+
return transformers.pipeline as FeatureExtractionPipelineFactory;
|
|
197
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './embedder.js';
|
|
@@ -1,242 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* Called after each tool execution - stores tool observations
|
|
3
|
+
* Compatibility entrypoint for the Claude post-tool-use hook.
|
|
5
4
|
*
|
|
6
|
-
*
|
|
7
|
-
* {
|
|
8
|
-
* session_id, tool_name, tool_input, tool_use_id,
|
|
9
|
-
* tool_response: { stdout?, stderr?, content?, interrupted?, isImage? },
|
|
10
|
-
* cwd, transcript_path, permission_mode, hook_event_name
|
|
11
|
-
* }
|
|
5
|
+
* Implementation lives in the Claude adapter layer so core stays platform-agnostic.
|
|
12
6
|
*/
|
|
13
|
-
|
|
14
|
-
import { getLightweightMemoryService } from '../services/memory-service.js';
|
|
15
|
-
import { applyPrivacyFilter, maskSensitiveInput, truncateOutput } from '../core/privacy/index.js';
|
|
16
|
-
import { extractMetadata } from '../core/metadata-extractor.js';
|
|
17
|
-
import { readTurnState } from '../core/turn-state.js';
|
|
18
|
-
import type { PostToolUseInput, ToolObservationPayload, Config } from '../core/types.js';
|
|
19
|
-
|
|
20
|
-
// Default config
|
|
21
|
-
const DEFAULT_CONFIG: Config['toolObservation'] = {
|
|
22
|
-
enabled: true,
|
|
23
|
-
excludedTools: [
|
|
24
|
-
// Trivial meta tools
|
|
25
|
-
'TodoWrite', 'TodoRead',
|
|
26
|
-
// Reproducible query tools (no storage value)
|
|
27
|
-
'Read', 'Grep', 'Glob',
|
|
28
|
-
'ToolSearch', 'WebFetch', 'WebSearch', 'NotebookRead',
|
|
29
|
-
// Low-value system tools
|
|
30
|
-
'Skill', 'EnterPlanMode',
|
|
31
|
-
],
|
|
32
|
-
minOutputLength: parseInt(process.env.CLAUDE_MEMORY_TOOL_MIN_OUTPUT_LEN || '100'),
|
|
33
|
-
maxOutputLength: 10000,
|
|
34
|
-
maxOutputLines: 100,
|
|
35
|
-
storeOnlyOnSuccess: false
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
// Tools that are always stored regardless of output length
|
|
39
|
-
const ALWAYS_STORE_TOOLS = new Set([
|
|
40
|
-
'Write', 'Edit', 'MultiEdit', 'Agent', 'Task', 'ExitPlanMode'
|
|
41
|
-
]);
|
|
42
|
-
|
|
43
|
-
// Keywords that indicate a Bash output is worth storing
|
|
44
|
-
const IMPORTANT_BASH_KEYWORDS = [
|
|
45
|
-
'error', 'failed', 'exception', 'traceback', 'panic',
|
|
46
|
-
'warning', 'deprecated',
|
|
47
|
-
'test passed', 'test failed', 'tests passed', 'tests failed',
|
|
48
|
-
'coverage', 'assert',
|
|
49
|
-
'published', 'deployed', 'built successfully', 'build complete',
|
|
50
|
-
'successfully installed', 'successfully created',
|
|
51
|
-
];
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* For Bash commands, only store output that is significant:
|
|
55
|
-
* - Has stderr content
|
|
56
|
-
* - Contains important keywords (errors, test results, deploy events)
|
|
57
|
-
* - Output is very long (> 2000 chars), indicating meaningful work
|
|
58
|
-
*/
|
|
59
|
-
function isBashSignificant(output: string, response: PostToolUseInput['tool_response']): boolean {
|
|
60
|
-
if (response?.stderr && response.stderr.trim().length > 20) return true;
|
|
61
|
-
const lower = output.toLowerCase();
|
|
62
|
-
if (IMPORTANT_BASH_KEYWORDS.some((kw) => lower.includes(kw))) return true;
|
|
63
|
-
return output.trim().length > 2000;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Determine if a tool output is significant enough to store.
|
|
68
|
-
* Always-store tools bypass the length check.
|
|
69
|
-
* Bash uses keyword-based significance detection.
|
|
70
|
-
* Other tools require non-empty stderr or output length >= minLen.
|
|
71
|
-
*/
|
|
72
|
-
function hasSignificantOutput(
|
|
73
|
-
toolName: string,
|
|
74
|
-
output: string,
|
|
75
|
-
response: PostToolUseInput['tool_response'],
|
|
76
|
-
minLen: number
|
|
77
|
-
): boolean {
|
|
78
|
-
if (ALWAYS_STORE_TOOLS.has(toolName)) return true;
|
|
79
|
-
if (toolName === 'Bash') return isBashSignificant(output, response);
|
|
80
|
-
if (response?.stderr && response.stderr.trim().length > 0) return true;
|
|
81
|
-
return output.trim().length >= minLen;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const DEFAULT_PRIVACY_CONFIG: Config['privacy'] = {
|
|
85
|
-
excludePatterns: ['password', 'secret', 'api_key', 'token', 'bearer'],
|
|
86
|
-
anonymize: false,
|
|
87
|
-
privateTags: {
|
|
88
|
-
enabled: true,
|
|
89
|
-
marker: '[PRIVATE]',
|
|
90
|
-
preserveLineCount: false,
|
|
91
|
-
supportedFormats: ['xml']
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Extract text output from tool_response object
|
|
97
|
-
*/
|
|
98
|
-
function extractToolOutput(response: PostToolUseInput['tool_response']): string {
|
|
99
|
-
if (!response) return '';
|
|
100
|
-
|
|
101
|
-
// Bash tools: stdout + stderr
|
|
102
|
-
if (response.stdout !== undefined) {
|
|
103
|
-
const parts: string[] = [];
|
|
104
|
-
if (response.stdout) parts.push(response.stdout);
|
|
105
|
-
if (response.stderr) parts.push(`[stderr] ${response.stderr}`);
|
|
106
|
-
return parts.join('\n') || '';
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Other tools may have content field
|
|
110
|
-
if (response.content !== undefined) {
|
|
111
|
-
return typeof response.content === 'string'
|
|
112
|
-
? response.content
|
|
113
|
-
: JSON.stringify(response.content);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Fallback: stringify the whole response
|
|
117
|
-
return JSON.stringify(response);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Determine if the tool execution was successful
|
|
122
|
-
*/
|
|
123
|
-
function isToolSuccess(response: PostToolUseInput['tool_response']): boolean {
|
|
124
|
-
if (!response) return false;
|
|
125
|
-
if (response.interrupted) return false;
|
|
126
|
-
// If stderr has content but stdout also has content, still consider success
|
|
127
|
-
return true;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
async function main(): Promise<void> {
|
|
131
|
-
// Read input from stdin
|
|
132
|
-
const inputData = await readStdin();
|
|
133
|
-
const input: PostToolUseInput = JSON.parse(inputData);
|
|
134
|
-
|
|
135
|
-
const config = { ...DEFAULT_CONFIG };
|
|
136
|
-
const privacyConfig = DEFAULT_PRIVACY_CONFIG;
|
|
137
|
-
|
|
138
|
-
// Allow env-based blocklist override
|
|
139
|
-
const envBlocklist = process.env.CLAUDE_MEMORY_TOOL_BLOCKLIST;
|
|
140
|
-
if (envBlocklist !== undefined) {
|
|
141
|
-
config.excludedTools = envBlocklist.split(',').map((s) => s.trim()).filter(Boolean);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// 1. Check if tool observation is enabled
|
|
145
|
-
if (!config.enabled) {
|
|
146
|
-
console.log(JSON.stringify({}));
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// 2. Check if tool is excluded
|
|
151
|
-
if (config.excludedTools?.includes(input.tool_name)) {
|
|
152
|
-
console.log(JSON.stringify({}));
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// 3. Extract output from tool_response object
|
|
157
|
-
const toolOutput = extractToolOutput(input.tool_response);
|
|
158
|
-
const success = isToolSuccess(input.tool_response);
|
|
159
|
-
|
|
160
|
-
// 4. Check success filter
|
|
161
|
-
if (!success && config.storeOnlyOnSuccess) {
|
|
162
|
-
console.log(JSON.stringify({}));
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// 4.5. Output-level filter: skip low-signal outputs
|
|
167
|
-
if (!hasSignificantOutput(
|
|
168
|
-
input.tool_name, toolOutput, input.tool_response,
|
|
169
|
-
config.minOutputLength ?? 100
|
|
170
|
-
)) {
|
|
171
|
-
console.log(JSON.stringify({}));
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
try {
|
|
176
|
-
const memoryService = getLightweightMemoryService(input.session_id);
|
|
177
|
-
|
|
178
|
-
// 5. Mask sensitive data in input
|
|
179
|
-
const maskedInput = maskSensitiveInput(input.tool_input);
|
|
180
|
-
|
|
181
|
-
// 6. Apply privacy filter to output
|
|
182
|
-
const filterResult = applyPrivacyFilter(toolOutput, privacyConfig);
|
|
183
|
-
const maskedOutput = filterResult.content;
|
|
184
|
-
|
|
185
|
-
// 7. Truncate output
|
|
186
|
-
const truncatedOutput = truncateOutput(maskedOutput, {
|
|
187
|
-
maxLength: config.maxOutputLength,
|
|
188
|
-
maxLines: config.maxOutputLines
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
// 8. Extract metadata
|
|
192
|
-
const metadata = extractMetadata(
|
|
193
|
-
input.tool_name,
|
|
194
|
-
maskedInput,
|
|
195
|
-
toolOutput,
|
|
196
|
-
success
|
|
197
|
-
);
|
|
198
|
-
|
|
199
|
-
// 8.5. Read current turn_id from state file
|
|
200
|
-
const turnId = readTurnState(input.session_id);
|
|
201
|
-
|
|
202
|
-
// 9. Create payload (include turnId in metadata for grouping)
|
|
203
|
-
const payload: ToolObservationPayload = {
|
|
204
|
-
toolName: input.tool_name,
|
|
205
|
-
toolInput: maskedInput,
|
|
206
|
-
toolOutput: truncatedOutput,
|
|
207
|
-
durationMs: 0, // Claude Code doesn't provide timing info
|
|
208
|
-
success,
|
|
209
|
-
errorMessage: input.tool_response?.stderr || undefined,
|
|
210
|
-
metadata: {
|
|
211
|
-
...metadata,
|
|
212
|
-
...(turnId ? { turnId } : {})
|
|
213
|
-
}
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
// 10. Store observation
|
|
217
|
-
await memoryService.storeToolObservation(input.session_id, payload);
|
|
218
|
-
|
|
219
|
-
// Output empty (hook doesn't return context)
|
|
220
|
-
console.log(JSON.stringify({}));
|
|
221
|
-
} catch (error) {
|
|
222
|
-
if (process.env.CLAUDE_MEMORY_DEBUG) {
|
|
223
|
-
console.error('PostToolUse hook error:', error);
|
|
224
|
-
}
|
|
225
|
-
console.log(JSON.stringify({}));
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
function readStdin(): Promise<string> {
|
|
230
|
-
return new Promise((resolve) => {
|
|
231
|
-
let data = '';
|
|
232
|
-
process.stdin.setEncoding('utf8');
|
|
233
|
-
process.stdin.on('data', (chunk) => {
|
|
234
|
-
data += chunk;
|
|
235
|
-
});
|
|
236
|
-
process.stdin.on('end', () => {
|
|
237
|
-
resolve(data);
|
|
238
|
-
});
|
|
239
|
-
});
|
|
240
|
-
}
|
|
7
|
+
import { main } from '../adapters/claude/hooks/post-tool-use.js';
|
|
241
8
|
|
|
242
9
|
main().catch(console.error);
|
|
@@ -1,208 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import * as fs from 'fs';
|
|
3
|
-
import * as net from 'net';
|
|
4
|
-
import * as os from 'os';
|
|
5
|
-
import * as path from 'path';
|
|
6
|
-
|
|
7
|
-
interface SemanticRequest {
|
|
8
|
-
sessionId: string;
|
|
9
|
-
prompt: string;
|
|
10
|
-
topK: number;
|
|
11
|
-
minScore: number;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
interface SemanticMemory {
|
|
15
|
-
type: string;
|
|
16
|
-
content: string;
|
|
17
|
-
id?: string;
|
|
18
|
-
score?: number;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
interface SemanticDaemonRequest {
|
|
22
|
-
type: 'retrieve';
|
|
23
|
-
sessionId: string;
|
|
24
|
-
prompt: string;
|
|
25
|
-
topK: number;
|
|
26
|
-
minScore: number;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
interface SemanticDaemonResponse {
|
|
30
|
-
ok: boolean;
|
|
31
|
-
memories?: SemanticMemory[];
|
|
32
|
-
error?: string;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const DEFAULT_SOCKET_PATH = path.join(
|
|
36
|
-
os.homedir(),
|
|
37
|
-
'.claude-code',
|
|
38
|
-
'memory',
|
|
39
|
-
'semantic-daemon.sock'
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
const DAEMON_SOCKET_PATH = process.env.CLAUDE_MEMORY_SEMANTIC_SOCKET || DEFAULT_SOCKET_PATH;
|
|
43
|
-
const DAEMON_START_TIMEOUT_MS = parseInt(process.env.CLAUDE_MEMORY_SEMANTIC_DAEMON_START_MS || '1500');
|
|
44
|
-
|
|
45
|
-
let daemonStartPromise: Promise<void> | null = null;
|
|
46
|
-
|
|
47
|
-
export async function retrieveSemanticMemories(
|
|
48
|
-
request: SemanticRequest,
|
|
49
|
-
timeoutMs: number
|
|
50
|
-
): Promise<SemanticMemory[]> {
|
|
51
|
-
const payload: SemanticDaemonRequest = {
|
|
52
|
-
type: 'retrieve',
|
|
53
|
-
sessionId: request.sessionId,
|
|
54
|
-
prompt: request.prompt,
|
|
55
|
-
topK: request.topK,
|
|
56
|
-
minScore: request.minScore
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
try {
|
|
60
|
-
return await requestFromDaemon(payload, timeoutMs);
|
|
61
|
-
} catch (error) {
|
|
62
|
-
if (!isConnectionError(error)) {
|
|
63
|
-
throw error;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
await ensureDaemonRunning();
|
|
67
|
-
return requestFromDaemon(payload, timeoutMs).catch((retryError) => {
|
|
68
|
-
if (process.env.CLAUDE_MEMORY_DEBUG) {
|
|
69
|
-
console.error('[semantic-client] retry failed after daemon start:', retryError);
|
|
70
|
-
}
|
|
71
|
-
throw retryError;
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function requestFromDaemon(
|
|
77
|
-
payload: SemanticDaemonRequest,
|
|
78
|
-
timeoutMs: number
|
|
79
|
-
): Promise<SemanticMemory[]> {
|
|
80
|
-
return new Promise((resolve, reject) => {
|
|
81
|
-
const client = net.createConnection(DAEMON_SOCKET_PATH);
|
|
82
|
-
client.setEncoding('utf8');
|
|
83
|
-
|
|
84
|
-
let settled = false;
|
|
85
|
-
let responseRaw = '';
|
|
86
|
-
const timer = setTimeout(() => {
|
|
87
|
-
const timeoutError = new Error(`semantic daemon timeout (${timeoutMs}ms)`);
|
|
88
|
-
(timeoutError as NodeJS.ErrnoException).code = 'ETIMEDOUT';
|
|
89
|
-
settle(timeoutError);
|
|
90
|
-
client.destroy();
|
|
91
|
-
}, timeoutMs);
|
|
92
|
-
|
|
93
|
-
const settle = (error?: Error, memories?: SemanticMemory[]) => {
|
|
94
|
-
if (settled) return;
|
|
95
|
-
settled = true;
|
|
96
|
-
clearTimeout(timer);
|
|
97
|
-
if (error) {
|
|
98
|
-
reject(error);
|
|
99
|
-
} else {
|
|
100
|
-
resolve(memories || []);
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
client.on('connect', () => {
|
|
105
|
-
client.end(JSON.stringify(payload));
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
client.on('data', (chunk) => {
|
|
109
|
-
responseRaw += chunk;
|
|
110
|
-
if (responseRaw.length > 4 * 1024 * 1024) {
|
|
111
|
-
settle(new Error('semantic daemon response too large'));
|
|
112
|
-
client.destroy();
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
client.on('end', () => {
|
|
117
|
-
try {
|
|
118
|
-
const parsed = JSON.parse(responseRaw || '{}') as SemanticDaemonResponse;
|
|
119
|
-
if (!parsed.ok) {
|
|
120
|
-
settle(new Error(parsed.error || 'semantic daemon error'));
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
settle(undefined, parsed.memories || []);
|
|
124
|
-
} catch (error) {
|
|
125
|
-
settle(error as Error);
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
client.on('error', (error) => {
|
|
130
|
-
settle(error as Error);
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export async function ensureDaemonRunning(): Promise<void> {
|
|
136
|
-
if (daemonStartPromise) {
|
|
137
|
-
return daemonStartPromise;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
daemonStartPromise = (async () => {
|
|
141
|
-
if (await canConnect()) {
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const daemonScriptPath = getDaemonScriptPath();
|
|
146
|
-
if (!fs.existsSync(daemonScriptPath)) {
|
|
147
|
-
throw new Error(`semantic daemon script not found: ${daemonScriptPath}`);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const daemonDir = path.dirname(DAEMON_SOCKET_PATH);
|
|
151
|
-
if (!fs.existsSync(daemonDir)) {
|
|
152
|
-
fs.mkdirSync(daemonDir, { recursive: true });
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const child = spawn(process.execPath, [daemonScriptPath], {
|
|
156
|
-
detached: true,
|
|
157
|
-
stdio: 'ignore',
|
|
158
|
-
env: process.env
|
|
159
|
-
});
|
|
160
|
-
child.unref();
|
|
161
|
-
|
|
162
|
-
const startDeadline = Date.now() + DAEMON_START_TIMEOUT_MS;
|
|
163
|
-
while (Date.now() < startDeadline) {
|
|
164
|
-
if (await canConnect()) {
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
await sleep(60);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
throw new Error(`semantic daemon start timeout (${DAEMON_START_TIMEOUT_MS}ms)`);
|
|
171
|
-
})();
|
|
172
|
-
|
|
173
|
-
try {
|
|
174
|
-
await daemonStartPromise;
|
|
175
|
-
} finally {
|
|
176
|
-
daemonStartPromise = null;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function getDaemonScriptPath(): string {
|
|
181
|
-
return path.join(path.dirname(new URL(import.meta.url).pathname), 'semantic-daemon.js');
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function canConnect(): Promise<boolean> {
|
|
185
|
-
return new Promise((resolve) => {
|
|
186
|
-
let settled = false;
|
|
187
|
-
const client = net.createConnection(DAEMON_SOCKET_PATH);
|
|
188
|
-
const finalize = (ok: boolean) => {
|
|
189
|
-
if (settled) return;
|
|
190
|
-
settled = true;
|
|
191
|
-
client.destroy();
|
|
192
|
-
resolve(ok);
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
client.on('connect', () => finalize(true));
|
|
196
|
-
client.on('error', () => finalize(false));
|
|
197
|
-
setTimeout(() => finalize(false), 120).unref();
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
function isConnectionError(error: unknown): boolean {
|
|
202
|
-
const code = (error as NodeJS.ErrnoException | undefined)?.code;
|
|
203
|
-
return code === 'ENOENT' || code === 'ECONNREFUSED' || code === 'EPIPE' || code === 'ECONNRESET';
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
function sleep(ms: number): Promise<void> {
|
|
207
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
208
|
-
}
|
|
1
|
+
export * from '../adapters/claude/hooks/semantic-daemon-client.js';
|