squish-memory 1.1.5 → 1.2.0
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 +32 -16
- package/CHANGELOG.md +147 -0
- package/README.md +120 -78
- package/{scripts → bin}/dependency-manager.mjs +217 -217
- package/{scripts → bin}/detect-clients.mjs +78 -78
- package/bin/install-interactive.mjs +321 -0
- package/bin/squish-mcp.mjs +46 -0
- package/bin/squish.mjs +33 -0
- package/config/mcp-migration-map.json +1 -6
- package/config/mcp-mode-semantics.json +19 -23
- package/config/mcp-remote-auth.json +3 -26
- package/config/mcp-universal.schema.json +5 -35
- package/config/settings.json +107 -52
- package/config.js +5 -0
- package/config.ts +218 -0
- package/core/adapters/config/claude-code.ts +133 -0
- package/core/adapters/config/cursor.ts +90 -0
- package/core/adapters/config/opencode.ts +89 -0
- package/core/adapters/config/windsurf.ts +90 -0
- package/core/adapters/index.ts +102 -0
- package/core/adapters/timeline.ts +116 -0
- package/core/adapters/types.ts +166 -0
- package/core/agent-preferences.ts +140 -0
- package/core/algorithms/analytics/token-estimator.ts +216 -0
- package/core/algorithms/detection/hash-filters.ts +260 -0
- package/core/algorithms/detection/semantic-ranker.ts +194 -0
- package/core/algorithms/detection/two-stage-detector.ts +421 -0
- package/core/algorithms/handlers/approve-merge.ts +215 -0
- package/core/algorithms/handlers/detect-duplicates.ts +192 -0
- package/core/algorithms/handlers/get-stats.ts +132 -0
- package/core/algorithms/handlers/list-proposals.ts +130 -0
- package/core/algorithms/handlers/preview-merge.ts +139 -0
- package/core/algorithms/handlers/reject-merge.ts +93 -0
- package/core/algorithms/handlers/reverse-merge.ts +155 -0
- package/core/algorithms/index.ts +39 -0
- package/core/algorithms/operations/cache-maintenance.ts +182 -0
- package/core/algorithms/safety/safety-checks.ts +256 -0
- package/core/algorithms/strategies/merge-strategies.ts +381 -0
- package/core/algorithms/types.ts +140 -0
- package/core/algorithms/utils/response-builder.ts +61 -0
- package/core/associations.ts +363 -0
- package/core/beliefs/decay.ts +289 -0
- package/core/beliefs/extractor.ts +131 -0
- package/core/beliefs/store.ts +557 -0
- package/core/beliefs/types.ts +38 -0
- package/core/commands/mcp-server.ts +5 -0
- package/core/compression.ts +177 -0
- package/core/config.js +2 -0
- package/core/consolidation.ts +330 -0
- package/core/context/agent-context.ts +388 -0
- package/core/context/context-paging.ts +449 -0
- package/core/context/context-window.ts +234 -0
- package/core/context/context.ts +35 -0
- package/core/embeddings/embeddings.ts +616 -0
- package/core/embeddings/google-multimodal.ts +200 -0
- package/{dist/core/local-embeddings.js → core/embeddings/local-embeddings.ts} +12 -11
- package/core/embeddings/qmd-client.ts +495 -0
- package/core/embeddings/transformers-local.ts +261 -0
- package/core/embeddings.js +4 -0
- package/core/error-handling.ts +206 -0
- package/core/external +219 -0
- package/core/graph/entity-deduplicator.ts +232 -0
- package/core/graph/graph-builder.ts +257 -0
- package/core/graph/graph-traversal.ts +490 -0
- package/core/graph/index.ts +24 -0
- package/core/graph/llm-entity-extractor.ts +402 -0
- package/core/graph/multi-hop-retrieval.ts +317 -0
- package/core/graph/relationship-extractor.ts +465 -0
- package/core/hooks/agent-hooks.ts +653 -0
- package/core/hooks/auto-tagger.ts +149 -0
- package/core/hooks/capture-filter.ts +169 -0
- package/core/hot-cache.ts +388 -0
- package/core/index.ts +10 -0
- package/core/ingestion/agent-memory.ts +167 -0
- package/core/ingestion/core-memory.ts +326 -0
- package/core/ingestion/learnings.ts +260 -0
- package/core/ingestion/signal-engine.ts +266 -0
- package/core/integrations/obsidian-vault.ts +197 -0
- package/core/layers/generator.ts +115 -0
- package/core/lib/db-client.ts +168 -0
- package/core/lib/parse-embedding.ts +59 -0
- package/core/lib/schemas.ts +102 -0
- package/core/lib/types.ts +49 -0
- package/core/lib/utils.ts +151 -0
- package/core/lib/validation.ts +180 -0
- package/core/lifecycle.ts +353 -0
- package/core/logger.ts +59 -0
- package/core/memory/bridge-discovery.ts +395 -0
- package/core/memory/categorizer.ts +390 -0
- package/core/memory/conflict-detector.ts +62 -0
- package/core/memory/consolidation.ts +372 -0
- package/core/memory/context-collector.ts +75 -0
- package/core/memory/contradiction-resolver.ts +494 -0
- package/core/memory/edit-workflow.ts +174 -0
- package/core/memory/entity-extractor.ts +426 -0
- package/core/memory/entity-resolver.ts +89 -0
- package/core/memory/explain.ts +112 -0
- package/core/memory/fact-deriver.ts +300 -0
- package/core/memory/fact-extractor.ts +120 -0
- package/core/memory/feedback-tracker.ts +200 -0
- package/core/memory/hooks.ts +230 -0
- package/core/memory/hybrid-retrieval.ts +65 -0
- package/core/memory/hybrid-scorer.ts +325 -0
- package/core/memory/hybrid-search.ts +748 -0
- package/core/memory/importance.ts +319 -0
- package/core/memory/index.ts +11 -0
- package/core/memory/loader.ts +178 -0
- package/core/memory/markdown/markdown-storage.ts +318 -0
- package/core/memory/memories.ts +565 -0
- package/core/memory/memory-lifecycle.ts +51 -0
- package/core/memory/memory-manager.ts +53 -0
- package/core/memory/migrate.ts +173 -0
- package/core/memory/normalization.ts +30 -0
- package/core/memory/path-strengthener.ts +211 -0
- package/core/memory/progressive-disclosure.ts +392 -0
- package/core/memory/query-processor.ts +130 -0
- package/core/memory/query-rewriter.ts +153 -0
- package/core/memory/response-analyzer.ts +81 -0
- package/core/memory/retrieval-feedback.ts +276 -0
- package/core/memory/serialization.ts +83 -0
- package/core/memory/stale-cleaner.ts +147 -0
- package/core/memory/stats.ts +181 -0
- package/core/memory/telemetry.ts +392 -0
- package/core/memory/temporal-facts.ts +356 -0
- package/core/memory/temporal-parser.ts +477 -0
- package/core/memory/trigger-detector.ts +104 -0
- package/core/memory/write-gate.ts +288 -0
- package/core/places/index.ts +14 -0
- package/core/places/memory-places.ts +339 -0
- package/core/places/places.ts +406 -0
- package/core/places/rules.ts +308 -0
- package/core/places/walking.ts +192 -0
- package/core/projects +89 -0
- package/core/projects.ts +131 -0
- package/core/redis.ts +82 -0
- package/core/responses.ts +187 -0
- package/core/runtime/trust-report.ts +195 -0
- package/core/runtime/trust-state.ts +360 -0
- package/core/scheduler/cron-scheduler.ts +581 -0
- package/core/scheduler/heartbeat.ts +91 -0
- package/core/scheduler/index.ts +8 -0
- package/core/scheduler/job-runner.ts +197 -0
- package/core/search/conversations.ts +166 -0
- package/core/search/entities.ts +46 -0
- package/core/search/folder-context.ts +154 -0
- package/core/search/graph-boost.ts +22 -0
- package/core/search/index.ts +4 -0
- package/core/search/qmd-wrapper.ts +84 -0
- package/core/security/encrypt.ts +51 -0
- package/core/security/governance.ts +102 -0
- package/core/security/privacy.ts +108 -0
- package/core/security/secret-detector.ts +122 -0
- package/core/session/auto-load.ts +160 -0
- package/core/session/entity-tracker.ts +363 -0
- package/core/session/index.ts +7 -0
- package/core/session/reference-resolver.ts +158 -0
- package/core/session/self-iteration-job.ts +478 -0
- package/core/session/session-hooks.ts +69 -0
- package/core/session/types.ts +36 -0
- package/core/session/working-set.ts +275 -0
- package/core/snapshots/cleanup.ts +13 -0
- package/core/snapshots/comparison.ts +59 -0
- package/core/snapshots/creation.ts +139 -0
- package/core/snapshots/retrieval.ts +44 -0
- package/core/snapshots/stats.ts +63 -0
- package/core/storage/cache.ts +241 -0
- package/core/storage/database.ts +23 -0
- package/core/summarization/cleanup.ts +13 -0
- package/core/summarization/queries.ts +32 -0
- package/core/summarization/stats.ts +64 -0
- package/core/summarization/strategies.ts +52 -0
- package/core/summarization.ts +248 -0
- package/core/temporal-facts.ts +244 -0
- package/core/tracing/collector.ts +470 -0
- package/core/tracing/visualizer.ts +195 -0
- package/core/utils/cleanup-operations.ts +50 -0
- package/core/utils/content-extraction.ts +95 -0
- package/core/utils/filter-builder.ts +56 -0
- package/core/utils/history-traversal.ts +63 -0
- package/core/utils/memory-operations.ts +56 -0
- package/core/utils/query-operations.ts +83 -0
- package/core/utils/summarization-helpers.ts +45 -0
- package/core/utils/temporal-queries.ts +39 -0
- package/core/utils/vector-operations.ts +135 -0
- package/core/utils/version-management.ts +74 -0
- package/core/worker.ts +324 -0
- package/db/adapter.ts +215 -0
- package/db/bootstrap.ts +1055 -0
- package/db/drizzle/migrations/0000_needy_cerebro.sql +402 -0
- package/db/drizzle/migrations/meta/0000_snapshot.json +3451 -0
- package/db/drizzle/migrations/meta/_journal.json +13 -0
- package/db/drizzle/schema-sqlite.ts +1032 -0
- package/db/drizzle/schema.ts +1128 -0
- package/db/drizzle.config.ts +12 -0
- package/db/index.ts +83 -0
- package/db/init.sql +5 -0
- package/db/migrations/associations.ts +35 -0
- package/db/migrations/beliefs.ts +89 -0
- package/db/migrations/core-memory.ts +35 -0
- package/db/migrations/fts.ts +59 -0
- package/db/migrations/index.ts +54 -0
- package/db/migrations/indexes.ts +36 -0
- package/db/migrations/learnings.ts +34 -0
- package/db/migrations/maintenance.ts +68 -0
- package/db/migrations/memories.ts +22 -0
- package/db/migrations/memory-places.ts +35 -0
- package/db/migrations/places.ts +49 -0
- package/db/migrations/projects.ts +21 -0
- package/db/migrations/tier-conversion.ts +24 -0
- package/db/neon.ts +22 -0
- package/db/schema/beliefs.ts +50 -0
- package/db/schema/generator.ts +159 -0
- package/db/schema/index.ts +58 -0
- package/db/schema/learnings.ts +32 -0
- package/db/schema/memories.ts +83 -0
- package/db/schema/projects.ts +33 -0
- package/db/schema.ts +13 -0
- package/db/supabase.ts +27 -0
- package/dist/config.d.ts +40 -17
- package/dist/config.js +150 -198
- package/dist/core/adapters/types.d.ts +13 -33
- package/dist/core/adapters/types.js +1 -1
- package/dist/core/agent-preferences.d.ts +16 -0
- package/dist/core/agent-preferences.js +124 -0
- package/dist/core/algorithms/safety/safety-checks.d.ts +1 -5
- package/dist/core/algorithms/types.d.ts +0 -8
- package/dist/core/associations.d.ts +3 -1
- package/dist/core/associations.js +37 -1
- package/dist/core/beliefs/decay.d.ts +27 -0
- package/dist/core/beliefs/decay.js +217 -0
- package/dist/core/beliefs/extractor.d.ts +9 -0
- package/dist/core/beliefs/extractor.js +113 -0
- package/dist/core/beliefs/store.d.ts +46 -0
- package/dist/core/beliefs/store.js +466 -0
- package/dist/core/beliefs/types.d.ts +28 -0
- package/dist/core/beliefs/types.js +2 -0
- package/dist/core/commands/mcp-server.d.ts +0 -1
- package/dist/core/commands/mcp-server.js +4 -737
- package/dist/core/commands/remember.d.ts +24 -0
- package/dist/core/commands/remember.js +144 -0
- package/dist/core/{toon.d.ts → compression.d.ts} +6 -4
- package/dist/core/{toon.js → compression.js} +8 -8
- package/dist/core/context/agent-context.js +1 -1
- package/dist/core/embeddings/embeddings.d.ts +29 -0
- package/dist/core/embeddings/embeddings.js +546 -0
- package/dist/core/embeddings/google-multimodal.js +6 -2
- package/dist/core/{local-embeddings.d.ts → embeddings/local-embeddings.d.ts} +1 -1
- package/dist/core/embeddings/local-embeddings.js +11 -0
- package/dist/core/embeddings/qmd-client.js +1 -1
- package/dist/core/embeddings/transformers-local.d.ts +64 -0
- package/dist/core/embeddings/transformers-local.js +213 -0
- package/dist/core/embeddings.d.ts +1 -28
- package/dist/core/embeddings.js +2 -453
- package/dist/core/graph/entity-deduplicator.d.ts +24 -0
- package/dist/core/graph/entity-deduplicator.js +183 -0
- package/dist/core/graph/graph-builder.d.ts +46 -0
- package/dist/core/graph/graph-builder.js +174 -0
- package/dist/core/graph/graph-traversal.d.ts +80 -0
- package/dist/core/graph/graph-traversal.js +315 -0
- package/dist/core/graph/index.d.ts +19 -0
- package/dist/core/graph/index.js +13 -0
- package/dist/core/graph/llm-entity-extractor.d.ts +49 -0
- package/dist/core/graph/llm-entity-extractor.js +313 -0
- package/dist/core/graph/multi-hop-retrieval.d.ts +48 -0
- package/dist/core/graph/multi-hop-retrieval.js +215 -0
- package/dist/core/graph/relationship-extractor.d.ts +48 -0
- package/dist/core/graph/relationship-extractor.js +351 -0
- package/dist/core/hooks/agent-hooks.d.ts +10 -1
- package/dist/core/hooks/agent-hooks.js +301 -24
- package/dist/core/hot-cache.d.ts +86 -0
- package/dist/core/hot-cache.js +285 -0
- package/dist/core/index.d.ts +9 -9
- package/dist/core/index.js +9 -12
- package/dist/core/ingestion/core-memory.d.ts +2 -2
- package/dist/core/ingestion/core-memory.js +3 -3
- package/dist/core/ingestion/learnings.js +3 -0
- package/dist/core/ingestion/signal-engine.d.ts +41 -0
- package/dist/core/ingestion/signal-engine.js +201 -0
- package/dist/core/{obsidian-vault.d.ts → integrations/obsidian-vault.d.ts} +2 -1
- package/dist/core/{obsidian-vault.js → integrations/obsidian-vault.js} +69 -7
- package/dist/core/lib/parse-embedding.d.ts +9 -0
- package/dist/core/lib/parse-embedding.js +58 -0
- package/dist/core/lib/schemas.d.ts +57 -54
- package/dist/core/lib/types.d.ts +45 -0
- package/dist/core/lib/types.js +6 -0
- package/dist/core/lib/utils.d.ts +4 -0
- package/dist/core/lib/utils.js +55 -0
- package/dist/core/lifecycle.d.ts +0 -1
- package/dist/core/lifecycle.js +13 -23
- package/dist/core/logger.d.ts +1 -0
- package/dist/core/logger.js +14 -8
- package/dist/core/mcp/tools.d.ts +0 -2
- package/dist/core/mcp/tools.js +0 -87
- package/dist/core/mcp/types.d.ts +25 -253
- package/dist/core/mcp/types.js +2 -2
- package/dist/core/memory/categorizer.js +1 -0
- package/dist/core/memory/consolidation.js +2 -28
- package/dist/core/memory/entity-extractor.d.ts +4 -0
- package/dist/core/memory/entity-extractor.js +30 -16
- package/dist/core/memory/explain.d.ts +18 -0
- package/dist/core/memory/explain.js +92 -0
- package/dist/core/memory/fact-deriver.d.ts +31 -0
- package/dist/core/memory/fact-deriver.js +236 -0
- package/dist/core/memory/hybrid-retrieval.d.ts +14 -16
- package/dist/core/memory/hybrid-retrieval.js +25 -127
- package/dist/core/memory/hybrid-scorer.js +6 -23
- package/dist/core/memory/hybrid-search.d.ts +10 -7
- package/dist/core/memory/hybrid-search.js +458 -221
- package/dist/core/memory/importance.d.ts +0 -17
- package/dist/core/memory/importance.js +1 -58
- package/dist/core/memory/index.d.ts +1 -0
- package/dist/core/memory/index.js +1 -0
- package/dist/core/memory/memories.d.ts +13 -17
- package/dist/core/memory/memories.js +78 -75
- package/dist/core/memory/memory-lifecycle.d.ts +2 -2
- package/dist/core/memory/memory-lifecycle.js +10 -18
- package/dist/core/memory/normalization.d.ts +1 -16
- package/dist/core/memory/path-strengthener.d.ts +39 -0
- package/dist/core/memory/path-strengthener.js +150 -0
- package/dist/core/memory/query-processor.js +37 -3
- package/dist/core/memory/retrieval-feedback.d.ts +70 -0
- package/dist/core/memory/retrieval-feedback.js +213 -0
- package/dist/core/memory/stale-cleaner.d.ts +26 -0
- package/dist/core/memory/stale-cleaner.js +97 -0
- package/dist/core/memory/stats.d.ts +10 -0
- package/dist/core/memory/stats.js +8 -3
- package/dist/core/memory/trigger-detector.d.ts +8 -1
- package/dist/core/memory/trigger-detector.js +42 -5
- package/dist/core/places/index.d.ts +1 -1
- package/dist/core/places/index.js +1 -1
- package/dist/core/places/places.d.ts +13 -13
- package/dist/core/places/places.js +27 -27
- package/dist/core/places/rules.js +23 -23
- package/dist/core/places/walking.d.ts +3 -3
- package/dist/core/places/walking.js +7 -7
- package/dist/core/projects.js +8 -0
- package/dist/core/runtime/trust-report.d.ts +102 -0
- package/dist/core/runtime/trust-report.js +107 -0
- package/dist/core/runtime/trust-state.d.ts +12 -0
- package/dist/core/runtime/trust-state.js +309 -0
- package/dist/core/scheduler/cron-scheduler.d.ts +1 -1
- package/dist/core/scheduler/cron-scheduler.js +164 -3
- package/dist/core/scheduler/job-runner.js +1 -1
- package/dist/core/search/qmd-wrapper.d.ts +36 -0
- package/dist/core/search/qmd-wrapper.js +58 -0
- package/dist/core/session/auto-load.js +28 -3
- package/dist/core/session/entity-tracker.d.ts +62 -0
- package/dist/core/session/entity-tracker.js +287 -0
- package/dist/core/session/reference-resolver.d.ts +26 -0
- package/dist/core/session/reference-resolver.js +121 -0
- package/dist/core/session/self-iteration-job.d.ts +15 -0
- package/dist/core/session/self-iteration-job.js +163 -58
- package/dist/core/session/working-set.d.ts +50 -0
- package/dist/core/session/working-set.js +212 -0
- package/dist/core/snapshots/creation.d.ts +2 -8
- package/dist/core/snapshots/creation.js +3 -12
- package/dist/core/utils/summarization-helpers.d.ts +0 -4
- package/dist/core/utils/summarization-helpers.js +1 -6
- package/dist/db/bootstrap.d.ts +2 -0
- package/dist/db/bootstrap.js +229 -280
- package/dist/db/drizzle/schema-sqlite.d.ts +702 -1
- package/dist/db/drizzle/schema-sqlite.js +83 -4
- package/dist/db/drizzle/schema.d.ts +653 -1
- package/dist/db/drizzle/schema.js +93 -4
- package/dist/db/migrations/associations.d.ts +6 -0
- package/dist/db/migrations/associations.js +29 -0
- package/dist/db/migrations/beliefs.d.ts +10 -0
- package/dist/db/migrations/beliefs.js +76 -0
- package/dist/db/migrations/core-memory.d.ts +6 -0
- package/dist/db/migrations/core-memory.js +29 -0
- package/dist/db/migrations/fts.d.ts +6 -0
- package/dist/db/migrations/fts.js +52 -0
- package/dist/db/migrations/index.d.ts +25 -0
- package/dist/db/migrations/index.js +51 -0
- package/dist/db/migrations/indexes.d.ts +6 -0
- package/dist/db/migrations/indexes.js +30 -0
- package/dist/db/migrations/learnings.d.ts +7 -0
- package/dist/db/migrations/learnings.js +26 -0
- package/dist/db/migrations/maintenance.d.ts +6 -0
- package/dist/db/migrations/maintenance.js +61 -0
- package/dist/db/migrations/memories.d.ts +7 -0
- package/dist/db/migrations/memories.js +16 -0
- package/dist/db/migrations/memory-places.d.ts +6 -0
- package/dist/db/migrations/memory-places.js +29 -0
- package/dist/db/migrations/places.d.ts +6 -0
- package/dist/db/migrations/places.js +43 -0
- package/dist/db/migrations/projects.d.ts +3 -0
- package/dist/db/migrations/projects.js +13 -0
- package/dist/db/migrations/tier-conversion.d.ts +7 -0
- package/dist/db/migrations/tier-conversion.js +20 -0
- package/dist/db/schema/beliefs.d.ts +9 -0
- package/dist/db/schema/beliefs.js +46 -0
- package/dist/db/schema/generator.d.ts +38 -0
- package/dist/db/schema/generator.js +108 -0
- package/dist/db/schema/index.d.ts +19 -20
- package/dist/db/schema/index.js +25 -79
- package/dist/db/schema/learnings.d.ts +7 -0
- package/dist/db/schema/learnings.js +30 -0
- package/dist/db/schema/memories.d.ts +7 -0
- package/dist/db/schema/memories.js +81 -0
- package/dist/db/schema/projects.d.ts +4 -0
- package/dist/db/schema/projects.js +31 -0
- package/dist/packages/mcp/src/index.d.ts +3 -0
- package/dist/packages/mcp/src/index.js +733 -0
- package/mcp.json.example +8 -11
- package/package.json +57 -76
- package/packages/cli/package.json +22 -0
- package/packages/cli/src/commands/clean.ts +68 -0
- package/packages/cli/src/commands/context.ts +79 -0
- package/packages/cli/src/commands/doctor.ts +357 -0
- package/packages/cli/src/commands/forget.ts +72 -0
- package/packages/cli/src/commands/health.ts +36 -0
- package/packages/cli/src/commands/inspect.ts +41 -0
- package/packages/cli/src/commands/link.ts +50 -0
- package/packages/cli/src/commands/migrate.ts +93 -0
- package/packages/cli/src/commands/recall.ts +99 -0
- package/packages/cli/src/commands/recent.ts +57 -0
- package/packages/cli/src/commands/remember.ts +139 -0
- package/packages/cli/src/commands/run.ts +58 -0
- package/packages/cli/src/commands/stale.ts +43 -0
- package/packages/cli/src/commands/stats.ts +42 -0
- package/packages/cli/src/index.ts +57 -0
- package/packages/cli/tsconfig.json +24 -0
- package/packages/mcp/package.json +26 -0
- package/packages/mcp/src/index.ts +877 -0
- package/packages/mcp/tsconfig.json +20 -0
- package/skills/squish-memory/SKILL.md +38 -35
- package/skills/squish-memory/{scripts/install.sh → install.sh} +1 -1
- package/skills/squish-memory/references/claude-desktop.json +12 -0
- package/skills/squish-memory/references/openclaw.json +13 -0
- package/skills/squish-memory/references/opencode.json +14 -0
- package/config/hooks/claude-code-hooks.json +0 -39
- package/config/hooks/cursor-hooks.json +0 -30
- package/config/hooks/opencode-hooks.json +0 -30
- package/config/hooks/windsurf-hooks.json +0 -30
- package/config/mcp-cli-fallback-policy.json +0 -22
- package/config/mcp.json +0 -38
- package/config/plugin-manifest.json +0 -101
- package/config/plugin-manifest.schema.json +0 -244
- package/config/plugin.json +0 -32
- package/config/remote-memory-policy.json +0 -32
- package/core/commands/context-paging.md +0 -51
- package/core/commands/context-status.md +0 -22
- package/core/commands/context.md +0 -5
- package/core/commands/core-memory.md +0 -56
- package/core/commands/health.md +0 -5
- package/core/commands/init.md +0 -39
- package/core/commands/merge.md +0 -113
- package/core/commands/recall.md +0 -5
- package/core/commands/remember.md +0 -11
- package/core/commands/search.md +0 -10
- package/dist/core/commands/managed-sync.d.ts +0 -10
- package/dist/core/commands/managed-sync.js +0 -64
- package/dist/core/external-folder/index.d.ts +0 -102
- package/dist/core/external-folder/index.js +0 -294
- package/dist/core/namespaces/index.d.ts +0 -71
- package/dist/core/namespaces/index.js +0 -305
- package/dist/core/namespaces/uri-parser.d.ts +0 -31
- package/dist/core/namespaces/uri-parser.js +0 -74
- package/dist/core/search/qmd-search.d.ts +0 -61
- package/dist/core/search/qmd-search.js +0 -178
- package/dist/core/session-hooks/self-iteration-job.d.ts +0 -20
- package/dist/core/session-hooks/self-iteration-job.js +0 -282
- package/dist/core/session-hooks/session-hooks.d.ts +0 -18
- package/dist/core/session-hooks/session-hooks.js +0 -58
- package/dist/core/snapshots.d.ts +0 -29
- package/dist/core/snapshots.js +0 -220
- package/dist/core/sync/qmd-sync.d.ts +0 -94
- package/dist/core/sync/qmd-sync.js +0 -201
- package/dist/index.d.ts +0 -7
- package/dist/index.js +0 -1677
- package/dist/vendor/sql.js/sql-wasm.wasm +0 -0
- package/dist/webui/server.d.ts +0 -5
- package/dist/webui/server.js +0 -642
- package/generated/mcp/manifest.json +0 -23
- package/generated/mcp/mcp-servers.json +0 -25
- package/generated/mcp/mcporter.json +0 -34
- package/generated/mcp/openclaw-memory-qmd.json +0 -17
- package/generated/mcp/runtime.json +0 -12
- package/scripts/README.md +0 -60
- package/scripts/build-release.sh +0 -36
- package/scripts/check-secrets.js +0 -132
- package/scripts/copy-runtime-assets.mjs +0 -26
- package/scripts/generate-mcp.mjs +0 -264
- package/scripts/github-release.sh +0 -77
- package/scripts/init-dirs.mjs +0 -13
- package/scripts/install-claude-code.sh +0 -85
- package/scripts/install-cursor.sh +0 -56
- package/scripts/install-hooks.sh +0 -73
- package/scripts/install-interactive.mjs +0 -357
- package/scripts/install-opencode.sh +0 -75
- package/scripts/install-plugin.mjs +0 -415
- package/scripts/install-windsurf.sh +0 -67
- package/scripts/remote-preflight.mjs +0 -62
- package/scripts/squish-fallback.mjs +0 -132
- package/scripts/test-interactive.mjs +0 -131
- package/scripts/verify-mcp.mjs +0 -214
- package/skills/squish-memory/scripts/install.mjs +0 -335
- package/skills/squish-memory/write_skill.js +0 -2
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Vector Search - Pure semantic search with optional graph boosting + multi-session support
|
|
3
|
+
*
|
|
4
|
+
* Uses cosine similarity on embeddings + optional graph boost
|
|
5
|
+
* BM25 removed - use qmd-client for hot tier (BM25 + vectors + reranking)
|
|
4
6
|
*/
|
|
5
7
|
import { getDb } from '../../db/index.js';
|
|
6
8
|
import { createDatabaseClient } from '../storage/database.js';
|
|
@@ -9,70 +11,391 @@ import { requireProject } from '../../core/projects.js';
|
|
|
9
11
|
import { deserializeTags, deserializeMetadata, normalizeTags } from './serialization.js';
|
|
10
12
|
import { computeGraphBoost } from '../search/graph-boost.js';
|
|
11
13
|
import { normalizeTimestamp } from '../lib/utils.js';
|
|
14
|
+
import { parseEmbedding } from '../lib/parse-embedding.js';
|
|
12
15
|
import { cosineSimilarity } from '../utils/vector-operations.js';
|
|
13
16
|
import config from '../../config.js';
|
|
17
|
+
import { getRelatedMemories } from '../associations.js';
|
|
18
|
+
import { getPlaceMemories } from '../places/memory-places.js';
|
|
19
|
+
import { getPlaceByType } from '../places/places.js';
|
|
20
|
+
import { multiHopSearch } from '../graph/multi-hop-retrieval.js';
|
|
21
|
+
import { logger } from '../logger.js';
|
|
22
|
+
/**
|
|
23
|
+
* Detect if query asks about time (temporal queries)
|
|
24
|
+
*/
|
|
25
|
+
function isTemporalQuery(query) {
|
|
26
|
+
const temporalIndicators = [
|
|
27
|
+
'when', 'how long', 'how many', 'ago', 'since', 'until',
|
|
28
|
+
'before', 'after', 'earlier', 'later', 'yesterday', 'tomorrow',
|
|
29
|
+
'last week', 'next week', 'last month', 'next month'
|
|
30
|
+
];
|
|
31
|
+
const lower = query.toLowerCase();
|
|
32
|
+
return temporalIndicators.some(w => lower.includes(w));
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Detect if query spans multiple sessions (multi-hop queries)
|
|
36
|
+
*/
|
|
37
|
+
function isMultiSessionQuery(query) {
|
|
38
|
+
const multiSessionIndicators = [
|
|
39
|
+
'when', 'then', 'before', 'after', 'earlier', 'later',
|
|
40
|
+
'previous', 'next', 'first', 'later', 'subsequently',
|
|
41
|
+
'across', 'between', 'both', 'another', 'different'
|
|
42
|
+
];
|
|
43
|
+
const lower = query.toLowerCase();
|
|
44
|
+
return multiSessionIndicators.some(w => lower.includes(w));
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Check if content contains date/time references
|
|
48
|
+
*/
|
|
49
|
+
function hasDateReference(content) {
|
|
50
|
+
const datePatterns = [
|
|
51
|
+
/\b\d{4}\b/, // Years: 2023, 2022
|
|
52
|
+
/\b\d{1,2}\s+(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\b/i, // Month dates
|
|
53
|
+
/\b(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\s+\d{1,2}\b/i,
|
|
54
|
+
/\b\d{1,2}\/\d{1,2}\/\d{2,4}\b/, // Dates: 5/7/2023
|
|
55
|
+
/\b(yesterday|today|tomorrow|last week|last month|last year)\b/i,
|
|
56
|
+
/\b(\d+)\s+(day|week|month|year)s?\s+(ago|before)\b/i,
|
|
57
|
+
];
|
|
58
|
+
const lower = content.toLowerCase();
|
|
59
|
+
return datePatterns.some(p => p.test(content) || p.test(lower));
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Expand query for multi-session retrieval
|
|
63
|
+
*/
|
|
64
|
+
function expandQueryForMultiSession(query) {
|
|
65
|
+
const expansions = [query];
|
|
66
|
+
const lower = query.toLowerCase();
|
|
67
|
+
// Add time-based expansions
|
|
68
|
+
if (lower.includes('when')) {
|
|
69
|
+
expansions.push(query.replace(/when/i, '').trim());
|
|
70
|
+
}
|
|
71
|
+
if (lower.includes('before') || lower.includes('after')) {
|
|
72
|
+
expansions.push(query.replace(/before|after/i, '').trim());
|
|
73
|
+
}
|
|
74
|
+
if (lower.includes('earlier') || lower.includes('later')) {
|
|
75
|
+
expansions.push(query.replace(/earlier|later/i, '').trim());
|
|
76
|
+
}
|
|
77
|
+
// Add "mentioning" expansions for factual queries
|
|
78
|
+
if (lower.includes('what') || lower.includes('how')) {
|
|
79
|
+
expansions.push(query + ' mentioned');
|
|
80
|
+
expansions.push(query + ' said');
|
|
81
|
+
}
|
|
82
|
+
return [...new Set(expansions.filter(e => e.length > 2))];
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Expand query for temporal retrieval - add date/temporal context
|
|
86
|
+
*/
|
|
87
|
+
function expandQueryForTemporal(query) {
|
|
88
|
+
const expansions = [query];
|
|
89
|
+
const lower = query.toLowerCase();
|
|
90
|
+
// For "when" questions, search with entity + date context
|
|
91
|
+
if (lower.includes('when')) {
|
|
92
|
+
// Extract entity name (everything after "when did" or "when is")
|
|
93
|
+
const entityMatch = query.match(/when\s+(?:did|is|was|were)\s+(\w+)/i);
|
|
94
|
+
if (entityMatch) {
|
|
95
|
+
const entity = entityMatch[1];
|
|
96
|
+
// Search with entity name alone (might match date mentions)
|
|
97
|
+
expansions.push(entity);
|
|
98
|
+
expansions.push(query.replace(/when\s+(?:did|is|was|were)\s+/i, '').trim());
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Add "date" and "time" expansions
|
|
102
|
+
if (lower.includes('when') || lower.includes('how long') || lower.includes('ago')) {
|
|
103
|
+
expansions.push(query + ' date');
|
|
104
|
+
expansions.push(query + ' time');
|
|
105
|
+
}
|
|
106
|
+
return [...new Set(expansions.filter(e => e.length > 2))];
|
|
107
|
+
}
|
|
14
108
|
/**
|
|
15
|
-
*
|
|
16
|
-
* Higher values = more influence from lower-ranked items
|
|
17
|
-
* 60 is the standard value used in research
|
|
109
|
+
* Score with recency + similarity + entity boost (NO LLM required)
|
|
18
110
|
*/
|
|
19
|
-
|
|
111
|
+
function scoreWithHeuristics(result, query, now) {
|
|
112
|
+
let score = result.similarity ?? 0;
|
|
113
|
+
// 1. Recency boost: Recent = higher (up to +0.1)
|
|
114
|
+
if (result.createdAt) {
|
|
115
|
+
const created = new Date(result.createdAt).getTime();
|
|
116
|
+
const ageHours = (now - created) / (1000 * 60 * 60);
|
|
117
|
+
const recencyScore = Math.max(0, 0.1 * Math.exp(-ageHours / 720)); // Decay over 30 days
|
|
118
|
+
score += recencyScore;
|
|
119
|
+
}
|
|
120
|
+
// 2. Entity overlap: Query words appearing in content = boost
|
|
121
|
+
const queryWords = new Set(query.toLowerCase().split(/\s+/).filter(w => w.length > 3));
|
|
122
|
+
const contentWords = new Set((result.content ?? "").toLowerCase().split(/\s+/));
|
|
123
|
+
const overlap = [...queryWords].filter(w => contentWords.has(w)).length;
|
|
124
|
+
score += overlap * 0.02; // Small boost per matching word
|
|
125
|
+
return score;
|
|
126
|
+
}
|
|
20
127
|
/**
|
|
21
|
-
* Main
|
|
128
|
+
* Main search function - vectors + graph boost + heuristics + places + sessions
|
|
129
|
+
* Unified search integrating Places, Graph, and Memory
|
|
22
130
|
*/
|
|
23
131
|
export async function hybridSearch(input, options = {}) {
|
|
24
132
|
const limit = options.limit ?? input.limit ?? 10;
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
133
|
+
const enableMultiSession = options.enableMultiSession !== false;
|
|
134
|
+
const enableHeuristics = options.enableHeuristics !== false;
|
|
135
|
+
const isMultiHop = enableMultiSession && isMultiSessionQuery(input.query);
|
|
136
|
+
const isTemporal = isTemporalQuery(input.query);
|
|
137
|
+
let vectorResults;
|
|
138
|
+
if (isMultiHop) {
|
|
139
|
+
// Multi-hop: use expansion to get more coverage
|
|
140
|
+
const expandedQueries = expandQueryForMultiSession(input.query);
|
|
141
|
+
const allResults = [];
|
|
142
|
+
for (const expQuery of expandedQueries) {
|
|
143
|
+
const expResults = await vectorSearch({ ...input, query: expQuery }, { ...options, limit: Math.ceil(limit * 2) });
|
|
144
|
+
allResults.push(...expResults);
|
|
145
|
+
}
|
|
146
|
+
const byId = new Map();
|
|
147
|
+
for (const r of allResults) {
|
|
148
|
+
const existing = byId.get(r.id);
|
|
149
|
+
if (!existing || (r.similarity ?? 0) > (existing.similarity ?? 0)) {
|
|
150
|
+
byId.set(r.id, r);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
vectorResults = Array.from(byId.values());
|
|
154
|
+
}
|
|
155
|
+
else if (isTemporal) {
|
|
156
|
+
// Temporal: fetch more results
|
|
157
|
+
vectorResults = await vectorSearch(input, { ...options, limit: limit * 4 });
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
// Regular query
|
|
161
|
+
vectorResults = await vectorSearch(input, { ...options, limit: limit * 2 });
|
|
162
|
+
}
|
|
163
|
+
// Task 2: Integrate Places into retrieval
|
|
164
|
+
// If placeId or placeType specified, filter/boost results from that place
|
|
165
|
+
if (input.placeId || input.placeType) {
|
|
166
|
+
vectorResults = await applyPlaceFilterAndBoost(vectorResults, input, limit);
|
|
167
|
+
}
|
|
168
|
+
// Task 3: Add session temporal scope
|
|
169
|
+
// If sessionId specified, boost memories from same session
|
|
170
|
+
if (input.sessionId) {
|
|
171
|
+
vectorResults = applySessionBoost(vectorResults, input.sessionId);
|
|
172
|
+
}
|
|
173
|
+
// TEMPORAL: Boost results with dates for temporal queries
|
|
174
|
+
if (isTemporal) {
|
|
175
|
+
vectorResults = applyTemporalBoost(vectorResults);
|
|
176
|
+
}
|
|
177
|
+
// Apply heuristics if enabled (recency + entity overlap)
|
|
178
|
+
if (enableHeuristics) {
|
|
179
|
+
const now = Date.now();
|
|
180
|
+
vectorResults = vectorResults.map(r => ({
|
|
181
|
+
...r,
|
|
182
|
+
similarity: scoreWithHeuristics(r, input.query, now)
|
|
183
|
+
}));
|
|
184
|
+
}
|
|
185
|
+
// Graph boost
|
|
186
|
+
const graphWeight = config.scoringWeights.graphBoost;
|
|
187
|
+
const candidateIds = vectorResults.map(r => r.id);
|
|
34
188
|
const graphBoostMap = await computeGraphBoost(candidateIds);
|
|
35
|
-
|
|
36
|
-
|
|
189
|
+
let results = applyGraphBoostWithWeight(vectorResults, graphBoostMap, limit, graphWeight);
|
|
190
|
+
// Expand with associated memories for better coverage
|
|
191
|
+
if (options.includeAssociations !== false) {
|
|
192
|
+
results = await expandWithAssociations(results, limit);
|
|
193
|
+
}
|
|
194
|
+
// Task 4: Enable multi-hop graph traversal by default
|
|
195
|
+
// If query is detected as multi-hop, use actual graph traversal
|
|
196
|
+
if (isMultiHop && options.enableGraphTraversal !== false && input.project) {
|
|
197
|
+
try {
|
|
198
|
+
const graphResults = await multiHopSearch({
|
|
199
|
+
query: input.query,
|
|
200
|
+
project: input.project,
|
|
201
|
+
limit: limit,
|
|
202
|
+
includeVectorResults: false,
|
|
203
|
+
includeGraphResults: true,
|
|
204
|
+
});
|
|
205
|
+
// Merge graph results with vector results
|
|
206
|
+
const existingIds = new Set(results.map(r => r.id));
|
|
207
|
+
for (const gr of graphResults) {
|
|
208
|
+
if (!existingIds.has(gr.id)) {
|
|
209
|
+
results.push({
|
|
210
|
+
...gr,
|
|
211
|
+
similarity: (gr.similarity ?? 0) * 0.9 // Slightly lower weight
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// Re-sort
|
|
216
|
+
results.sort((a, b) => (b.similarity ?? 0) - (a.similarity ?? 0));
|
|
217
|
+
}
|
|
218
|
+
catch (e) {
|
|
219
|
+
// Multi-hop failed, continue with vector results
|
|
220
|
+
logger.debug(`[HybridSearch] Multi-hop failed: ${e}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return results;
|
|
37
224
|
}
|
|
38
225
|
/**
|
|
39
|
-
*
|
|
226
|
+
* Task 2: Filter and boost results by place
|
|
40
227
|
*/
|
|
41
|
-
async function
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const isEmptyQuery = ftsQuery === '*';
|
|
50
|
-
// Build WHERE conditions
|
|
51
|
-
const conditions = [];
|
|
52
|
-
const params = [];
|
|
53
|
-
// For non-empty queries, use FTS5 MATCH
|
|
54
|
-
if (!isEmptyQuery) {
|
|
55
|
-
conditions.push('memories_fts MATCH ?');
|
|
56
|
-
params.push(ftsQuery);
|
|
228
|
+
async function applyPlaceFilterAndBoost(results, input, limit) {
|
|
229
|
+
let placeIds = [];
|
|
230
|
+
// Get place IDs from placeId or placeType
|
|
231
|
+
if (input.placeType && input.project) {
|
|
232
|
+
const project = await requireProject(input.project);
|
|
233
|
+
const place = await getPlaceByType(project.id, input.placeType);
|
|
234
|
+
if (place) {
|
|
235
|
+
placeIds = [place.id];
|
|
57
236
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
237
|
+
}
|
|
238
|
+
else if (input.placeId) {
|
|
239
|
+
placeIds = [input.placeId];
|
|
240
|
+
}
|
|
241
|
+
if (placeIds.length === 0) {
|
|
242
|
+
return results;
|
|
243
|
+
}
|
|
244
|
+
// Get memories in this place
|
|
245
|
+
const placeMemories = new Set();
|
|
246
|
+
for (const pid of placeIds) {
|
|
247
|
+
const memIds = await getPlaceMemories(pid, limit * 3);
|
|
248
|
+
for (const mid of memIds) {
|
|
249
|
+
placeMemories.add(mid);
|
|
61
250
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
251
|
+
}
|
|
252
|
+
// Boost memories from the specified place
|
|
253
|
+
const PLACE_BOOST = 0.15;
|
|
254
|
+
const boosted = results.map(r => {
|
|
255
|
+
if (placeMemories.has(r.id)) {
|
|
256
|
+
return {
|
|
257
|
+
...r,
|
|
258
|
+
similarity: (r.similarity ?? 0) + PLACE_BOOST
|
|
259
|
+
};
|
|
65
260
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
261
|
+
return r;
|
|
262
|
+
});
|
|
263
|
+
// Re-sort with place boost
|
|
264
|
+
boosted.sort((a, b) => (b.similarity ?? 0) - (a.similarity ?? 0));
|
|
265
|
+
return boosted;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Task 3: Boost memories from the same session (temporal)
|
|
269
|
+
*/
|
|
270
|
+
function applySessionBoost(results, sessionId) {
|
|
271
|
+
const SESSION_BOOST = 0.1;
|
|
272
|
+
const boosted = results.map(r => {
|
|
273
|
+
// Check if memory's session matches query's session
|
|
274
|
+
const memSession = r.metadata?.sessionMetadata?.sessionId;
|
|
275
|
+
if (memSession === sessionId) {
|
|
276
|
+
return {
|
|
277
|
+
...r,
|
|
278
|
+
similarity: (r.similarity ?? 0) + SESSION_BOOST
|
|
279
|
+
};
|
|
72
280
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
281
|
+
return r;
|
|
282
|
+
});
|
|
283
|
+
// Re-sort with session boost
|
|
284
|
+
boosted.sort((a, b) => (b.similarity ?? 0) - (a.similarity ?? 0));
|
|
285
|
+
return boosted;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* TEMPORAL FIX: Boost memories that contain date references for "when" questions
|
|
289
|
+
* Also boost by date RECENCY - closer to today = higher for temporal queries
|
|
290
|
+
*/
|
|
291
|
+
function applyTemporalBoost(results) {
|
|
292
|
+
const TEMPORAL_BOOST = 0.25; // Moderate boost for date-containing memories
|
|
293
|
+
const boosted = results.map(r => {
|
|
294
|
+
let boost = 0;
|
|
295
|
+
// Boost 1: Has date reference - high priority for temporal
|
|
296
|
+
if (hasDateReference(r.content ?? "")) {
|
|
297
|
+
boost += TEMPORAL_BOOST;
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
...r,
|
|
301
|
+
similarity: (r.similarity ?? 0) + boost
|
|
302
|
+
};
|
|
303
|
+
});
|
|
304
|
+
// Re-sort with temporal boost
|
|
305
|
+
boosted.sort((a, b) => (b.similarity ?? 0) - (a.similarity ?? 0));
|
|
306
|
+
return boosted;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* MULTI-HOP FIX: Get memories from adjacent sessions using DB query
|
|
310
|
+
* This provides actual cross-session retrieval for multi-hop queries
|
|
311
|
+
*/
|
|
312
|
+
async function applySessionAdjacencyBoost(results, input, limit) {
|
|
313
|
+
if (!input.project)
|
|
314
|
+
return results;
|
|
315
|
+
// Extract session tags from top results
|
|
316
|
+
const sessionTags = new Set();
|
|
317
|
+
for (const r of results.slice(0, 5)) {
|
|
318
|
+
const sessionTag = extractSessionTag(r.tags);
|
|
319
|
+
if (sessionTag) {
|
|
320
|
+
sessionTags.add(sessionTag);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
if (sessionTags.size === 0) {
|
|
324
|
+
return results;
|
|
325
|
+
}
|
|
326
|
+
// Get adjacent session tags
|
|
327
|
+
const adjacentSessionTags = new Set();
|
|
328
|
+
for (const tag of sessionTags) {
|
|
329
|
+
const adjacent = getAdjacentSessionTags(tag);
|
|
330
|
+
for (const adj of adjacent) {
|
|
331
|
+
adjacentSessionTags.add(adj);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if (adjacentSessionTags.size === 0) {
|
|
335
|
+
return results;
|
|
336
|
+
}
|
|
337
|
+
// Fetch memories from adjacent sessions
|
|
338
|
+
const project = await requireProject(input.project);
|
|
339
|
+
const adjacentMemories = [];
|
|
340
|
+
for (const sessionTag of adjacentSessionTags) {
|
|
341
|
+
const memories = await getMemoriesBySession(project.id, sessionTag, limit);
|
|
342
|
+
adjacentMemories.push(...memories);
|
|
343
|
+
}
|
|
344
|
+
if (adjacentMemories.length === 0) {
|
|
345
|
+
return results;
|
|
346
|
+
}
|
|
347
|
+
// Merge with boost
|
|
348
|
+
const existingIds = new Set(results.map(r => r.id));
|
|
349
|
+
const ADJACENT_BOOST = 0.3;
|
|
350
|
+
const merged = [...results];
|
|
351
|
+
for (const mem of adjacentMemories) {
|
|
352
|
+
if (!existingIds.has(mem.id)) {
|
|
353
|
+
merged.push({
|
|
354
|
+
...mem,
|
|
355
|
+
similarity: (mem.similarity ?? 0) + ADJACENT_BOOST
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
// Re-sort
|
|
360
|
+
merged.sort((a, b) => (b.similarity ?? 0) - (a.similarity ?? 0));
|
|
361
|
+
return merged.slice(0, limit * 2);
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Expand results with directly associated memories
|
|
365
|
+
*/
|
|
366
|
+
async function expandWithAssociations(results, limit) {
|
|
367
|
+
const allIds = new Set(results.map(r => r.id));
|
|
368
|
+
const expanded = [...results];
|
|
369
|
+
// Get related for top results only
|
|
370
|
+
for (const r of results.slice(0, 3)) {
|
|
371
|
+
try {
|
|
372
|
+
const related = await getRelatedMemories(r.id, 5);
|
|
373
|
+
for (const rel of related) {
|
|
374
|
+
if (!allIds.has(rel.id)) {
|
|
375
|
+
allIds.add(rel.id);
|
|
376
|
+
expanded.push({
|
|
377
|
+
...rel,
|
|
378
|
+
similarity: (rel.similarity ?? 0) * 0.8 // Slightly lower weight
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
catch (e) {
|
|
384
|
+
// Skip errors
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
// Re-sort and return top results
|
|
388
|
+
expanded.sort((a, b) => (b.similarity ?? 0) - (a.similarity ?? 0));
|
|
389
|
+
return expanded.slice(0, limit);
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Get memories by session ID (stored in tags as "dialogue-1", "session-2", etc.)
|
|
393
|
+
*/
|
|
394
|
+
async function getMemoriesBySession(projectId, sessionTag, limit) {
|
|
395
|
+
try {
|
|
396
|
+
const db = createDatabaseClient(await getDb());
|
|
397
|
+
const sqlite = db.$client;
|
|
398
|
+
// Query memories where tags contain the session tag
|
|
76
399
|
const statement = sqlite.prepare(`
|
|
77
400
|
SELECT
|
|
78
401
|
m.id as id,
|
|
@@ -82,36 +405,69 @@ async function bm25Search(input, options) {
|
|
|
82
405
|
m.summary as summary,
|
|
83
406
|
m.tags as tags,
|
|
84
407
|
m.metadata as metadata,
|
|
85
|
-
|
|
408
|
+
m.embedding as embedding,
|
|
409
|
+
m.embedding_json as embeddingJson,
|
|
86
410
|
m.created_at as createdAt
|
|
87
411
|
FROM memories m
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
${isEmptyQuery ? 'ORDER BY m.created_at DESC' : 'ORDER BY bm25(memories_fts)'}
|
|
412
|
+
WHERE m.project_id = ? AND m.tags LIKE ?
|
|
413
|
+
ORDER BY m.created_at DESC
|
|
91
414
|
LIMIT ?
|
|
92
415
|
`);
|
|
93
|
-
const rows = statement.all(
|
|
94
|
-
|
|
95
|
-
return rows.map((row, index) => ({
|
|
416
|
+
const rows = statement.all(projectId, `%${sessionTag}%`, limit);
|
|
417
|
+
return rows.map((row) => ({
|
|
96
418
|
id: row.id,
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
tags: deserializeTags(row.tags ?? null),
|
|
106
|
-
metadata: deserializeMetadata(row.metadata ?? null),
|
|
107
|
-
createdAt: row.createdAt ? (normalizeTimestamp(Number(row.createdAt)) ?? undefined) : undefined,
|
|
108
|
-
},
|
|
419
|
+
projectId: row.projectId,
|
|
420
|
+
type: row.type,
|
|
421
|
+
content: row.content,
|
|
422
|
+
summary: row.summary ?? undefined,
|
|
423
|
+
tags: deserializeTags(row.tags ?? null),
|
|
424
|
+
metadata: deserializeMetadata(row.metadata ?? null),
|
|
425
|
+
createdAt: row.createdAt ? (normalizeTimestamp(Number(row.createdAt)) ?? undefined) : undefined,
|
|
426
|
+
similarity: 0.5, // Default similarity for fetched memories
|
|
109
427
|
}));
|
|
110
428
|
}
|
|
111
429
|
catch (error) {
|
|
112
|
-
|
|
430
|
+
logger.debug(`[HybridSearch] getMemoriesBySession failed: ${error}`);
|
|
431
|
+
return [];
|
|
113
432
|
}
|
|
114
433
|
}
|
|
434
|
+
/**
|
|
435
|
+
* Extract session tag from memory tags (e.g., "dialogue-1", "session_1", "D1", "D2")
|
|
436
|
+
*/
|
|
437
|
+
function extractSessionTag(tags) {
|
|
438
|
+
if (!tags)
|
|
439
|
+
return null;
|
|
440
|
+
// Look for patterns:
|
|
441
|
+
// - "dialogue-1", "dialogue_1", "dialogue1"
|
|
442
|
+
// - "session-2", "session_2", "session2"
|
|
443
|
+
// - "conv-1", "conv_1", "conv1"
|
|
444
|
+
// - "D1", "D2" (capital D followed by number)
|
|
445
|
+
for (const tag of tags) {
|
|
446
|
+
const lower = tag.toLowerCase();
|
|
447
|
+
if (/^(dialogue|session|conv|d)[_-]?\d+$/.test(lower)) {
|
|
448
|
+
return tag;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return null;
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Get adjacent session tags (e.g., dialogue-1 -> dialogue-2, D1 -> D2)
|
|
455
|
+
*/
|
|
456
|
+
function getAdjacentSessionTags(currentTag) {
|
|
457
|
+
const adjacent = [];
|
|
458
|
+
// Pattern: dialogue-N, session-N, D-N, conv-N (with or without hyphen)
|
|
459
|
+
const match = currentTag.match(/^(.*?)(\d+)$/i);
|
|
460
|
+
if (match) {
|
|
461
|
+
const prefix = match[1];
|
|
462
|
+
const num = parseInt(match[2], 10);
|
|
463
|
+
// Adjacent: previous and next
|
|
464
|
+
if (num > 1) {
|
|
465
|
+
adjacent.push(`${prefix}${num - 1}`);
|
|
466
|
+
}
|
|
467
|
+
adjacent.push(`${prefix}${num + 1}`);
|
|
468
|
+
}
|
|
469
|
+
return adjacent;
|
|
470
|
+
}
|
|
115
471
|
/**
|
|
116
472
|
* Vector semantic search using cosine similarity
|
|
117
473
|
*/
|
|
@@ -127,8 +483,6 @@ async function vectorSearch(input, options) {
|
|
|
127
483
|
let queryEmbedding = null;
|
|
128
484
|
if (!isEmptyQuery) {
|
|
129
485
|
queryEmbedding = await getEmbedding(input.query);
|
|
130
|
-
// If embedding fails but query is not empty, still proceed without semantic ranking
|
|
131
|
-
// Fall back to recency-based ranking
|
|
132
486
|
}
|
|
133
487
|
// Build WHERE conditions
|
|
134
488
|
const conditions = [];
|
|
@@ -172,21 +526,16 @@ async function vectorSearch(input, options) {
|
|
|
172
526
|
const rows = statement.all(...params, limit * 3);
|
|
173
527
|
// If no embedding available, return results ordered by recency
|
|
174
528
|
if (!queryEmbedding) {
|
|
175
|
-
return rows.slice(0, limit * 2).map((item
|
|
529
|
+
return rows.slice(0, limit * 2).map((item) => ({
|
|
176
530
|
id: item.id,
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
tags: deserializeTags(item.tags ?? null),
|
|
186
|
-
metadata: deserializeMetadata(item.metadata ?? null),
|
|
187
|
-
createdAt: item.createdAt ? (normalizeTimestamp(Number(item.createdAt)) ?? undefined) : undefined,
|
|
188
|
-
similarity: 0,
|
|
189
|
-
},
|
|
531
|
+
projectId: item.projectId,
|
|
532
|
+
type: item.type,
|
|
533
|
+
content: item.content,
|
|
534
|
+
summary: item.summary ?? undefined,
|
|
535
|
+
tags: deserializeTags(item.tags ?? null),
|
|
536
|
+
metadata: deserializeMetadata(item.metadata ?? null),
|
|
537
|
+
createdAt: item.createdAt ? (normalizeTimestamp(Number(item.createdAt)) ?? undefined) : undefined,
|
|
538
|
+
similarity: 0,
|
|
190
539
|
}));
|
|
191
540
|
}
|
|
192
541
|
// Calculate cosine similarity for each result
|
|
@@ -209,23 +558,18 @@ async function vectorSearch(input, options) {
|
|
|
209
558
|
};
|
|
210
559
|
})
|
|
211
560
|
.filter((item) => item !== null);
|
|
212
|
-
// Sort by similarity (descending) and return
|
|
561
|
+
// Sort by similarity (descending) and return
|
|
213
562
|
scored.sort((a, b) => b.similarity - a.similarity);
|
|
214
|
-
return scored.slice(0, limit * 2).map((item
|
|
563
|
+
return scored.slice(0, limit * 2).map((item) => ({
|
|
215
564
|
id: item.id,
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
tags: deserializeTags(item.tags ?? null),
|
|
225
|
-
metadata: deserializeMetadata(item.metadata ?? null),
|
|
226
|
-
createdAt: item.createdAt ? (normalizeTimestamp(Number(item.createdAt)) ?? undefined) : undefined,
|
|
227
|
-
similarity: item.similarity,
|
|
228
|
-
},
|
|
565
|
+
projectId: item.projectId,
|
|
566
|
+
type: item.type,
|
|
567
|
+
content: item.content,
|
|
568
|
+
summary: item.summary ?? undefined,
|
|
569
|
+
tags: deserializeTags(item.tags ?? null),
|
|
570
|
+
metadata: deserializeMetadata(item.metadata ?? null),
|
|
571
|
+
createdAt: item.createdAt ? (normalizeTimestamp(Number(item.createdAt)) ?? undefined) : undefined,
|
|
572
|
+
similarity: item.similarity,
|
|
229
573
|
}));
|
|
230
574
|
}
|
|
231
575
|
catch (error) {
|
|
@@ -233,127 +577,20 @@ async function vectorSearch(input, options) {
|
|
|
233
577
|
}
|
|
234
578
|
}
|
|
235
579
|
/**
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
* RRF Formula: score(item) = sum(weight_i / (k + rank_i))
|
|
239
|
-
* Where k is a constant (typically 60) that prevents high ranks from dominating
|
|
240
|
-
*
|
|
241
|
-
* Benefits:
|
|
242
|
-
* - No need to calibrate different scoring systems (BM25 vs cosine similarity)
|
|
243
|
-
* - Handles items that appear in only one list
|
|
244
|
-
* - Proven to outperform weighted score fusion in most cases
|
|
580
|
+
* Apply small graph boost to results
|
|
581
|
+
* Graph boost is ADDITIVE (not dominating)
|
|
245
582
|
*/
|
|
246
|
-
function
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
existing.score += rrfScore;
|
|
255
|
-
}
|
|
256
|
-
else {
|
|
257
|
-
scores.set(item.id, { score: rrfScore, result: item.result });
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
// Process vector results
|
|
261
|
-
for (const item of vectorResults) {
|
|
262
|
-
const rrfScore = (vectorWeight * 2) / (RRF_K + item.rank);
|
|
263
|
-
const existing = scores.get(item.id);
|
|
264
|
-
if (existing) {
|
|
265
|
-
existing.score += rrfScore;
|
|
266
|
-
}
|
|
267
|
-
else {
|
|
268
|
-
scores.set(item.id, { score: rrfScore, result: item.result });
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
// Add graph boost to each score
|
|
272
|
-
for (const [id, entry] of scores.entries()) {
|
|
273
|
-
const boost = graphBoostMap[id] ?? 0;
|
|
274
|
-
const weight = config.scoringWeights.graphBoost ?? 1;
|
|
275
|
-
entry.score += boost * weight;
|
|
276
|
-
}
|
|
277
|
-
// Sort by final score (descending) and return top results
|
|
278
|
-
return Array.from(scores.values())
|
|
279
|
-
.sort((a, b) => b.score - a.score)
|
|
280
|
-
.slice(0, limit)
|
|
281
|
-
.map((item) => ({
|
|
282
|
-
...item.result,
|
|
283
|
-
similarity: item.result.similarity ?? 0,
|
|
284
|
-
}));
|
|
285
|
-
}
|
|
286
|
-
/**
|
|
287
|
-
* Build FTS5 query string from user input
|
|
288
|
-
* Handles phrase searches, OR operators for better recall, and special characters
|
|
289
|
-
*/
|
|
290
|
-
function buildFtsQuery(query) {
|
|
291
|
-
// Ensure query is a string
|
|
292
|
-
const queryString = typeof query === 'string' ? query : String(query ?? '');
|
|
293
|
-
// Remove special characters that could break FTS5 syntax (except quotes)
|
|
294
|
-
// Hyphens are problematic in FTS5 - they're treated as exclusion operators
|
|
295
|
-
let cleaned = queryString.replace(/[^\w\s"'-]/g, ' ');
|
|
296
|
-
// If query contains quotes, preserve as phrase search
|
|
297
|
-
if (cleaned.includes('"')) {
|
|
298
|
-
return cleaned || '*'; // Return * for empty after cleaning
|
|
299
|
-
}
|
|
300
|
-
// Split into terms
|
|
301
|
-
const terms = cleaned.trim().split(/\s+/).filter((t) => t.length > 0);
|
|
302
|
-
if (terms.length === 0) {
|
|
303
|
-
return '*'; // Match all for empty query
|
|
304
|
-
}
|
|
305
|
-
// Wrap terms containing hyphens in quotes (e.g., "trash-cli" prevents hyphen being interpreted as NOT)
|
|
306
|
-
const processedTerms = terms.map(term => {
|
|
307
|
-
if (term.includes('-')) {
|
|
308
|
-
return `"${term}"`;
|
|
309
|
-
}
|
|
310
|
-
return term;
|
|
583
|
+
function applyGraphBoostWithWeight(results, graphBoostMap, limit, graphWeight) {
|
|
584
|
+
// Apply SMALL additive boost to each result
|
|
585
|
+
// graphWeight should be 0.1-0.3, not > 1
|
|
586
|
+
const boosted = results.map(result => {
|
|
587
|
+
const boost = graphBoostMap[result.id] ?? 0;
|
|
588
|
+
// Add small nudge, don't replace similarity
|
|
589
|
+
const boostedSimilarity = (result.similarity ?? 0) + (boost * graphWeight);
|
|
590
|
+
return { ...result, similarity: boostedSimilarity };
|
|
311
591
|
});
|
|
312
|
-
//
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
return processedTerms.join(' OR ');
|
|
316
|
-
}
|
|
317
|
-
return processedTerms[0];
|
|
318
|
-
}
|
|
319
|
-
/**
|
|
320
|
-
* Parse embedding from SQLite storage
|
|
321
|
-
*/
|
|
322
|
-
function parseEmbedding(embeddingData) {
|
|
323
|
-
if (!embeddingData)
|
|
324
|
-
return null;
|
|
325
|
-
if (Array.isArray(embeddingData))
|
|
326
|
-
return embeddingData;
|
|
327
|
-
if (embeddingData instanceof Uint8Array || Buffer.isBuffer(embeddingData)) {
|
|
328
|
-
try {
|
|
329
|
-
const json = JSON.parse(embeddingData.toString());
|
|
330
|
-
if (Array.isArray(json))
|
|
331
|
-
return json;
|
|
332
|
-
}
|
|
333
|
-
catch {
|
|
334
|
-
try {
|
|
335
|
-
const buffer = embeddingData.buffer;
|
|
336
|
-
const arrayBuffer = buffer instanceof ArrayBuffer
|
|
337
|
-
? buffer
|
|
338
|
-
: buffer;
|
|
339
|
-
const floatArray = new Float32Array(arrayBuffer);
|
|
340
|
-
return Array.from(floatArray);
|
|
341
|
-
}
|
|
342
|
-
catch {
|
|
343
|
-
return null;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
if (typeof embeddingData === 'string') {
|
|
348
|
-
try {
|
|
349
|
-
const parsed = JSON.parse(embeddingData);
|
|
350
|
-
if (Array.isArray(parsed))
|
|
351
|
-
return parsed;
|
|
352
|
-
}
|
|
353
|
-
catch {
|
|
354
|
-
return null;
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
return null;
|
|
592
|
+
// Re-sort by boosted similarity
|
|
593
|
+
boosted.sort((a, b) => (b.similarity ?? 0) - (a.similarity ?? 0));
|
|
594
|
+
return boosted.slice(0, limit);
|
|
358
595
|
}
|
|
359
596
|
//# sourceMappingURL=hybrid-search.js.map
|