@vinaes/succ 1.4.0 → 1.5.42
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/README.md +64 -10
- package/dist/cli.js +81 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/agents-md.d.ts.map +1 -1
- package/dist/commands/agents-md.js +3 -2
- package/dist/commands/agents-md.js.map +1 -1
- package/dist/commands/analyze-profile.d.ts.map +1 -1
- package/dist/commands/analyze-profile.js +32 -8
- package/dist/commands/analyze-profile.js.map +1 -1
- package/dist/commands/analyze-recursive.d.ts.map +1 -1
- package/dist/commands/analyze-recursive.js +6 -2
- package/dist/commands/analyze-recursive.js.map +1 -1
- package/dist/commands/analyze-utils.d.ts.map +1 -1
- package/dist/commands/analyze-utils.js +17 -4
- package/dist/commands/analyze-utils.js.map +1 -1
- package/dist/commands/benchmark-quality.d.ts.map +1 -1
- package/dist/commands/benchmark-quality.js +11 -4
- package/dist/commands/benchmark-quality.js.map +1 -1
- package/dist/commands/benchmark-sqlite-vec.d.ts.map +1 -1
- package/dist/commands/benchmark-sqlite-vec.js +4 -0
- package/dist/commands/benchmark-sqlite-vec.js.map +1 -1
- package/dist/commands/benchmark.d.ts.map +1 -1
- package/dist/commands/benchmark.js +5 -1
- package/dist/commands/benchmark.js.map +1 -1
- package/dist/commands/codex-chat.d.ts +8 -0
- package/dist/commands/codex-chat.d.ts.map +1 -0
- package/dist/commands/codex-chat.js +161 -0
- package/dist/commands/codex-chat.js.map +1 -0
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +32 -4
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/daemon.d.ts.map +1 -1
- package/dist/commands/daemon.js +13 -4
- package/dist/commands/daemon.js.map +1 -1
- package/dist/commands/index-code.d.ts +4 -0
- package/dist/commands/index-code.d.ts.map +1 -1
- package/dist/commands/index-code.js +1 -1
- package/dist/commands/index-code.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +305 -203
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/memories.d.ts.map +1 -1
- package/dist/commands/memories.js +25 -14
- package/dist/commands/memories.js.map +1 -1
- package/dist/commands/progress.d.ts.map +1 -1
- package/dist/commands/progress.js +3 -2
- package/dist/commands/progress.js.map +1 -1
- package/dist/commands/reindex.d.ts.map +1 -1
- package/dist/commands/reindex.js +54 -36
- package/dist/commands/reindex.js.map +1 -1
- package/dist/commands/retention.d.ts.map +1 -1
- package/dist/commands/retention.js +7 -5
- package/dist/commands/retention.js.map +1 -1
- package/dist/commands/scan-code.d.ts +76 -0
- package/dist/commands/scan-code.d.ts.map +1 -0
- package/dist/commands/scan-code.js +385 -0
- package/dist/commands/scan-code.js.map +1 -0
- package/dist/commands/score.d.ts.map +1 -1
- package/dist/commands/score.js +3 -2
- package/dist/commands/score.js.map +1 -1
- package/dist/commands/session.d.ts +33 -0
- package/dist/commands/session.d.ts.map +1 -0
- package/dist/commands/session.js +163 -0
- package/dist/commands/session.js.map +1 -0
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +254 -15
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/soul.js +3 -2
- package/dist/commands/soul.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +14 -5
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/commands/watch.js +13 -4
- package/dist/commands/watch.js.map +1 -1
- package/dist/daemon/analyzer.d.ts.map +1 -1
- package/dist/daemon/analyzer.js +21 -5
- package/dist/daemon/analyzer.js.map +1 -1
- package/dist/daemon/client.d.ts.map +1 -1
- package/dist/daemon/client.js +32 -8
- package/dist/daemon/client.js.map +1 -1
- package/dist/daemon/routes/analyzer.d.ts +3 -0
- package/dist/daemon/routes/analyzer.d.ts.map +1 -0
- package/dist/daemon/routes/analyzer.js +27 -0
- package/dist/daemon/routes/analyzer.js.map +1 -0
- package/dist/daemon/routes/hooks.d.ts +14 -0
- package/dist/daemon/routes/hooks.d.ts.map +1 -0
- package/dist/daemon/routes/hooks.js +1212 -0
- package/dist/daemon/routes/hooks.js.map +1 -0
- package/dist/daemon/routes/memory.d.ts +4 -0
- package/dist/daemon/routes/memory.d.ts.map +1 -0
- package/dist/daemon/routes/memory.js +71 -0
- package/dist/daemon/routes/memory.js.map +1 -0
- package/dist/daemon/routes/reflection.d.ts +10 -0
- package/dist/daemon/routes/reflection.d.ts.map +1 -0
- package/dist/daemon/routes/reflection.js +397 -0
- package/dist/daemon/routes/reflection.js.map +1 -0
- package/dist/daemon/routes/search.d.ts +5 -0
- package/dist/daemon/routes/search.d.ts.map +1 -0
- package/dist/daemon/routes/search.js +93 -0
- package/dist/daemon/routes/search.js.map +1 -0
- package/dist/daemon/routes/sessions.d.ts +3 -0
- package/dist/daemon/routes/sessions.d.ts.map +1 -0
- package/dist/daemon/routes/sessions.js +160 -0
- package/dist/daemon/routes/sessions.js.map +1 -0
- package/dist/daemon/routes/skills.d.ts +3 -0
- package/dist/daemon/routes/skills.d.ts.map +1 -0
- package/dist/daemon/routes/skills.js +36 -0
- package/dist/daemon/routes/skills.js.map +1 -0
- package/dist/daemon/routes/status.d.ts +3 -0
- package/dist/daemon/routes/status.d.ts.map +1 -0
- package/dist/daemon/routes/status.js +47 -0
- package/dist/daemon/routes/status.js.map +1 -0
- package/dist/daemon/routes/types.d.ts +240 -0
- package/dist/daemon/routes/types.d.ts.map +1 -0
- package/dist/daemon/routes/types.js +97 -0
- package/dist/daemon/routes/types.js.map +1 -0
- package/dist/daemon/routes/versioning.d.ts +27 -0
- package/dist/daemon/routes/versioning.d.ts.map +1 -0
- package/dist/daemon/routes/versioning.js +44 -0
- package/dist/daemon/routes/versioning.js.map +1 -0
- package/dist/daemon/routes/watcher.d.ts +3 -0
- package/dist/daemon/routes/watcher.d.ts.map +1 -0
- package/dist/daemon/routes/watcher.js +28 -0
- package/dist/daemon/routes/watcher.js.map +1 -0
- package/dist/daemon/service.d.ts +5 -23
- package/dist/daemon/service.d.ts.map +1 -1
- package/dist/daemon/service.js +201 -933
- package/dist/daemon/service.js.map +1 -1
- package/dist/daemon/session-processor.d.ts +4 -8
- package/dist/daemon/session-processor.d.ts.map +1 -1
- package/dist/daemon/session-processor.js +39 -38
- package/dist/daemon/session-processor.js.map +1 -1
- package/dist/lib/ai-readiness.d.ts.map +1 -1
- package/dist/lib/ai-readiness.js +33 -8
- package/dist/lib/ai-readiness.js.map +1 -1
- package/dist/lib/analyze-state.d.ts.map +1 -1
- package/dist/lib/analyze-state.js +25 -3
- package/dist/lib/analyze-state.js.map +1 -1
- package/dist/lib/auto-memory/consolidation.d.ts +41 -0
- package/dist/lib/auto-memory/consolidation.d.ts.map +1 -0
- package/dist/lib/auto-memory/consolidation.js +151 -0
- package/dist/lib/auto-memory/consolidation.js.map +1 -0
- package/dist/lib/bpe.d.ts.map +1 -1
- package/dist/lib/bpe.js +9 -10
- package/dist/lib/bpe.js.map +1 -1
- package/dist/lib/brain-export.d.ts +65 -0
- package/dist/lib/brain-export.d.ts.map +1 -0
- package/dist/lib/brain-export.js +413 -0
- package/dist/lib/brain-export.js.map +1 -0
- package/dist/lib/checkpoint.d.ts.map +1 -1
- package/dist/lib/checkpoint.js +22 -6
- package/dist/lib/checkpoint.js.map +1 -1
- package/dist/lib/chunker.d.ts.map +1 -1
- package/dist/lib/chunker.js +6 -1
- package/dist/lib/chunker.js.map +1 -1
- package/dist/lib/claude-ws-transport.d.ts.map +1 -1
- package/dist/lib/claude-ws-transport.js +12 -4
- package/dist/lib/claude-ws-transport.js.map +1 -1
- package/dist/lib/command-safety.d.ts +64 -0
- package/dist/lib/command-safety.d.ts.map +1 -0
- package/dist/lib/command-safety.js +625 -0
- package/dist/lib/command-safety.js.map +1 -0
- package/dist/lib/compact-briefing.d.ts.map +1 -1
- package/dist/lib/compact-briefing.js +10 -13
- package/dist/lib/compact-briefing.js.map +1 -1
- package/dist/lib/config-defaults.d.ts.map +1 -1
- package/dist/lib/config-defaults.js +3 -0
- package/dist/lib/config-defaults.js.map +1 -1
- package/dist/lib/config-display.d.ts +4 -0
- package/dist/lib/config-display.d.ts.map +1 -1
- package/dist/lib/config-display.js +6 -1
- package/dist/lib/config-display.js.map +1 -1
- package/dist/lib/config-types.d.ts +156 -0
- package/dist/lib/config-types.d.ts.map +1 -1
- package/dist/lib/config-validation.d.ts.map +1 -1
- package/dist/lib/config-validation.js +5 -0
- package/dist/lib/config-validation.js.map +1 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +92 -9
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/consolidate.d.ts.map +1 -1
- package/dist/lib/consolidate.js +66 -47
- package/dist/lib/consolidate.js.map +1 -1
- package/dist/lib/content-sanitizer.d.ts +29 -0
- package/dist/lib/content-sanitizer.d.ts.map +1 -0
- package/dist/lib/content-sanitizer.js +72 -0
- package/dist/lib/content-sanitizer.js.map +1 -0
- package/dist/lib/cross-repo.d.ts +44 -0
- package/dist/lib/cross-repo.d.ts.map +1 -0
- package/dist/lib/cross-repo.js +108 -0
- package/dist/lib/cross-repo.js.map +1 -0
- package/dist/lib/daemon-port.d.ts +12 -0
- package/dist/lib/daemon-port.d.ts.map +1 -0
- package/dist/lib/daemon-port.js +20 -0
- package/dist/lib/daemon-port.js.map +1 -0
- package/dist/lib/db/auto-memory.d.ts +40 -0
- package/dist/lib/db/auto-memory.d.ts.map +1 -0
- package/dist/lib/db/auto-memory.js +74 -0
- package/dist/lib/db/auto-memory.js.map +1 -0
- package/dist/lib/db/bm25-indexes.d.ts.map +1 -1
- package/dist/lib/db/bm25-indexes.js +16 -4
- package/dist/lib/db/bm25-indexes.js.map +1 -1
- package/dist/lib/db/documents.d.ts.map +1 -1
- package/dist/lib/db/documents.js +4 -1
- package/dist/lib/db/documents.js.map +1 -1
- package/dist/lib/db/global-memories.d.ts +2 -10
- package/dist/lib/db/global-memories.d.ts.map +1 -1
- package/dist/lib/db/global-memories.js +13 -6
- package/dist/lib/db/global-memories.js.map +1 -1
- package/dist/lib/db/graph.d.ts +5 -1
- package/dist/lib/db/graph.d.ts.map +1 -1
- package/dist/lib/db/graph.js +38 -8
- package/dist/lib/db/graph.js.map +1 -1
- package/dist/lib/db/hybrid-search.d.ts +4 -2
- package/dist/lib/db/hybrid-search.d.ts.map +1 -1
- package/dist/lib/db/hybrid-search.js +29 -11
- package/dist/lib/db/hybrid-search.js.map +1 -1
- package/dist/lib/db/index.d.ts +6 -1
- package/dist/lib/db/index.d.ts.map +1 -1
- package/dist/lib/db/index.js +5 -1
- package/dist/lib/db/index.js.map +1 -1
- package/dist/lib/db/memories.d.ts +19 -14
- package/dist/lib/db/memories.d.ts.map +1 -1
- package/dist/lib/db/memories.js +100 -37
- package/dist/lib/db/memories.js.map +1 -1
- package/dist/lib/db/parse-helpers.d.ts +14 -0
- package/dist/lib/db/parse-helpers.d.ts.map +1 -0
- package/dist/lib/db/parse-helpers.js +59 -0
- package/dist/lib/db/parse-helpers.js.map +1 -0
- package/dist/lib/db/recall-events.d.ts +49 -0
- package/dist/lib/db/recall-events.d.ts.map +1 -0
- package/dist/lib/db/recall-events.js +196 -0
- package/dist/lib/db/recall-events.js.map +1 -0
- package/dist/lib/db/retention.d.ts +4 -3
- package/dist/lib/db/retention.d.ts.map +1 -1
- package/dist/lib/db/retention.js +12 -1
- package/dist/lib/db/retention.js.map +1 -1
- package/dist/lib/db/schema.d.ts +2 -0
- package/dist/lib/db/schema.d.ts.map +1 -1
- package/dist/lib/db/schema.js +140 -80
- package/dist/lib/db/schema.js.map +1 -1
- package/dist/lib/db/skills.d.ts.map +1 -1
- package/dist/lib/db/skills.js +10 -6
- package/dist/lib/db/skills.js.map +1 -1
- package/dist/lib/diff-brain.d.ts +24 -0
- package/dist/lib/diff-brain.d.ts.map +1 -0
- package/dist/lib/diff-brain.js +114 -0
- package/dist/lib/diff-brain.js.map +1 -0
- package/dist/lib/diff-parser.d.ts +74 -0
- package/dist/lib/diff-parser.d.ts.map +1 -0
- package/dist/lib/diff-parser.js +200 -0
- package/dist/lib/diff-parser.js.map +1 -0
- package/dist/lib/embedding-pool.d.ts.map +1 -1
- package/dist/lib/embedding-pool.js +5 -1
- package/dist/lib/embedding-pool.js.map +1 -1
- package/dist/lib/embeddings.d.ts +12 -0
- package/dist/lib/embeddings.d.ts.map +1 -1
- package/dist/lib/embeddings.js +77 -19
- package/dist/lib/embeddings.js.map +1 -1
- package/dist/lib/errors.d.ts +2 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +4 -0
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/fault-logger.d.ts.map +1 -1
- package/dist/lib/fault-logger.js +22 -7
- package/dist/lib/fault-logger.js.map +1 -1
- package/dist/lib/git/co-change.d.ts +39 -0
- package/dist/lib/git/co-change.d.ts.map +1 -0
- package/dist/lib/git/co-change.js +139 -0
- package/dist/lib/git/co-change.js.map +1 -0
- package/dist/lib/graph/bridge-edges.d.ts +93 -0
- package/dist/lib/graph/bridge-edges.d.ts.map +1 -0
- package/dist/lib/graph/bridge-edges.js +276 -0
- package/dist/lib/graph/bridge-edges.js.map +1 -0
- package/dist/lib/graph/centrality.d.ts +11 -0
- package/dist/lib/graph/centrality.d.ts.map +1 -1
- package/dist/lib/graph/centrality.js +51 -3
- package/dist/lib/graph/centrality.js.map +1 -1
- package/dist/lib/graph/cleanup.d.ts.map +1 -1
- package/dist/lib/graph/cleanup.js +2 -1
- package/dist/lib/graph/cleanup.js.map +1 -1
- package/dist/lib/graph/community-detection.d.ts +17 -2
- package/dist/lib/graph/community-detection.d.ts.map +1 -1
- package/dist/lib/graph/community-detection.js +147 -48
- package/dist/lib/graph/community-detection.js.map +1 -1
- package/dist/lib/graph/community-summaries.d.ts +26 -0
- package/dist/lib/graph/community-summaries.d.ts.map +1 -0
- package/dist/lib/graph/community-summaries.js +130 -0
- package/dist/lib/graph/community-summaries.js.map +1 -0
- package/dist/lib/graph/contextual-proximity.d.ts.map +1 -1
- package/dist/lib/graph/contextual-proximity.js +11 -4
- package/dist/lib/graph/contextual-proximity.js.map +1 -1
- package/dist/lib/graph/graphology-bridge.d.ts +101 -0
- package/dist/lib/graph/graphology-bridge.d.ts.map +1 -0
- package/dist/lib/graph/graphology-bridge.js +488 -0
- package/dist/lib/graph/graphology-bridge.js.map +1 -0
- package/dist/lib/graph/llm-relations.d.ts.map +1 -1
- package/dist/lib/graph/llm-relations.js +27 -5
- package/dist/lib/graph/llm-relations.js.map +1 -1
- package/dist/lib/graph-export.d.ts.map +1 -1
- package/dist/lib/graph-export.js +2 -2
- package/dist/lib/graph-export.js.map +1 -1
- package/dist/lib/graph-scheduler.d.ts +0 -5
- package/dist/lib/graph-scheduler.d.ts.map +1 -1
- package/dist/lib/graph-scheduler.js +5 -1
- package/dist/lib/graph-scheduler.js.map +1 -1
- package/dist/lib/guardrails.d.ts +50 -0
- package/dist/lib/guardrails.d.ts.map +1 -0
- package/dist/lib/guardrails.js +502 -0
- package/dist/lib/guardrails.js.map +1 -0
- package/dist/lib/hook-rules.d.ts +1 -1
- package/dist/lib/hook-rules.d.ts.map +1 -1
- package/dist/lib/hook-rules.js +8 -2
- package/dist/lib/hook-rules.js.map +1 -1
- package/dist/lib/ifc/file-labels.d.ts +35 -0
- package/dist/lib/ifc/file-labels.d.ts.map +1 -0
- package/dist/lib/ifc/file-labels.js +208 -0
- package/dist/lib/ifc/file-labels.js.map +1 -0
- package/dist/lib/ifc/label.d.ts +38 -0
- package/dist/lib/ifc/label.d.ts.map +1 -0
- package/dist/lib/ifc/label.js +80 -0
- package/dist/lib/ifc/label.js.map +1 -0
- package/dist/lib/ifc/session-ifc.d.ts +92 -0
- package/dist/lib/ifc/session-ifc.d.ts.map +1 -0
- package/dist/lib/ifc/session-ifc.js +222 -0
- package/dist/lib/ifc/session-ifc.js.map +1 -0
- package/dist/lib/indexer.js +2 -2
- package/dist/lib/indexer.js.map +1 -1
- package/dist/lib/injection-detector.d.ts +83 -0
- package/dist/lib/injection-detector.d.ts.map +1 -0
- package/dist/lib/injection-detector.js +586 -0
- package/dist/lib/injection-detector.js.map +1 -0
- package/dist/lib/injection-semantic.d.ts +31 -0
- package/dist/lib/injection-semantic.d.ts.map +1 -0
- package/dist/lib/injection-semantic.js +230 -0
- package/dist/lib/injection-semantic.js.map +1 -0
- package/dist/lib/llm.d.ts.map +1 -1
- package/dist/lib/llm.js +19 -4
- package/dist/lib/llm.js.map +1 -1
- package/dist/lib/lock.d.ts.map +1 -1
- package/dist/lib/lock.js +24 -3
- package/dist/lib/lock.js.map +1 -1
- package/dist/lib/md-fetch.d.ts.map +1 -1
- package/dist/lib/md-fetch.js +9 -2
- package/dist/lib/md-fetch.js.map +1 -1
- package/dist/lib/observability.d.ts +75 -0
- package/dist/lib/observability.d.ts.map +1 -0
- package/dist/lib/observability.js +201 -0
- package/dist/lib/observability.js.map +1 -0
- package/dist/lib/ort-session.d.ts +26 -0
- package/dist/lib/ort-session.d.ts.map +1 -1
- package/dist/lib/ort-session.js +107 -3
- package/dist/lib/ort-session.js.map +1 -1
- package/dist/lib/prd/codebase-context.d.ts.map +1 -1
- package/dist/lib/prd/codebase-context.js +9 -2
- package/dist/lib/prd/codebase-context.js.map +1 -1
- package/dist/lib/prd/context.d.ts.map +1 -1
- package/dist/lib/prd/context.js +11 -3
- package/dist/lib/prd/context.js.map +1 -1
- package/dist/lib/prd/export.js +1 -1
- package/dist/lib/prd/export.js.map +1 -1
- package/dist/lib/prd/generate.d.ts.map +1 -1
- package/dist/lib/prd/generate.js +17 -4
- package/dist/lib/prd/generate.js.map +1 -1
- package/dist/lib/prd/parse.d.ts.map +1 -1
- package/dist/lib/prd/parse.js +6 -1
- package/dist/lib/prd/parse.js.map +1 -1
- package/dist/lib/prd/runner.d.ts +1 -2
- package/dist/lib/prd/runner.d.ts.map +1 -1
- package/dist/lib/prd/runner.js +43 -32
- package/dist/lib/prd/runner.js.map +1 -1
- package/dist/lib/prd/worktree.d.ts +1 -2
- package/dist/lib/prd/worktree.d.ts.map +1 -1
- package/dist/lib/prd/worktree.js +62 -70
- package/dist/lib/prd/worktree.js.map +1 -1
- package/dist/lib/precompute-context.d.ts.map +1 -1
- package/dist/lib/precompute-context.js +15 -34
- package/dist/lib/precompute-context.js.map +1 -1
- package/dist/lib/pricing.d.ts.map +1 -1
- package/dist/lib/pricing.js +5 -1
- package/dist/lib/pricing.js.map +1 -1
- package/dist/lib/process-registry.js +3 -3
- package/dist/lib/process-registry.js.map +1 -1
- package/dist/lib/public-api.d.ts +41 -1
- package/dist/lib/public-api.d.ts.map +1 -1
- package/dist/lib/public-api.js +38 -0
- package/dist/lib/public-api.js.map +1 -1
- package/dist/lib/quality.d.ts.map +1 -1
- package/dist/lib/quality.js +15 -6
- package/dist/lib/quality.js.map +1 -1
- package/dist/lib/query-expansion.d.ts +32 -0
- package/dist/lib/query-expansion.d.ts.map +1 -1
- package/dist/lib/query-expansion.js +62 -1
- package/dist/lib/query-expansion.js.map +1 -1
- package/dist/lib/reference-embeddings.d.ts.map +1 -1
- package/dist/lib/reference-embeddings.js +17 -4
- package/dist/lib/reference-embeddings.js.map +1 -1
- package/dist/lib/reflection-synthesizer.d.ts.map +1 -1
- package/dist/lib/reflection-synthesizer.js +7 -1
- package/dist/lib/reflection-synthesizer.js.map +1 -1
- package/dist/lib/reranker.d.ts +41 -0
- package/dist/lib/reranker.d.ts.map +1 -0
- package/dist/lib/reranker.js +294 -0
- package/dist/lib/reranker.js.map +1 -0
- package/dist/lib/retrieval-feedback.d.ts +100 -0
- package/dist/lib/retrieval-feedback.d.ts.map +1 -0
- package/dist/lib/retrieval-feedback.js +174 -0
- package/dist/lib/retrieval-feedback.js.map +1 -0
- package/dist/lib/review/context-pack.d.ts +58 -0
- package/dist/lib/review/context-pack.d.ts.map +1 -0
- package/dist/lib/review/context-pack.js +300 -0
- package/dist/lib/review/context-pack.js.map +1 -0
- package/dist/lib/search/hierarchical-summaries.d.ts +65 -0
- package/dist/lib/search/hierarchical-summaries.d.ts.map +1 -0
- package/dist/lib/search/hierarchical-summaries.js +423 -0
- package/dist/lib/search/hierarchical-summaries.js.map +1 -0
- package/dist/lib/search/hyde.d.ts +27 -0
- package/dist/lib/search/hyde.d.ts.map +1 -0
- package/dist/lib/search/hyde.js +141 -0
- package/dist/lib/search/hyde.js.map +1 -0
- package/dist/lib/search/late-chunking.d.ts +53 -0
- package/dist/lib/search/late-chunking.d.ts.map +1 -0
- package/dist/lib/search/late-chunking.js +230 -0
- package/dist/lib/search/late-chunking.js.map +1 -0
- package/dist/lib/search/ppr-retrieval.d.ts +49 -0
- package/dist/lib/search/ppr-retrieval.d.ts.map +1 -0
- package/dist/lib/search/ppr-retrieval.js +135 -0
- package/dist/lib/search/ppr-retrieval.js.map +1 -0
- package/dist/lib/search/repo-map.d.ts +43 -0
- package/dist/lib/search/repo-map.d.ts.map +1 -0
- package/dist/lib/search/repo-map.js +165 -0
- package/dist/lib/search/repo-map.js.map +1 -0
- package/dist/lib/session-analyzer.d.ts +90 -0
- package/dist/lib/session-analyzer.d.ts.map +1 -0
- package/dist/lib/session-analyzer.js +467 -0
- package/dist/lib/session-analyzer.js.map +1 -0
- package/dist/lib/session-observations.d.ts.map +1 -1
- package/dist/lib/session-observations.js +13 -3
- package/dist/lib/session-observations.js.map +1 -1
- package/dist/lib/session-summary.d.ts.map +1 -1
- package/dist/lib/session-summary.js +57 -50
- package/dist/lib/session-summary.js.map +1 -1
- package/dist/lib/session-surgeon.d.ts +53 -0
- package/dist/lib/session-surgeon.d.ts.map +1 -0
- package/dist/lib/session-surgeon.js +501 -0
- package/dist/lib/session-surgeon.js.map +1 -0
- package/dist/lib/similarity-utils.d.ts +26 -0
- package/dist/lib/similarity-utils.d.ts.map +1 -0
- package/dist/lib/similarity-utils.js +66 -0
- package/dist/lib/similarity-utils.js.map +1 -0
- package/dist/lib/skills.d.ts.map +1 -1
- package/dist/lib/skills.js +11 -11
- package/dist/lib/skills.js.map +1 -1
- package/dist/lib/storage/backends/interface.d.ts +13 -3
- package/dist/lib/storage/backends/interface.d.ts.map +1 -1
- package/dist/lib/storage/backends/postgresql.d.ts +52 -3
- package/dist/lib/storage/backends/postgresql.d.ts.map +1 -1
- package/dist/lib/storage/backends/postgresql.js +694 -49
- package/dist/lib/storage/backends/postgresql.js.map +1 -1
- package/dist/lib/storage/benchmark.js +2 -2
- package/dist/lib/storage/benchmark.js.map +1 -1
- package/dist/lib/storage/dispatcher/base.d.ts +114 -0
- package/dist/lib/storage/dispatcher/base.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/base.js +160 -0
- package/dist/lib/storage/dispatcher/base.js.map +1 -0
- package/dist/lib/storage/dispatcher/documents.d.ts +25 -0
- package/dist/lib/storage/dispatcher/documents.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/documents.js +194 -0
- package/dist/lib/storage/dispatcher/documents.js.map +1 -0
- package/dist/lib/storage/dispatcher/embeddings.d.ts +34 -0
- package/dist/lib/storage/dispatcher/embeddings.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/embeddings.js +144 -0
- package/dist/lib/storage/dispatcher/embeddings.js.map +1 -0
- package/dist/lib/storage/dispatcher/export-import.d.ts +139 -0
- package/dist/lib/storage/dispatcher/export-import.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/export-import.js +191 -0
- package/dist/lib/storage/dispatcher/export-import.js.map +1 -0
- package/dist/lib/storage/dispatcher/file-hashes.d.ts +13 -0
- package/dist/lib/storage/dispatcher/file-hashes.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/file-hashes.js +36 -0
- package/dist/lib/storage/dispatcher/file-hashes.js.map +1 -0
- package/dist/lib/storage/dispatcher/global-memories.d.ts +28 -0
- package/dist/lib/storage/dispatcher/global-memories.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/global-memories.js +151 -0
- package/dist/lib/storage/dispatcher/global-memories.js.map +1 -0
- package/dist/lib/storage/dispatcher/graph.d.ts +32 -0
- package/dist/lib/storage/dispatcher/graph.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/graph.js +146 -0
- package/dist/lib/storage/dispatcher/graph.js.map +1 -0
- package/dist/lib/storage/dispatcher/index.d.ts +34 -0
- package/dist/lib/storage/dispatcher/index.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/index.js +139 -0
- package/dist/lib/storage/dispatcher/index.js.map +1 -0
- package/dist/lib/storage/dispatcher/memories.d.ts +65 -0
- package/dist/lib/storage/dispatcher/memories.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/memories.js +466 -0
- package/dist/lib/storage/dispatcher/memories.js.map +1 -0
- package/dist/lib/storage/dispatcher/mixin-helper.d.ts +6 -0
- package/dist/lib/storage/dispatcher/mixin-helper.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/mixin-helper.js +10 -0
- package/dist/lib/storage/dispatcher/mixin-helper.js.map +1 -0
- package/dist/lib/storage/dispatcher/retention.d.ts +20 -0
- package/dist/lib/storage/dispatcher/retention.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/retention.js +123 -0
- package/dist/lib/storage/dispatcher/retention.js.map +1 -0
- package/dist/lib/storage/dispatcher/search.d.ts +34 -0
- package/dist/lib/storage/dispatcher/search.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/search.js +222 -0
- package/dist/lib/storage/dispatcher/search.js.map +1 -0
- package/dist/lib/storage/dispatcher/skills.d.ts +53 -0
- package/dist/lib/storage/dispatcher/skills.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/skills.js +98 -0
- package/dist/lib/storage/dispatcher/skills.js.map +1 -0
- package/dist/lib/storage/dispatcher/token-stats.d.ts +23 -0
- package/dist/lib/storage/dispatcher/token-stats.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/token-stats.js +92 -0
- package/dist/lib/storage/dispatcher/token-stats.js.map +1 -0
- package/dist/lib/storage/dispatcher/web-search.d.ts +10 -0
- package/dist/lib/storage/dispatcher/web-search.d.ts.map +1 -0
- package/dist/lib/storage/dispatcher/web-search.js +39 -0
- package/dist/lib/storage/dispatcher/web-search.js.map +1 -0
- package/dist/lib/storage/dispatcher-export.d.ts.map +1 -1
- package/dist/lib/storage/dispatcher-export.js +48 -39
- package/dist/lib/storage/dispatcher-export.js.map +1 -1
- package/dist/lib/storage/dispatcher.d.ts +1 -468
- package/dist/lib/storage/dispatcher.d.ts.map +1 -1
- package/dist/lib/storage/dispatcher.js +1 -1931
- package/dist/lib/storage/dispatcher.js.map +1 -1
- package/dist/lib/storage/index.d.ts +20 -5
- package/dist/lib/storage/index.d.ts.map +1 -1
- package/dist/lib/storage/index.js +36 -7
- package/dist/lib/storage/index.js.map +1 -1
- package/dist/lib/storage/migration/export-import.d.ts.map +1 -1
- package/dist/lib/storage/migration/export-import.js +9 -2
- package/dist/lib/storage/migration/export-import.js.map +1 -1
- package/dist/lib/storage/types.d.ts +152 -10
- package/dist/lib/storage/types.d.ts.map +1 -1
- package/dist/lib/storage/types.js +13 -0
- package/dist/lib/storage/types.js.map +1 -1
- package/dist/lib/storage/vector/interface.d.ts +4 -0
- package/dist/lib/storage/vector/interface.d.ts.map +1 -1
- package/dist/lib/storage/vector/qdrant.d.ts +13 -2
- package/dist/lib/storage/vector/qdrant.d.ts.map +1 -1
- package/dist/lib/storage/vector/qdrant.js +147 -61
- package/dist/lib/storage/vector/qdrant.js.map +1 -1
- package/dist/lib/token-budget.d.ts.map +1 -1
- package/dist/lib/token-budget.js +9 -2
- package/dist/lib/token-budget.js.map +1 -1
- package/dist/lib/transcript-utils.d.ts +60 -0
- package/dist/lib/transcript-utils.d.ts.map +1 -0
- package/dist/lib/transcript-utils.js +69 -0
- package/dist/lib/transcript-utils.js.map +1 -0
- package/dist/lib/tree-sitter/extractor.d.ts +1 -1
- package/dist/lib/tree-sitter/extractor.d.ts.map +1 -1
- package/dist/lib/tree-sitter/extractor.js +34 -9
- package/dist/lib/tree-sitter/extractor.js.map +1 -1
- package/dist/lib/tree-sitter/parser.d.ts.map +1 -1
- package/dist/lib/tree-sitter/parser.js +45 -11
- package/dist/lib/tree-sitter/parser.js.map +1 -1
- package/dist/lib/tree-sitter/public.d.ts +12 -0
- package/dist/lib/tree-sitter/public.d.ts.map +1 -1
- package/dist/lib/tree-sitter/public.js +33 -1
- package/dist/lib/tree-sitter/public.js.map +1 -1
- package/dist/lib/tree-sitter/queries.d.ts.map +1 -1
- package/dist/lib/tree-sitter/queries.js +8 -0
- package/dist/lib/tree-sitter/queries.js.map +1 -1
- package/dist/lib/version-check.d.ts +29 -0
- package/dist/lib/version-check.d.ts.map +1 -0
- package/dist/lib/version-check.js +187 -0
- package/dist/lib/version-check.js.map +1 -0
- package/dist/lib/working-memory-pipeline.d.ts.map +1 -1
- package/dist/lib/working-memory-pipeline.js +12 -3
- package/dist/lib/working-memory-pipeline.js.map +1 -1
- package/dist/lib/worktree-detect.d.ts +43 -0
- package/dist/lib/worktree-detect.d.ts.map +1 -0
- package/dist/lib/worktree-detect.js +154 -0
- package/dist/lib/worktree-detect.js.map +1 -0
- package/dist/lsp/client.d.ts +96 -0
- package/dist/lsp/client.d.ts.map +1 -0
- package/dist/lsp/client.js +435 -0
- package/dist/lsp/client.js.map +1 -0
- package/dist/lsp/installer.d.ts +39 -0
- package/dist/lsp/installer.d.ts.map +1 -0
- package/dist/lsp/installer.js +275 -0
- package/dist/lsp/installer.js.map +1 -0
- package/dist/lsp/manager.d.ts +62 -0
- package/dist/lsp/manager.d.ts.map +1 -0
- package/dist/lsp/manager.js +234 -0
- package/dist/lsp/manager.js.map +1 -0
- package/dist/lsp/servers.d.ts +52 -0
- package/dist/lsp/servers.d.ts.map +1 -0
- package/dist/lsp/servers.js +162 -0
- package/dist/lsp/servers.js.map +1 -0
- package/dist/mcp/helpers.d.ts.map +1 -1
- package/dist/mcp/helpers.js +8 -2
- package/dist/mcp/helpers.js.map +1 -1
- package/dist/mcp/profile.js +1 -1
- package/dist/mcp/server.d.ts +3 -2
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +19 -7
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools/config.d.ts.map +1 -1
- package/dist/mcp/tools/config.js +28 -118
- package/dist/mcp/tools/config.js.map +1 -1
- package/dist/mcp/tools/dead-end.d.ts.map +1 -1
- package/dist/mcp/tools/dead-end.js +4 -3
- package/dist/mcp/tools/dead-end.js.map +1 -1
- package/dist/mcp/tools/debug.d.ts.map +1 -1
- package/dist/mcp/tools/debug.js +27 -112
- package/dist/mcp/tools/debug.js.map +1 -1
- package/dist/mcp/tools/graph.d.ts.map +1 -1
- package/dist/mcp/tools/graph.js +164 -176
- package/dist/mcp/tools/graph.js.map +1 -1
- package/dist/mcp/tools/indexing.d.ts +1 -1
- package/dist/mcp/tools/indexing.d.ts.map +1 -1
- package/dist/mcp/tools/indexing.js +63 -164
- package/dist/mcp/tools/indexing.js.map +1 -1
- package/dist/mcp/tools/memory/forget.d.ts +3 -0
- package/dist/mcp/tools/memory/forget.d.ts.map +1 -0
- package/dist/mcp/tools/memory/forget.js +175 -0
- package/dist/mcp/tools/memory/forget.js.map +1 -0
- package/dist/mcp/tools/memory/memory-helpers.d.ts +45 -0
- package/dist/mcp/tools/memory/memory-helpers.d.ts.map +1 -0
- package/dist/mcp/tools/memory/memory-helpers.js +291 -0
- package/dist/mcp/tools/memory/memory-helpers.js.map +1 -0
- package/dist/mcp/tools/memory/recall.d.ts +3 -0
- package/dist/mcp/tools/memory/recall.d.ts.map +1 -0
- package/dist/mcp/tools/memory/recall.js +495 -0
- package/dist/mcp/tools/memory/recall.js.map +1 -0
- package/dist/mcp/tools/memory/remember.d.ts +3 -0
- package/dist/mcp/tools/memory/remember.d.ts.map +1 -0
- package/dist/mcp/tools/memory/remember.js +256 -0
- package/dist/mcp/tools/memory/remember.js.map +1 -0
- package/dist/mcp/tools/memory/temporal-query.d.ts +8 -0
- package/dist/mcp/tools/memory/temporal-query.d.ts.map +1 -0
- package/dist/mcp/tools/memory/temporal-query.js +68 -0
- package/dist/mcp/tools/memory/temporal-query.js.map +1 -0
- package/dist/mcp/tools/memory.d.ts +0 -11
- package/dist/mcp/tools/memory.d.ts.map +1 -1
- package/dist/mcp/tools/memory.js +6 -1228
- package/dist/mcp/tools/memory.js.map +1 -1
- package/dist/mcp/tools/prd.d.ts.map +1 -1
- package/dist/mcp/tools/prd.js +19 -70
- package/dist/mcp/tools/prd.js.map +1 -1
- package/dist/mcp/tools/review.d.ts +8 -0
- package/dist/mcp/tools/review.d.ts.map +1 -0
- package/dist/mcp/tools/review.js +133 -0
- package/dist/mcp/tools/review.js.map +1 -0
- package/dist/mcp/tools/search.d.ts.map +1 -1
- package/dist/mcp/tools/search.js +79 -8
- package/dist/mcp/tools/search.js.map +1 -1
- package/dist/mcp/tools/status.d.ts.map +1 -1
- package/dist/mcp/tools/status.js +50 -75
- package/dist/mcp/tools/status.js.map +1 -1
- package/dist/mcp/tools/web-fetch.d.ts.map +1 -1
- package/dist/mcp/tools/web-fetch.js +5 -1
- package/dist/mcp/tools/web-fetch.js.map +1 -1
- package/dist/mcp/tools/web-search.d.ts.map +1 -1
- package/dist/mcp/tools/web-search.js +25 -103
- package/dist/mcp/tools/web-search.js.map +1 -1
- package/dist/prompts/guardrails.d.ts +14 -0
- package/dist/prompts/guardrails.d.ts.map +1 -0
- package/dist/prompts/guardrails.js +115 -0
- package/dist/prompts/guardrails.js.map +1 -0
- package/dist/prompts/index.d.ts +2 -1
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +3 -1
- package/dist/prompts/index.js.map +1 -1
- package/dist/prompts/prd.d.ts +0 -2
- package/dist/prompts/prd.d.ts.map +1 -1
- package/dist/prompts/prd.js +0 -2
- package/dist/prompts/prd.js.map +1 -1
- package/hooks/core/__tests__/adapter.test.cjs +340 -0
- package/hooks/core/adapter.cjs +463 -0
- package/hooks/core/config.cjs +83 -0
- package/hooks/core/daemon-boot.cjs +140 -0
- package/hooks/core/log.cjs +41 -0
- package/hooks/core/worktree.cjs +119 -0
- package/hooks/succ-post-tool.cjs +198 -134
- package/hooks/succ-pre-compact.cjs +262 -0
- package/hooks/succ-pre-tool.cjs +526 -182
- package/hooks/succ-session-end.cjs +40 -64
- package/hooks/succ-session-start.cjs +528 -427
- package/hooks/succ-stop-reflection.cjs +36 -62
- package/hooks/succ-user-prompt.cjs +137 -180
- package/package.json +17 -6
|
@@ -9,7 +9,10 @@
|
|
|
9
9
|
* - vector(N) type from pgvector extension
|
|
10
10
|
* - ON CONFLICT instead of INSERT OR REPLACE
|
|
11
11
|
*/
|
|
12
|
+
import { SOURCE_TYPES } from '../types.js';
|
|
12
13
|
import { StorageError, ConfigError } from '../../errors.js';
|
|
14
|
+
import { tokenizeCode, tokenizeCodeWithAST, tokenizeDocs, reciprocalRankFusion, } from '../../bm25.js';
|
|
15
|
+
import { logWarn } from '../../fault-logger.js';
|
|
13
16
|
// Lazy-load pg to make it optional
|
|
14
17
|
let pg = null;
|
|
15
18
|
async function loadPg() {
|
|
@@ -19,7 +22,10 @@ async function loadPg() {
|
|
|
19
22
|
pg = await import('pg');
|
|
20
23
|
return pg;
|
|
21
24
|
}
|
|
22
|
-
catch {
|
|
25
|
+
catch (error) {
|
|
26
|
+
logWarn('postgresql', 'Failed to import pg package for PostgreSQL backend', {
|
|
27
|
+
error: error instanceof Error ? error.message : String(error),
|
|
28
|
+
});
|
|
23
29
|
throw new ConfigError('PostgreSQL support requires the "pg" package. ' + 'Install it with: npm install pg');
|
|
24
30
|
}
|
|
25
31
|
}
|
|
@@ -105,7 +111,10 @@ export class PostgresBackend {
|
|
|
105
111
|
const { getEmbeddingInfo } = await import('../../embeddings.js');
|
|
106
112
|
return getEmbeddingInfo().dimensions ?? 384;
|
|
107
113
|
}
|
|
108
|
-
catch {
|
|
114
|
+
catch (error) {
|
|
115
|
+
logWarn('postgresql', 'Failed to read embedding dimensions, using default 384', {
|
|
116
|
+
error: error instanceof Error ? error.message : String(error),
|
|
117
|
+
});
|
|
109
118
|
return 384;
|
|
110
119
|
}
|
|
111
120
|
}
|
|
@@ -193,6 +202,9 @@ export class PostgresBackend {
|
|
|
193
202
|
await pool.query('CREATE INDEX IF NOT EXISTS idx_documents_project_id ON documents(project_id)');
|
|
194
203
|
await pool.query('CREATE INDEX IF NOT EXISTS idx_documents_file_path ON documents(file_path)');
|
|
195
204
|
await pool.query('CREATE INDEX IF NOT EXISTS idx_documents_embedding ON documents USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100)');
|
|
205
|
+
// Migration: add search_vector column for full-text search (tsvector)
|
|
206
|
+
await pool.query('ALTER TABLE documents ADD COLUMN IF NOT EXISTS search_vector tsvector');
|
|
207
|
+
await pool.query('CREATE INDEX IF NOT EXISTS idx_documents_search_vector ON documents USING GIN(search_vector)');
|
|
196
208
|
// Metadata table
|
|
197
209
|
await pool.query(`
|
|
198
210
|
CREATE TABLE IF NOT EXISTS metadata (
|
|
@@ -264,6 +276,9 @@ export class PostgresBackend {
|
|
|
264
276
|
await pool.query('CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(type)');
|
|
265
277
|
await pool.query('CREATE INDEX IF NOT EXISTS idx_memories_project_id ON memories(project_id)');
|
|
266
278
|
await pool.query('CREATE INDEX IF NOT EXISTS idx_memories_embedding ON memories USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100)');
|
|
279
|
+
// Migration: add search_vector column for full-text search (tsvector)
|
|
280
|
+
await pool.query('ALTER TABLE memories ADD COLUMN IF NOT EXISTS search_vector tsvector');
|
|
281
|
+
await pool.query('CREATE INDEX IF NOT EXISTS idx_memories_search_vector ON memories USING GIN(search_vector)');
|
|
267
282
|
// Memory links table
|
|
268
283
|
await pool.query(`
|
|
269
284
|
CREATE TABLE IF NOT EXISTS memory_links (
|
|
@@ -282,6 +297,8 @@ export class PostgresBackend {
|
|
|
282
297
|
await pool.query('CREATE INDEX IF NOT EXISTS idx_memory_links_target ON memory_links(target_id)');
|
|
283
298
|
// Migration: add llm_enriched column to memory_links
|
|
284
299
|
await pool.query('ALTER TABLE memory_links ADD COLUMN IF NOT EXISTS llm_enriched INTEGER DEFAULT 0');
|
|
300
|
+
// Migration: add metadata JSON column to memory_links (for bridge edges)
|
|
301
|
+
await pool.query('ALTER TABLE memory_links ADD COLUMN IF NOT EXISTS metadata JSONB');
|
|
285
302
|
// Migration: add project_id column to memory_links (for multi-project PG)
|
|
286
303
|
await pool.query(`
|
|
287
304
|
DO $$
|
|
@@ -295,6 +312,72 @@ export class PostgresBackend {
|
|
|
295
312
|
END $$;
|
|
296
313
|
`);
|
|
297
314
|
await pool.query('CREATE INDEX IF NOT EXISTS idx_memory_links_project_id ON memory_links(project_id)');
|
|
315
|
+
// Migration: add confidence and source_type columns for memory provenance
|
|
316
|
+
await pool.query('ALTER TABLE memories ADD COLUMN IF NOT EXISTS confidence REAL DEFAULT 0.5');
|
|
317
|
+
await pool.query("ALTER TABLE memories ADD COLUMN IF NOT EXISTS source_type TEXT DEFAULT 'human'");
|
|
318
|
+
// CHECK constraints for provenance columns (defense-in-depth)
|
|
319
|
+
await pool.query(`
|
|
320
|
+
DO $$ BEGIN
|
|
321
|
+
IF NOT EXISTS (
|
|
322
|
+
SELECT 1 FROM pg_constraint
|
|
323
|
+
WHERE conname = 'chk_memories_confidence'
|
|
324
|
+
AND conrelid = 'memories'::regclass
|
|
325
|
+
) THEN
|
|
326
|
+
UPDATE memories
|
|
327
|
+
SET confidence = 0.5
|
|
328
|
+
WHERE confidence IS NULL
|
|
329
|
+
OR confidence < 0
|
|
330
|
+
OR confidence > 1
|
|
331
|
+
OR confidence <> confidence;
|
|
332
|
+
ALTER TABLE memories
|
|
333
|
+
ADD CONSTRAINT chk_memories_confidence
|
|
334
|
+
CHECK (confidence >= 0 AND confidence <= 1 AND confidence = confidence);
|
|
335
|
+
END IF;
|
|
336
|
+
|
|
337
|
+
IF NOT EXISTS (
|
|
338
|
+
SELECT 1 FROM pg_constraint
|
|
339
|
+
WHERE conname = 'chk_memories_source_type'
|
|
340
|
+
AND conrelid = 'memories'::regclass
|
|
341
|
+
) THEN
|
|
342
|
+
UPDATE memories
|
|
343
|
+
SET source_type = 'human'
|
|
344
|
+
WHERE source_type IS NULL
|
|
345
|
+
OR source_type NOT IN ('human','agent','canonical_doc','imported','auto_extracted');
|
|
346
|
+
ALTER TABLE memories
|
|
347
|
+
ADD CONSTRAINT chk_memories_source_type
|
|
348
|
+
CHECK (source_type IN ('human','agent','canonical_doc','imported','auto_extracted'));
|
|
349
|
+
END IF;
|
|
350
|
+
END $$;
|
|
351
|
+
`);
|
|
352
|
+
// CHECK constraint for non-negative link weights (required for Dijkstra correctness)
|
|
353
|
+
await pool.query(`
|
|
354
|
+
DO $$ BEGIN
|
|
355
|
+
IF NOT EXISTS (
|
|
356
|
+
SELECT 1 FROM pg_constraint
|
|
357
|
+
WHERE conname = 'chk_memory_links_weight'
|
|
358
|
+
AND conrelid = 'memory_links'::regclass
|
|
359
|
+
) THEN
|
|
360
|
+
UPDATE memory_links SET weight = GREATEST(weight, 0)
|
|
361
|
+
WHERE weight < 0;
|
|
362
|
+
ALTER TABLE memory_links
|
|
363
|
+
ADD CONSTRAINT chk_memory_links_weight CHECK (weight >= 0);
|
|
364
|
+
END IF;
|
|
365
|
+
END $$;
|
|
366
|
+
`);
|
|
367
|
+
// Retrieval feedback table
|
|
368
|
+
await pool.query(`
|
|
369
|
+
CREATE TABLE IF NOT EXISTS recall_events (
|
|
370
|
+
id SERIAL PRIMARY KEY,
|
|
371
|
+
memory_id INTEGER NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
|
|
372
|
+
query TEXT NOT NULL,
|
|
373
|
+
was_used INTEGER NOT NULL DEFAULT 0,
|
|
374
|
+
rank_position INTEGER,
|
|
375
|
+
similarity_score REAL,
|
|
376
|
+
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
377
|
+
)
|
|
378
|
+
`);
|
|
379
|
+
await pool.query('CREATE INDEX IF NOT EXISTS idx_recall_events_memory ON recall_events(memory_id)');
|
|
380
|
+
await pool.query('CREATE INDEX IF NOT EXISTS idx_recall_events_created ON recall_events(created_at)');
|
|
298
381
|
// Memory centrality cache table
|
|
299
382
|
await pool.query(`
|
|
300
383
|
CREATE TABLE IF NOT EXISTS memory_centrality (
|
|
@@ -471,6 +554,20 @@ export class PostgresBackend {
|
|
|
471
554
|
await pool.query('CREATE INDEX IF NOT EXISTS idx_wsh_project_id ON web_search_history(project_id)');
|
|
472
555
|
await pool.query('CREATE INDEX IF NOT EXISTS idx_wsh_created ON web_search_history(created_at)');
|
|
473
556
|
await pool.query('CREATE INDEX IF NOT EXISTS idx_wsh_tool ON web_search_history(tool_name)');
|
|
557
|
+
// One-time backfill: populate search_vector for rows that existed before tsvector migration
|
|
558
|
+
const migrationKey = 'search_vector_backfill_done';
|
|
559
|
+
const migCheck = await pool.query('SELECT value FROM metadata WHERE key = $1', [migrationKey]);
|
|
560
|
+
if (migCheck.rows.length === 0) {
|
|
561
|
+
try {
|
|
562
|
+
await this.rebuildAllSearchVectors();
|
|
563
|
+
await pool.query(`INSERT INTO metadata (key, value) VALUES ($1, '1') ON CONFLICT(key) DO NOTHING`, [migrationKey]);
|
|
564
|
+
}
|
|
565
|
+
catch (error) {
|
|
566
|
+
logWarn('postgresql', 'search_vector backfill failed, will retry on next init', {
|
|
567
|
+
error,
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
}
|
|
474
571
|
}
|
|
475
572
|
/**
|
|
476
573
|
* Close all connections.
|
|
@@ -513,7 +610,14 @@ export class PostgresBackend {
|
|
|
513
610
|
symbolType ?? null,
|
|
514
611
|
signature ?? null,
|
|
515
612
|
]);
|
|
516
|
-
|
|
613
|
+
const docId = result.rows[0].id;
|
|
614
|
+
// Compute and store search_vector for full-text search
|
|
615
|
+
const tokens = filePath.startsWith('code:')
|
|
616
|
+
? tokenizeCodeWithAST(content, signature ? tokenizeCode(signature) : [], symbolName)
|
|
617
|
+
: tokenizeDocs(content);
|
|
618
|
+
const tokenStr = tokens.join(' ');
|
|
619
|
+
await pool.query(`UPDATE documents SET search_vector = CASE WHEN $1 = '' THEN NULL ELSE to_tsvector('simple', $1) END WHERE id = $2`, [tokenStr, docId]);
|
|
620
|
+
return docId;
|
|
517
621
|
}
|
|
518
622
|
async upsertDocumentsBatch(documents) {
|
|
519
623
|
if (documents.length === 0)
|
|
@@ -550,7 +654,14 @@ export class PostgresBackend {
|
|
|
550
654
|
doc.symbolType ?? null,
|
|
551
655
|
doc.signature ?? null,
|
|
552
656
|
]);
|
|
553
|
-
|
|
657
|
+
const docId = result.rows[0].id;
|
|
658
|
+
ids.push(docId);
|
|
659
|
+
// Compute and store search_vector for full-text search
|
|
660
|
+
const tokens = doc.filePath.startsWith('code:')
|
|
661
|
+
? tokenizeCodeWithAST(doc.content, doc.signature ? tokenizeCode(doc.signature) : [], doc.symbolName)
|
|
662
|
+
: tokenizeDocs(doc.content);
|
|
663
|
+
const tokenStr = tokens.join(' ');
|
|
664
|
+
await client.query(`UPDATE documents SET search_vector = CASE WHEN $1 = '' THEN NULL ELSE to_tsvector('simple', $1) END WHERE id = $2`, [tokenStr, docId]);
|
|
554
665
|
}
|
|
555
666
|
await client.query('COMMIT');
|
|
556
667
|
}
|
|
@@ -599,7 +710,14 @@ export class PostgresBackend {
|
|
|
599
710
|
doc.symbolType ?? null,
|
|
600
711
|
doc.signature ?? null,
|
|
601
712
|
]);
|
|
602
|
-
|
|
713
|
+
const docId = result.rows[0].id;
|
|
714
|
+
ids.push(docId);
|
|
715
|
+
// Compute and store search_vector for full-text search
|
|
716
|
+
const tokens = doc.filePath.startsWith('code:')
|
|
717
|
+
? tokenizeCodeWithAST(doc.content, doc.signature ? tokenizeCode(doc.signature) : [], doc.symbolName)
|
|
718
|
+
: tokenizeDocs(doc.content);
|
|
719
|
+
const tokenStr = tokens.join(' ');
|
|
720
|
+
await client.query(`UPDATE documents SET search_vector = CASE WHEN $1 = '' THEN NULL ELSE to_tsvector('simple', $1) END WHERE id = $2`, [tokenStr, docId]);
|
|
603
721
|
if (!processedFiles.has(doc.filePath)) {
|
|
604
722
|
await client.query(`INSERT INTO file_hashes (project_id, file_path, content_hash, indexed_at)
|
|
605
723
|
VALUES ($1, $2, $3, NOW())
|
|
@@ -683,7 +801,9 @@ export class PostgresBackend {
|
|
|
683
801
|
const pool = await this.getPool();
|
|
684
802
|
let query = `
|
|
685
803
|
SELECT id, content, tags, source, type, quality_score, quality_factors,
|
|
686
|
-
access_count, last_accessed,
|
|
804
|
+
access_count, last_accessed, correction_count, is_invariant,
|
|
805
|
+
priority_score, valid_from, valid_until, confidence, source_type,
|
|
806
|
+
created_at
|
|
687
807
|
FROM memories WHERE id = ANY($1)`;
|
|
688
808
|
const params = [ids];
|
|
689
809
|
let idx = 2;
|
|
@@ -711,7 +831,6 @@ export class PostgresBackend {
|
|
|
711
831
|
if (filters?.createdBefore) {
|
|
712
832
|
query += ` AND created_at <= $${idx}`;
|
|
713
833
|
params.push(filters.createdBefore.toISOString());
|
|
714
|
-
idx++;
|
|
715
834
|
}
|
|
716
835
|
const result = await pool.query(query, params);
|
|
717
836
|
return result.rows.map((row) => ({
|
|
@@ -733,6 +852,8 @@ export class PostgresBackend {
|
|
|
733
852
|
priority_score: row.priority_score ?? null,
|
|
734
853
|
valid_from: row.valid_from,
|
|
735
854
|
valid_until: row.valid_until,
|
|
855
|
+
confidence: row.confidence ?? null,
|
|
856
|
+
source_type: (row.source_type ?? null),
|
|
736
857
|
created_at: row.created_at,
|
|
737
858
|
}));
|
|
738
859
|
}
|
|
@@ -774,6 +895,432 @@ export class PostgresBackend {
|
|
|
774
895
|
await pool.query("DELETE FROM metadata WHERE key = 'embedding_model'");
|
|
775
896
|
}
|
|
776
897
|
// ============================================================================
|
|
898
|
+
// Full-Text Search Vector Updates
|
|
899
|
+
// ============================================================================
|
|
900
|
+
/**
|
|
901
|
+
* Update the search_vector for a single document (called from BM25 dispatcher).
|
|
902
|
+
* tokens: pre-tokenized string joined with spaces, e.g. "auth user login".
|
|
903
|
+
*/
|
|
904
|
+
async updateDocumentSearchVector(docId, tokens) {
|
|
905
|
+
const pool = await this.getPool();
|
|
906
|
+
const trimmed = (tokens ?? '').trim();
|
|
907
|
+
await pool.query(`UPDATE documents SET search_vector = CASE WHEN $1 = '' THEN NULL ELSE to_tsvector('simple', $1) END WHERE id = $2`, [trimmed, docId]);
|
|
908
|
+
}
|
|
909
|
+
/**
|
|
910
|
+
* Update the search_vector for a single memory (called from BM25 dispatcher).
|
|
911
|
+
*/
|
|
912
|
+
async updateMemorySearchVector(memoryId, tokens) {
|
|
913
|
+
const pool = await this.getPool();
|
|
914
|
+
const trimmed = (tokens ?? '').trim();
|
|
915
|
+
await pool.query(`UPDATE memories SET search_vector = CASE WHEN $1 = '' THEN NULL ELSE to_tsvector('simple', $1) END WHERE id = $2`, [trimmed, memoryId]);
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* Rebuild all search_vector values from content.
|
|
919
|
+
* Use after tokenizer logic changes or manual DB edits.
|
|
920
|
+
* Processes in batches to avoid memory issues on large datasets.
|
|
921
|
+
*/
|
|
922
|
+
async rebuildAllSearchVectors(batchSize = 500) {
|
|
923
|
+
const pool = await this.getPool();
|
|
924
|
+
let docCount = 0;
|
|
925
|
+
let memCount = 0;
|
|
926
|
+
// Rebuild documents in batches with batch UPDATE
|
|
927
|
+
const docBaseParams = [];
|
|
928
|
+
let docWhere;
|
|
929
|
+
if (this.projectId) {
|
|
930
|
+
docBaseParams.push(this.projectId);
|
|
931
|
+
docWhere = `LOWER(project_id) = $1`;
|
|
932
|
+
}
|
|
933
|
+
else {
|
|
934
|
+
docWhere = `TRUE`;
|
|
935
|
+
}
|
|
936
|
+
const docLimitIdx = docBaseParams.length + 1;
|
|
937
|
+
const docOffsetIdx = docBaseParams.length + 2;
|
|
938
|
+
let offset = 0;
|
|
939
|
+
while (true) {
|
|
940
|
+
const batch = await pool.query(`SELECT id, file_path, content, symbol_name, signature FROM documents
|
|
941
|
+
WHERE ${docWhere} ORDER BY id LIMIT $${docLimitIdx} OFFSET $${docOffsetIdx}`, [...docBaseParams, batchSize, offset]);
|
|
942
|
+
if (batch.rows.length === 0)
|
|
943
|
+
break;
|
|
944
|
+
const ids = [];
|
|
945
|
+
const tokenStrs = [];
|
|
946
|
+
for (const row of batch.rows) {
|
|
947
|
+
const tokens = row.file_path.startsWith('code:')
|
|
948
|
+
? tokenizeCodeWithAST(row.content, row.signature ? tokenizeCode(row.signature) : [], row.symbol_name ?? undefined)
|
|
949
|
+
: tokenizeDocs(row.content);
|
|
950
|
+
ids.push(row.id);
|
|
951
|
+
tokenStrs.push(tokens.join(' '));
|
|
952
|
+
docCount++;
|
|
953
|
+
}
|
|
954
|
+
if (ids.length > 0) {
|
|
955
|
+
await pool.query(`UPDATE documents SET search_vector = CASE WHEN v.tokens = '' THEN NULL ELSE to_tsvector('simple', v.tokens) END
|
|
956
|
+
FROM unnest($1::int[], $2::text[]) AS v(id, tokens)
|
|
957
|
+
WHERE documents.id = v.id`, [ids, tokenStrs]);
|
|
958
|
+
}
|
|
959
|
+
offset += batch.rows.length;
|
|
960
|
+
}
|
|
961
|
+
// Rebuild memories in batches with batch UPDATE
|
|
962
|
+
const memBaseParams = [];
|
|
963
|
+
let memWhere;
|
|
964
|
+
if (this.projectId) {
|
|
965
|
+
memBaseParams.push(this.projectId);
|
|
966
|
+
memWhere = `(LOWER(project_id) = $1 OR project_id IS NULL)`;
|
|
967
|
+
}
|
|
968
|
+
else {
|
|
969
|
+
memWhere = `project_id IS NULL`;
|
|
970
|
+
}
|
|
971
|
+
const limitIdx = memBaseParams.length + 1;
|
|
972
|
+
const offsetIdx = memBaseParams.length + 2;
|
|
973
|
+
offset = 0;
|
|
974
|
+
while (true) {
|
|
975
|
+
const batch = await pool.query(`SELECT id, content FROM memories WHERE ${memWhere} ORDER BY id LIMIT $${limitIdx} OFFSET $${offsetIdx}`, [...memBaseParams, batchSize, offset]);
|
|
976
|
+
if (batch.rows.length === 0)
|
|
977
|
+
break;
|
|
978
|
+
const ids = [];
|
|
979
|
+
const tokenStrs = [];
|
|
980
|
+
for (const row of batch.rows) {
|
|
981
|
+
const tokens = tokenizeDocs(row.content);
|
|
982
|
+
ids.push(row.id);
|
|
983
|
+
tokenStrs.push(tokens.join(' '));
|
|
984
|
+
memCount++;
|
|
985
|
+
}
|
|
986
|
+
if (ids.length > 0) {
|
|
987
|
+
await pool.query(`UPDATE memories SET search_vector = CASE WHEN v.tokens = '' THEN NULL ELSE to_tsvector('simple', v.tokens) END
|
|
988
|
+
FROM unnest($1::int[], $2::text[]) AS v(id, tokens)
|
|
989
|
+
WHERE memories.id = v.id`, [ids, tokenStrs]);
|
|
990
|
+
}
|
|
991
|
+
offset += batch.rows.length;
|
|
992
|
+
}
|
|
993
|
+
return { documents: docCount, memories: memCount };
|
|
994
|
+
}
|
|
995
|
+
// ============================================================================
|
|
996
|
+
// Hybrid Search (tsvector + pgvector + RRF)
|
|
997
|
+
// ============================================================================
|
|
998
|
+
/**
|
|
999
|
+
* Build a tsquery string from tokenized query terms using OR semantics.
|
|
1000
|
+
* Returns null if no valid tokens (caller should skip text search).
|
|
1001
|
+
*/
|
|
1002
|
+
buildTsquery(query, isCode) {
|
|
1003
|
+
const tokens = isCode ? tokenizeCode(query) : tokenizeDocs(query);
|
|
1004
|
+
const valid = tokens.filter((t) => t.length > 0);
|
|
1005
|
+
if (valid.length === 0)
|
|
1006
|
+
return null;
|
|
1007
|
+
// De-duplicate and join with OR operator
|
|
1008
|
+
return [...new Set(valid)].join(' | ');
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* Hybrid search over documents (code + brain docs).
|
|
1012
|
+
* Combines tsvector full-text search with pgvector cosine similarity via RRF.
|
|
1013
|
+
*/
|
|
1014
|
+
async hybridSearchDocuments(query, queryEmbedding, limit, threshold, options) {
|
|
1015
|
+
if (!this.projectId) {
|
|
1016
|
+
throw new StorageError('Project ID must be set before searching documents');
|
|
1017
|
+
}
|
|
1018
|
+
const pool = await this.getPool();
|
|
1019
|
+
const isCode = !!options?.codeOnly;
|
|
1020
|
+
// Build tsquery for text search
|
|
1021
|
+
const tsq = this.buildTsquery(query, isCode);
|
|
1022
|
+
// --- Text search ---
|
|
1023
|
+
const textResults = [];
|
|
1024
|
+
if (tsq) {
|
|
1025
|
+
let textWhere = `WHERE LOWER(project_id) = $2 AND search_vector @@ to_tsquery('simple', $1)`;
|
|
1026
|
+
const textParams = [tsq, this.projectId];
|
|
1027
|
+
if (options?.codeOnly)
|
|
1028
|
+
textWhere += ` AND file_path LIKE 'code:%'`;
|
|
1029
|
+
else if (options?.docsOnly)
|
|
1030
|
+
textWhere += ` AND file_path NOT LIKE 'code:%'`;
|
|
1031
|
+
if (options?.symbolType) {
|
|
1032
|
+
textParams.push(options.symbolType);
|
|
1033
|
+
textWhere += ` AND symbol_type = $${textParams.length}`;
|
|
1034
|
+
}
|
|
1035
|
+
textParams.push(limit * 3); // overfetch for RRF
|
|
1036
|
+
const textQ = `
|
|
1037
|
+
SELECT id, ts_rank_cd(search_vector, to_tsquery('simple', $1)) as score
|
|
1038
|
+
FROM documents
|
|
1039
|
+
${textWhere}
|
|
1040
|
+
ORDER BY score DESC
|
|
1041
|
+
LIMIT $${textParams.length}
|
|
1042
|
+
`;
|
|
1043
|
+
try {
|
|
1044
|
+
const r = await pool.query(textQ, textParams);
|
|
1045
|
+
for (const row of r.rows) {
|
|
1046
|
+
textResults.push({ docId: row.id, score: parseFloat(String(row.score)) });
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
catch (error) {
|
|
1050
|
+
logWarn('postgresql', 'hybridSearchDocuments text search failed, using vector-only', {
|
|
1051
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
// --- Vector search ---
|
|
1056
|
+
const vectorResults = [];
|
|
1057
|
+
{
|
|
1058
|
+
let vecWhere = `WHERE LOWER(project_id) = $2 AND 1 - (embedding <=> $1) >= $3`;
|
|
1059
|
+
const vecParams = [toPgVector(queryEmbedding), this.projectId, threshold];
|
|
1060
|
+
if (options?.codeOnly)
|
|
1061
|
+
vecWhere += ` AND file_path LIKE 'code:%'`;
|
|
1062
|
+
else if (options?.docsOnly)
|
|
1063
|
+
vecWhere += ` AND file_path NOT LIKE 'code:%'`;
|
|
1064
|
+
if (options?.symbolType) {
|
|
1065
|
+
vecParams.push(options.symbolType);
|
|
1066
|
+
vecWhere += ` AND symbol_type = $${vecParams.length}`;
|
|
1067
|
+
}
|
|
1068
|
+
vecParams.push(limit * 3); // overfetch for RRF
|
|
1069
|
+
const vecQ = `
|
|
1070
|
+
SELECT id, 1 - (embedding <=> $1) as score
|
|
1071
|
+
FROM documents
|
|
1072
|
+
${vecWhere}
|
|
1073
|
+
ORDER BY embedding <=> $1
|
|
1074
|
+
LIMIT $${vecParams.length}
|
|
1075
|
+
`;
|
|
1076
|
+
const r = await pool.query(vecQ, vecParams);
|
|
1077
|
+
for (const row of r.rows) {
|
|
1078
|
+
vectorResults.push({ docId: row.id, score: parseFloat(String(row.score)) });
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
// --- RRF fusion ---
|
|
1082
|
+
const fused = reciprocalRankFusion(textResults, vectorResults, 0.5, limit);
|
|
1083
|
+
if (fused.length === 0)
|
|
1084
|
+
return [];
|
|
1085
|
+
const fusedIds = fused.map((r) => r.docId);
|
|
1086
|
+
const fusedScoreMap = new Map(fused.map((r) => [r.docId, r.score]));
|
|
1087
|
+
const textScoreMap = new Map(textResults.map((r) => [r.docId, r.score]));
|
|
1088
|
+
const vecScoreMap = new Map(vectorResults.map((r) => [r.docId, r.score]));
|
|
1089
|
+
// Fetch full rows for fused IDs
|
|
1090
|
+
const rows = await pool.query(`SELECT id, file_path, content, start_line, end_line, symbol_name, symbol_type, signature
|
|
1091
|
+
FROM documents WHERE id = ANY($1)`, [fusedIds]);
|
|
1092
|
+
// Map rows by id for ordering
|
|
1093
|
+
const rowMap = new Map(rows.rows.map((r) => [r.id, r]));
|
|
1094
|
+
const results = [];
|
|
1095
|
+
for (const { docId } of fused) {
|
|
1096
|
+
const row = rowMap.get(docId);
|
|
1097
|
+
if (!row)
|
|
1098
|
+
continue;
|
|
1099
|
+
results.push({
|
|
1100
|
+
file_path: row.file_path,
|
|
1101
|
+
content: row.content,
|
|
1102
|
+
start_line: row.start_line,
|
|
1103
|
+
end_line: row.end_line,
|
|
1104
|
+
similarity: fusedScoreMap.get(docId) ?? 0,
|
|
1105
|
+
bm25Score: textScoreMap.get(docId) ?? 0,
|
|
1106
|
+
vectorScore: vecScoreMap.get(docId) ?? 0,
|
|
1107
|
+
symbol_name: row.symbol_name,
|
|
1108
|
+
symbol_type: row.symbol_type,
|
|
1109
|
+
signature: row.signature,
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
return results;
|
|
1113
|
+
}
|
|
1114
|
+
/**
|
|
1115
|
+
* Hybrid search over project memories.
|
|
1116
|
+
*/
|
|
1117
|
+
async hybridSearchMemories(query, queryEmbedding, limit, threshold) {
|
|
1118
|
+
const pool = await this.getPool();
|
|
1119
|
+
const tsq = this.buildTsquery(query, false);
|
|
1120
|
+
const now = new Date().toISOString();
|
|
1121
|
+
// --- Text search ---
|
|
1122
|
+
const textResults = [];
|
|
1123
|
+
if (tsq) {
|
|
1124
|
+
const textParams = [tsq];
|
|
1125
|
+
let paramIdx = 2;
|
|
1126
|
+
let whereCond = `WHERE search_vector @@ to_tsquery('simple', $1) AND invalidated_by IS NULL`;
|
|
1127
|
+
whereCond += ` AND (valid_from IS NULL OR valid_from <= $${paramIdx})`;
|
|
1128
|
+
textParams.push(now);
|
|
1129
|
+
paramIdx++;
|
|
1130
|
+
whereCond += ` AND (valid_until IS NULL OR valid_until > $${paramIdx})`;
|
|
1131
|
+
textParams.push(now);
|
|
1132
|
+
paramIdx++;
|
|
1133
|
+
if (this.projectId) {
|
|
1134
|
+
whereCond += ` AND (LOWER(project_id) = $${paramIdx} OR project_id IS NULL)`;
|
|
1135
|
+
textParams.push(this.projectId);
|
|
1136
|
+
}
|
|
1137
|
+
else {
|
|
1138
|
+
whereCond += ` AND project_id IS NULL`;
|
|
1139
|
+
}
|
|
1140
|
+
textParams.push(limit * 3);
|
|
1141
|
+
const textQ = `
|
|
1142
|
+
SELECT id, ts_rank_cd(search_vector, to_tsquery('simple', $1)) as score
|
|
1143
|
+
FROM memories
|
|
1144
|
+
${whereCond}
|
|
1145
|
+
ORDER BY score DESC
|
|
1146
|
+
LIMIT $${textParams.length}
|
|
1147
|
+
`;
|
|
1148
|
+
try {
|
|
1149
|
+
const r = await pool.query(textQ, textParams);
|
|
1150
|
+
for (const row of r.rows) {
|
|
1151
|
+
textResults.push({ docId: row.id, score: parseFloat(String(row.score)) });
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
catch (error) {
|
|
1155
|
+
logWarn('postgresql', 'hybridSearchMemories text search failed, using vector-only', {
|
|
1156
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
// --- Vector search ---
|
|
1161
|
+
const vectorResults = [];
|
|
1162
|
+
{
|
|
1163
|
+
const vecParams = [toPgVector(queryEmbedding), threshold];
|
|
1164
|
+
let paramIdx = 3;
|
|
1165
|
+
let whereVec = `WHERE 1 - (embedding <=> $1) >= $2 AND invalidated_by IS NULL`;
|
|
1166
|
+
whereVec += ` AND (valid_from IS NULL OR valid_from <= $${paramIdx})`;
|
|
1167
|
+
vecParams.push(now);
|
|
1168
|
+
paramIdx++;
|
|
1169
|
+
whereVec += ` AND (valid_until IS NULL OR valid_until > $${paramIdx})`;
|
|
1170
|
+
vecParams.push(now);
|
|
1171
|
+
paramIdx++;
|
|
1172
|
+
if (this.projectId) {
|
|
1173
|
+
whereVec += ` AND (LOWER(project_id) = $${paramIdx} OR project_id IS NULL)`;
|
|
1174
|
+
vecParams.push(this.projectId);
|
|
1175
|
+
}
|
|
1176
|
+
else {
|
|
1177
|
+
whereVec += ` AND project_id IS NULL`;
|
|
1178
|
+
}
|
|
1179
|
+
vecParams.push(limit * 3);
|
|
1180
|
+
const vecQ = `
|
|
1181
|
+
SELECT id, 1 - (embedding <=> $1) as score
|
|
1182
|
+
FROM memories
|
|
1183
|
+
${whereVec}
|
|
1184
|
+
ORDER BY embedding <=> $1
|
|
1185
|
+
LIMIT $${vecParams.length}
|
|
1186
|
+
`;
|
|
1187
|
+
const r = await pool.query(vecQ, vecParams);
|
|
1188
|
+
for (const row of r.rows) {
|
|
1189
|
+
vectorResults.push({ docId: row.id, score: parseFloat(String(row.score)) });
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
// --- RRF fusion ---
|
|
1193
|
+
const fused = reciprocalRankFusion(textResults, vectorResults, 0.5, limit);
|
|
1194
|
+
if (fused.length === 0)
|
|
1195
|
+
return [];
|
|
1196
|
+
const fusedIds = fused.map((r) => r.docId);
|
|
1197
|
+
const fusedScoreMap = new Map(fused.map((r) => [r.docId, r.score]));
|
|
1198
|
+
const textScoreMap = new Map(textResults.map((r) => [r.docId, r.score]));
|
|
1199
|
+
const vecScoreMap = new Map(vectorResults.map((r) => [r.docId, r.score]));
|
|
1200
|
+
// Fetch full rows
|
|
1201
|
+
const rows = await pool.query(`SELECT id, content, tags, source, type, created_at,
|
|
1202
|
+
access_count, last_accessed, valid_from, valid_until
|
|
1203
|
+
FROM memories WHERE id = ANY($1)`, [fusedIds]);
|
|
1204
|
+
const rowMap = new Map(rows.rows.map((r) => [r.id, r]));
|
|
1205
|
+
const results = [];
|
|
1206
|
+
for (const { docId } of fused) {
|
|
1207
|
+
const row = rowMap.get(docId);
|
|
1208
|
+
if (!row)
|
|
1209
|
+
continue;
|
|
1210
|
+
results.push({
|
|
1211
|
+
id: row.id,
|
|
1212
|
+
content: row.content,
|
|
1213
|
+
tags: row.tags ? (typeof row.tags === 'string' ? JSON.parse(row.tags) : row.tags) : [],
|
|
1214
|
+
source: row.source,
|
|
1215
|
+
type: (row.type ?? null),
|
|
1216
|
+
created_at: row.created_at,
|
|
1217
|
+
similarity: fusedScoreMap.get(docId) ?? 0,
|
|
1218
|
+
bm25Score: textScoreMap.get(docId) ?? 0,
|
|
1219
|
+
vectorScore: vecScoreMap.get(docId) ?? 0,
|
|
1220
|
+
access_count: row.access_count ?? 0,
|
|
1221
|
+
last_accessed: row.last_accessed,
|
|
1222
|
+
valid_from: row.valid_from,
|
|
1223
|
+
valid_until: row.valid_until,
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
return results;
|
|
1227
|
+
}
|
|
1228
|
+
/**
|
|
1229
|
+
* Hybrid search over global memories (project_id IS NULL).
|
|
1230
|
+
*/
|
|
1231
|
+
async hybridSearchGlobalMemories(query, queryEmbedding, limit, threshold, tags, since) {
|
|
1232
|
+
const pool = await this.getPool();
|
|
1233
|
+
const tsq = this.buildTsquery(query, false);
|
|
1234
|
+
// --- Text search ---
|
|
1235
|
+
const textResults = [];
|
|
1236
|
+
if (tsq) {
|
|
1237
|
+
const textParams = [tsq];
|
|
1238
|
+
const paramIdx = 2;
|
|
1239
|
+
let whereText = `WHERE search_vector @@ to_tsquery('simple', $1) AND project_id IS NULL AND invalidated_by IS NULL`;
|
|
1240
|
+
if (since) {
|
|
1241
|
+
whereText += ` AND created_at >= $${paramIdx}`;
|
|
1242
|
+
textParams.push(since.toISOString());
|
|
1243
|
+
}
|
|
1244
|
+
textParams.push(limit * 3);
|
|
1245
|
+
const textQ = `
|
|
1246
|
+
SELECT id, ts_rank_cd(search_vector, to_tsquery('simple', $1)) as score
|
|
1247
|
+
FROM memories
|
|
1248
|
+
${whereText}
|
|
1249
|
+
ORDER BY score DESC
|
|
1250
|
+
LIMIT $${textParams.length}
|
|
1251
|
+
`;
|
|
1252
|
+
try {
|
|
1253
|
+
const r = await pool.query(textQ, textParams);
|
|
1254
|
+
for (const row of r.rows) {
|
|
1255
|
+
textResults.push({ docId: row.id, score: parseFloat(String(row.score)) });
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
catch (error) {
|
|
1259
|
+
logWarn('postgresql', 'hybridSearchGlobalMemories text search failed, using vector-only', {
|
|
1260
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
// --- Vector search ---
|
|
1265
|
+
const vectorResults = [];
|
|
1266
|
+
{
|
|
1267
|
+
const vecParams = [toPgVector(queryEmbedding), threshold];
|
|
1268
|
+
const paramIdx = 3;
|
|
1269
|
+
let whereVec = `WHERE 1 - (embedding <=> $1) >= $2 AND project_id IS NULL AND invalidated_by IS NULL`;
|
|
1270
|
+
if (since) {
|
|
1271
|
+
whereVec += ` AND created_at >= $${paramIdx}`;
|
|
1272
|
+
vecParams.push(since.toISOString());
|
|
1273
|
+
}
|
|
1274
|
+
vecParams.push(limit * 3);
|
|
1275
|
+
const vecQ = `
|
|
1276
|
+
SELECT id, 1 - (embedding <=> $1) as score
|
|
1277
|
+
FROM memories
|
|
1278
|
+
${whereVec}
|
|
1279
|
+
ORDER BY embedding <=> $1
|
|
1280
|
+
LIMIT $${vecParams.length}
|
|
1281
|
+
`;
|
|
1282
|
+
const r = await pool.query(vecQ, vecParams);
|
|
1283
|
+
for (const row of r.rows) {
|
|
1284
|
+
vectorResults.push({ docId: row.id, score: parseFloat(String(row.score)) });
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
// --- RRF fusion ---
|
|
1288
|
+
const fused = reciprocalRankFusion(textResults, vectorResults, 0.5, limit);
|
|
1289
|
+
if (fused.length === 0)
|
|
1290
|
+
return [];
|
|
1291
|
+
const fusedIds = fused.map((r) => r.docId);
|
|
1292
|
+
const fusedScoreMap = new Map(fused.map((r) => [r.docId, r.score]));
|
|
1293
|
+
const textScoreMap = new Map(textResults.map((r) => [r.docId, r.score]));
|
|
1294
|
+
const vecScoreMap = new Map(vectorResults.map((r) => [r.docId, r.score]));
|
|
1295
|
+
// Fetch full rows
|
|
1296
|
+
const rows = await pool.query(`SELECT id, project_id, content, tags, source, type, created_at
|
|
1297
|
+
FROM memories WHERE id = ANY($1)`, [fusedIds]);
|
|
1298
|
+
const rowMap = new Map(rows.rows.map((r) => [r.id, r]));
|
|
1299
|
+
let results = [];
|
|
1300
|
+
for (const { docId } of fused) {
|
|
1301
|
+
const row = rowMap.get(docId);
|
|
1302
|
+
if (!row)
|
|
1303
|
+
continue;
|
|
1304
|
+
results.push({
|
|
1305
|
+
id: row.id,
|
|
1306
|
+
content: row.content,
|
|
1307
|
+
tags: row.tags ? (typeof row.tags === 'string' ? JSON.parse(row.tags) : row.tags) : [],
|
|
1308
|
+
source: row.source,
|
|
1309
|
+
project: row.project_id,
|
|
1310
|
+
type: (row.type ?? null),
|
|
1311
|
+
created_at: row.created_at,
|
|
1312
|
+
similarity: fusedScoreMap.get(docId) ?? 0,
|
|
1313
|
+
bm25Score: textScoreMap.get(docId) ?? 0,
|
|
1314
|
+
vectorScore: vecScoreMap.get(docId) ?? 0,
|
|
1315
|
+
});
|
|
1316
|
+
}
|
|
1317
|
+
// Tag filter (post-filter in JS, consistent with searchGlobalMemories)
|
|
1318
|
+
if (tags && tags.length > 0) {
|
|
1319
|
+
results = results.filter((m) => tags.some((t) => m.tags.some((rt) => rt.toLowerCase().includes(t.toLowerCase()))));
|
|
1320
|
+
}
|
|
1321
|
+
return results;
|
|
1322
|
+
}
|
|
1323
|
+
// ============================================================================
|
|
777
1324
|
// File Hashes
|
|
778
1325
|
// ============================================================================
|
|
779
1326
|
async getFileHash(filePath) {
|
|
@@ -824,11 +1371,18 @@ export class PostgresBackend {
|
|
|
824
1371
|
// ============================================================================
|
|
825
1372
|
// Memory Operations
|
|
826
1373
|
// ============================================================================
|
|
827
|
-
async saveMemory(content, embedding, tags = [], source, type = 'observation', qualityScore, qualityFactors, validFrom, validUntil, isGlobal = false) {
|
|
1374
|
+
async saveMemory(content, embedding, tags = [], source, type = 'observation', qualityScore, qualityFactors, validFrom, validUntil, isGlobal = false, confidence = 0.5, sourceType = 'human') {
|
|
1375
|
+
// Validate provenance fields
|
|
1376
|
+
if (!Number.isFinite(confidence) || confidence < 0 || confidence > 1) {
|
|
1377
|
+
confidence = 0.5;
|
|
1378
|
+
}
|
|
1379
|
+
if (!SOURCE_TYPES.includes(sourceType)) {
|
|
1380
|
+
sourceType = 'human';
|
|
1381
|
+
}
|
|
828
1382
|
const pool = await this.getPool();
|
|
829
1383
|
const projectId = isGlobal ? null : this.projectId;
|
|
830
|
-
const result = await pool.query(`INSERT INTO memories (project_id, content, tags, source, type, quality_score, quality_factors, embedding, valid_from, valid_until)
|
|
831
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
|
1384
|
+
const result = await pool.query(`INSERT INTO memories (project_id, content, tags, source, type, quality_score, quality_factors, embedding, valid_from, valid_until, confidence, source_type)
|
|
1385
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
|
832
1386
|
RETURNING id`, [
|
|
833
1387
|
projectId,
|
|
834
1388
|
content,
|
|
@@ -840,8 +1394,15 @@ export class PostgresBackend {
|
|
|
840
1394
|
toPgVector(embedding),
|
|
841
1395
|
validFrom ?? null,
|
|
842
1396
|
validUntil ?? null,
|
|
1397
|
+
confidence,
|
|
1398
|
+
sourceType,
|
|
843
1399
|
]);
|
|
844
|
-
|
|
1400
|
+
const memoryId = result.rows[0].id;
|
|
1401
|
+
// Compute and store search_vector for full-text search
|
|
1402
|
+
const tokens = tokenizeDocs(content);
|
|
1403
|
+
const tokenStr = tokens.join(' ');
|
|
1404
|
+
await pool.query(`UPDATE memories SET search_vector = CASE WHEN $1 = '' THEN NULL ELSE to_tsvector('simple', $1) END WHERE id = $2`, [tokenStr, memoryId]);
|
|
1405
|
+
return memoryId;
|
|
845
1406
|
}
|
|
846
1407
|
async searchMemories(queryEmbedding, limit = 5, threshold = 0.3, tags, since, options) {
|
|
847
1408
|
const pool = await this.getPool();
|
|
@@ -850,8 +1411,9 @@ export class PostgresBackend {
|
|
|
850
1411
|
const includeGlobal = options?.includeGlobal ?? true;
|
|
851
1412
|
let query = `
|
|
852
1413
|
SELECT id, project_id, content, tags, source, type, quality_score, quality_factors,
|
|
853
|
-
access_count, last_accessed,
|
|
854
|
-
|
|
1414
|
+
access_count, last_accessed, correction_count, is_invariant,
|
|
1415
|
+
priority_score, valid_from, valid_until, confidence, source_type,
|
|
1416
|
+
created_at, 1 - (embedding <=> $1) as similarity
|
|
855
1417
|
FROM memories
|
|
856
1418
|
WHERE 1 - (embedding <=> $1) >= $2
|
|
857
1419
|
AND invalidated_by IS NULL
|
|
@@ -908,6 +1470,8 @@ export class PostgresBackend {
|
|
|
908
1470
|
priority_score: row.priority_score ?? null,
|
|
909
1471
|
valid_from: row.valid_from,
|
|
910
1472
|
valid_until: row.valid_until,
|
|
1473
|
+
confidence: row.confidence ?? null,
|
|
1474
|
+
source_type: (row.source_type ?? null),
|
|
911
1475
|
created_at: row.created_at,
|
|
912
1476
|
similarity: parseFloat(row.similarity),
|
|
913
1477
|
}));
|
|
@@ -921,7 +1485,7 @@ export class PostgresBackend {
|
|
|
921
1485
|
const pool = await this.getPool();
|
|
922
1486
|
const result = await pool.query(`SELECT id, content, tags, source, type, quality_score, quality_factors,
|
|
923
1487
|
access_count, last_accessed, valid_from, valid_until,
|
|
924
|
-
correction_count, is_invariant, priority_score, created_at
|
|
1488
|
+
correction_count, is_invariant, priority_score, confidence, source_type, created_at
|
|
925
1489
|
FROM memories WHERE id = $1`, [id]);
|
|
926
1490
|
if (result.rows.length === 0)
|
|
927
1491
|
return null;
|
|
@@ -945,6 +1509,8 @@ export class PostgresBackend {
|
|
|
945
1509
|
priority_score: row.priority_score ?? null,
|
|
946
1510
|
valid_from: row.valid_from,
|
|
947
1511
|
valid_until: row.valid_until,
|
|
1512
|
+
confidence: row.confidence ?? null,
|
|
1513
|
+
source_type: (row.source_type ?? null),
|
|
948
1514
|
created_at: row.created_at,
|
|
949
1515
|
};
|
|
950
1516
|
}
|
|
@@ -952,7 +1518,7 @@ export class PostgresBackend {
|
|
|
952
1518
|
const pool = await this.getPool();
|
|
953
1519
|
const result = await pool.query(`SELECT id, content, tags, source, type, quality_score, quality_factors,
|
|
954
1520
|
access_count, last_accessed, valid_from, valid_until,
|
|
955
|
-
correction_count, is_invariant, priority_score, created_at
|
|
1521
|
+
correction_count, is_invariant, priority_score, confidence, source_type, created_at
|
|
956
1522
|
FROM memories
|
|
957
1523
|
WHERE tags::jsonb ? $1
|
|
958
1524
|
AND invalidated_by IS NULL
|
|
@@ -978,6 +1544,8 @@ export class PostgresBackend {
|
|
|
978
1544
|
priority_score: row.priority_score ?? null,
|
|
979
1545
|
valid_from: row.valid_from,
|
|
980
1546
|
valid_until: row.valid_until,
|
|
1547
|
+
confidence: row.confidence ?? null,
|
|
1548
|
+
source_type: (row.source_type ?? null),
|
|
981
1549
|
created_at: row.created_at,
|
|
982
1550
|
}));
|
|
983
1551
|
}
|
|
@@ -1012,7 +1580,7 @@ export class PostgresBackend {
|
|
|
1012
1580
|
let query = `
|
|
1013
1581
|
SELECT id, project_id, content, tags, source, type, quality_score, quality_factors,
|
|
1014
1582
|
access_count, last_accessed, valid_from, valid_until,
|
|
1015
|
-
correction_count, is_invariant, priority_score, created_at
|
|
1583
|
+
correction_count, is_invariant, priority_score, confidence, source_type, created_at
|
|
1016
1584
|
FROM memories
|
|
1017
1585
|
`;
|
|
1018
1586
|
const params = [];
|
|
@@ -1053,6 +1621,8 @@ export class PostgresBackend {
|
|
|
1053
1621
|
correction_count: row.correction_count ?? 0,
|
|
1054
1622
|
is_invariant: !!row.is_invariant,
|
|
1055
1623
|
priority_score: row.priority_score ?? null,
|
|
1624
|
+
confidence: row.confidence ?? null,
|
|
1625
|
+
source_type: (row.source_type ?? null),
|
|
1056
1626
|
created_at: row.created_at,
|
|
1057
1627
|
}));
|
|
1058
1628
|
}
|
|
@@ -1078,7 +1648,7 @@ export class PostgresBackend {
|
|
|
1078
1648
|
let query = `
|
|
1079
1649
|
SELECT id, content, tags, source, type, quality_score, quality_factors,
|
|
1080
1650
|
access_count, last_accessed, valid_from, valid_until,
|
|
1081
|
-
correction_count, is_invariant, priority_score, created_at
|
|
1651
|
+
correction_count, is_invariant, priority_score, confidence, source_type, created_at
|
|
1082
1652
|
FROM memories
|
|
1083
1653
|
`;
|
|
1084
1654
|
const params = [];
|
|
@@ -1093,7 +1663,6 @@ export class PostgresBackend {
|
|
|
1093
1663
|
}
|
|
1094
1664
|
query += ` AND (correction_count >= $${paramIndex} OR is_invariant = 1)`;
|
|
1095
1665
|
params.push(threshold);
|
|
1096
|
-
paramIndex++;
|
|
1097
1666
|
query += ` ORDER BY is_invariant DESC, correction_count DESC, quality_score DESC`;
|
|
1098
1667
|
const result = await pool.query(query, params);
|
|
1099
1668
|
return result.rows.map((row) => ({
|
|
@@ -1115,6 +1684,8 @@ export class PostgresBackend {
|
|
|
1115
1684
|
correction_count: row.correction_count ?? 0,
|
|
1116
1685
|
is_invariant: !!row.is_invariant,
|
|
1117
1686
|
priority_score: row.priority_score ?? null,
|
|
1687
|
+
confidence: row.confidence ?? null,
|
|
1688
|
+
source_type: (row.source_type ?? null),
|
|
1118
1689
|
created_at: row.created_at,
|
|
1119
1690
|
}));
|
|
1120
1691
|
}
|
|
@@ -1128,7 +1699,7 @@ export class PostgresBackend {
|
|
|
1128
1699
|
// ============================================================================
|
|
1129
1700
|
// Memory Links
|
|
1130
1701
|
// ============================================================================
|
|
1131
|
-
async createMemoryLink(sourceId, targetId, relation = 'related', weight = 1.0, validFrom, validUntil) {
|
|
1702
|
+
async createMemoryLink(sourceId, targetId, relation = 'related', weight = 1.0, validFrom, validUntil, metadata) {
|
|
1132
1703
|
const pool = await this.getPool();
|
|
1133
1704
|
// Validate that both memories belong to current project (case-insensitive for Windows paths)
|
|
1134
1705
|
const validation = await pool.query(`SELECT COUNT(*) as count FROM memories
|
|
@@ -1136,16 +1707,35 @@ export class PostgresBackend {
|
|
|
1136
1707
|
if (parseInt(validation.rows[0].count) !== 2) {
|
|
1137
1708
|
throw new StorageError('Cannot link memories from different projects');
|
|
1138
1709
|
}
|
|
1139
|
-
const
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1710
|
+
const metadataJson = metadata ? JSON.stringify(metadata) : null;
|
|
1711
|
+
const result = await pool.query(`INSERT INTO memory_links (source_id, target_id, relation, weight, valid_from, valid_until, metadata)
|
|
1712
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)
|
|
1713
|
+
ON CONFLICT (source_id, target_id, relation) DO UPDATE
|
|
1714
|
+
SET metadata = CASE
|
|
1715
|
+
WHEN memory_links.metadata IS NULL THEN EXCLUDED.metadata
|
|
1716
|
+
WHEN EXCLUDED.metadata IS NULL THEN memory_links.metadata
|
|
1717
|
+
ELSE memory_links.metadata || EXCLUDED.metadata || jsonb_build_object(
|
|
1718
|
+
'code_paths',
|
|
1719
|
+
COALESCE((SELECT jsonb_agg(DISTINCT p)
|
|
1720
|
+
FROM jsonb_array_elements_text(
|
|
1721
|
+
COALESCE(memory_links.metadata->'code_paths', '[]'::jsonb) ||
|
|
1722
|
+
COALESCE(EXCLUDED.metadata->'code_paths', '[]'::jsonb) ||
|
|
1723
|
+
CASE WHEN memory_links.metadata ? 'code_path'
|
|
1724
|
+
THEN jsonb_build_array(memory_links.metadata->>'code_path')
|
|
1725
|
+
ELSE '[]'::jsonb END ||
|
|
1726
|
+
CASE WHEN EXCLUDED.metadata ? 'code_path'
|
|
1727
|
+
THEN jsonb_build_array(EXCLUDED.metadata->>'code_path')
|
|
1728
|
+
ELSE '[]'::jsonb END
|
|
1729
|
+
) p), '[]'::jsonb)
|
|
1730
|
+
)
|
|
1731
|
+
END,
|
|
1732
|
+
weight = EXCLUDED.weight,
|
|
1733
|
+
valid_from = COALESCE(EXCLUDED.valid_from, memory_links.valid_from),
|
|
1734
|
+
valid_until = COALESCE(EXCLUDED.valid_until, memory_links.valid_until)
|
|
1735
|
+
RETURNING id, xmax::text`, [sourceId, targetId, relation, weight, validFrom ?? null, validUntil ?? null, metadataJson]);
|
|
1736
|
+
const row = result.rows[0];
|
|
1737
|
+
// xmax = '0' means a fresh insert; non-zero means an update (conflict resolved)
|
|
1738
|
+
return { id: row.id, created: row.xmax === '0' };
|
|
1149
1739
|
}
|
|
1150
1740
|
async deleteMemoryLink(sourceId, targetId, relation) {
|
|
1151
1741
|
const pool = await this.getPool();
|
|
@@ -1296,7 +1886,8 @@ export class PostgresBackend {
|
|
|
1296
1886
|
const pool = await this.getPool();
|
|
1297
1887
|
const query = `
|
|
1298
1888
|
SELECT id, project_id, content, tags, source, type, quality_score, quality_factors,
|
|
1299
|
-
access_count, last_accessed, valid_from, valid_until,
|
|
1889
|
+
access_count, last_accessed, valid_from, valid_until,
|
|
1890
|
+
correction_count, is_invariant, priority_score, confidence, source_type, created_at,
|
|
1300
1891
|
1 - (embedding <=> $1) as similarity
|
|
1301
1892
|
FROM memories
|
|
1302
1893
|
WHERE 1 - (embedding <=> $1) >= $2
|
|
@@ -1326,6 +1917,8 @@ export class PostgresBackend {
|
|
|
1326
1917
|
priority_score: row.priority_score ?? null,
|
|
1327
1918
|
valid_from: row.valid_from,
|
|
1328
1919
|
valid_until: row.valid_until,
|
|
1920
|
+
confidence: row.confidence ?? null,
|
|
1921
|
+
source_type: (row.source_type ?? null),
|
|
1329
1922
|
created_at: row.created_at,
|
|
1330
1923
|
similarity: parseFloat(row.similarity),
|
|
1331
1924
|
}));
|
|
@@ -1341,7 +1934,8 @@ export class PostgresBackend {
|
|
|
1341
1934
|
async getRecentGlobalMemories(limit = 10) {
|
|
1342
1935
|
const pool = await this.getPool();
|
|
1343
1936
|
const result = await pool.query(`SELECT id, project_id, content, tags, source, type, quality_score, quality_factors,
|
|
1344
|
-
access_count, last_accessed, valid_from, valid_until,
|
|
1937
|
+
access_count, last_accessed, valid_from, valid_until,
|
|
1938
|
+
correction_count, is_invariant, priority_score, confidence, source_type, created_at
|
|
1345
1939
|
FROM memories
|
|
1346
1940
|
WHERE project_id IS NULL
|
|
1347
1941
|
AND invalidated_by IS NULL
|
|
@@ -1366,6 +1960,8 @@ export class PostgresBackend {
|
|
|
1366
1960
|
priority_score: row.priority_score ?? null,
|
|
1367
1961
|
valid_from: row.valid_from,
|
|
1368
1962
|
valid_until: row.valid_until,
|
|
1963
|
+
confidence: row.confidence ?? null,
|
|
1964
|
+
source_type: (row.source_type ?? null),
|
|
1369
1965
|
created_at: row.created_at,
|
|
1370
1966
|
}));
|
|
1371
1967
|
}
|
|
@@ -1651,7 +2247,11 @@ export class PostgresBackend {
|
|
|
1651
2247
|
catch (err) {
|
|
1652
2248
|
// Duplicate key: a link with the target (source_id, target_id, relation) already exists.
|
|
1653
2249
|
// Delete this link and mark the existing one as enriched instead.
|
|
1654
|
-
if (err
|
|
2250
|
+
if (typeof err === 'object' &&
|
|
2251
|
+
err !== null &&
|
|
2252
|
+
'code' in err &&
|
|
2253
|
+
err.code === '23505' &&
|
|
2254
|
+
updates.relation) {
|
|
1655
2255
|
const row = await pool.query('SELECT source_id, target_id FROM memory_links WHERE id = $1', [linkId]);
|
|
1656
2256
|
if (row.rows.length > 0) {
|
|
1657
2257
|
const { source_id, target_id } = row.rows[0];
|
|
@@ -1793,11 +2393,11 @@ export class PostgresBackend {
|
|
|
1793
2393
|
`, params);
|
|
1794
2394
|
return result.rows.map((row) => ({
|
|
1795
2395
|
event_type: row.event_type,
|
|
1796
|
-
query_count: parseInt(row.query_count),
|
|
1797
|
-
total_returned_tokens: parseInt(row.total_returned_tokens),
|
|
1798
|
-
total_full_source_tokens: parseInt(row.total_full_source_tokens),
|
|
1799
|
-
total_savings_tokens: parseInt(row.total_savings_tokens),
|
|
1800
|
-
total_estimated_cost: parseFloat(row.total_estimated_cost),
|
|
2396
|
+
query_count: parseInt(String(row.query_count), 10),
|
|
2397
|
+
total_returned_tokens: parseInt(String(row.total_returned_tokens), 10),
|
|
2398
|
+
total_full_source_tokens: parseInt(String(row.total_full_source_tokens), 10),
|
|
2399
|
+
total_savings_tokens: parseInt(String(row.total_savings_tokens), 10),
|
|
2400
|
+
total_estimated_cost: parseFloat(String(row.total_estimated_cost)),
|
|
1801
2401
|
}));
|
|
1802
2402
|
}
|
|
1803
2403
|
async clearTokenStats() {
|
|
@@ -1864,16 +2464,16 @@ export class PostgresBackend {
|
|
|
1864
2464
|
const result = await pool.query(`SELECT id, tool_name, model, query, prompt_tokens, completion_tokens, estimated_cost_usd, citations_count, has_reasoning, response_length_chars, created_at
|
|
1865
2465
|
FROM web_search_history ${where} ORDER BY created_at DESC LIMIT $${paramIdx}`, params);
|
|
1866
2466
|
return result.rows.map((row) => ({
|
|
1867
|
-
id: parseInt(row.id),
|
|
2467
|
+
id: parseInt(String(row.id), 10),
|
|
1868
2468
|
tool_name: row.tool_name,
|
|
1869
2469
|
model: row.model,
|
|
1870
2470
|
query: row.query,
|
|
1871
|
-
prompt_tokens: parseInt(row.prompt_tokens),
|
|
1872
|
-
completion_tokens: parseInt(row.completion_tokens),
|
|
1873
|
-
estimated_cost_usd: parseFloat(row.estimated_cost_usd),
|
|
1874
|
-
citations_count: parseInt(row.citations_count),
|
|
2471
|
+
prompt_tokens: parseInt(String(row.prompt_tokens), 10),
|
|
2472
|
+
completion_tokens: parseInt(String(row.completion_tokens), 10),
|
|
2473
|
+
estimated_cost_usd: parseFloat(String(row.estimated_cost_usd)),
|
|
2474
|
+
citations_count: parseInt(String(row.citations_count), 10),
|
|
1875
2475
|
has_reasoning: !!row.has_reasoning,
|
|
1876
|
-
response_length_chars: parseInt(row.response_length_chars),
|
|
2476
|
+
response_length_chars: parseInt(String(row.response_length_chars), 10),
|
|
1877
2477
|
created_at: row.created_at instanceof Date ? row.created_at.toISOString() : row.created_at,
|
|
1878
2478
|
}));
|
|
1879
2479
|
}
|
|
@@ -2066,8 +2666,16 @@ export class PostgresBackend {
|
|
|
2066
2666
|
? memory.validUntil.toISOString()
|
|
2067
2667
|
: memory.validUntil
|
|
2068
2668
|
: null;
|
|
2069
|
-
|
|
2070
|
-
|
|
2669
|
+
// Validate provenance fields — mirror saveMemory() behaviour
|
|
2670
|
+
const rawConfidence = memory.confidence ?? 0.5;
|
|
2671
|
+
const confidence = !Number.isFinite(rawConfidence) || rawConfidence < 0 || rawConfidence > 1
|
|
2672
|
+
? 0.5
|
|
2673
|
+
: rawConfidence;
|
|
2674
|
+
const sourceType = memory.sourceType && SOURCE_TYPES.includes(memory.sourceType)
|
|
2675
|
+
? memory.sourceType
|
|
2676
|
+
: 'human';
|
|
2677
|
+
const insertResult = await client.query(`INSERT INTO memories (project_id, content, tags, source, type, quality_score, quality_factors, embedding, valid_from, valid_until, confidence, source_type)
|
|
2678
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
|
2071
2679
|
RETURNING id`, [
|
|
2072
2680
|
this.projectId,
|
|
2073
2681
|
memory.content,
|
|
@@ -2079,11 +2687,18 @@ export class PostgresBackend {
|
|
|
2079
2687
|
toPgVector(memory.embedding),
|
|
2080
2688
|
validFromStr,
|
|
2081
2689
|
validUntilStr,
|
|
2690
|
+
confidence,
|
|
2691
|
+
sourceType,
|
|
2082
2692
|
]);
|
|
2693
|
+
const memId = insertResult.rows[0].id;
|
|
2694
|
+
// Compute and store search_vector for full-text search
|
|
2695
|
+
const memTokens = tokenizeDocs(memory.content);
|
|
2696
|
+
const memTokenStr = memTokens.join(' ');
|
|
2697
|
+
await client.query(`UPDATE memories SET search_vector = CASE WHEN $1 = '' THEN NULL ELSE to_tsvector('simple', $1) END WHERE id = $2`, [memTokenStr, memId]);
|
|
2083
2698
|
results.push({
|
|
2084
2699
|
index: i,
|
|
2085
2700
|
isDuplicate: false,
|
|
2086
|
-
id:
|
|
2701
|
+
id: memId,
|
|
2087
2702
|
reason: 'saved',
|
|
2088
2703
|
});
|
|
2089
2704
|
saved++;
|
|
@@ -2134,6 +2749,29 @@ export class PostgresBackend {
|
|
|
2134
2749
|
stale_count: parseInt(stale.rows[0].count),
|
|
2135
2750
|
};
|
|
2136
2751
|
}
|
|
2752
|
+
async getMemoryHealth() {
|
|
2753
|
+
const pool = await this.getPool();
|
|
2754
|
+
const scopeCond = this.projectId
|
|
2755
|
+
? '(LOWER(project_id) = $1 OR project_id IS NULL)'
|
|
2756
|
+
: 'project_id IS NULL';
|
|
2757
|
+
const scopeParams = this.projectId ? [this.projectId] : [];
|
|
2758
|
+
const result = await pool.query(`SELECT
|
|
2759
|
+
COUNT(*) as total,
|
|
2760
|
+
SUM(CASE WHEN access_count = 0 THEN 1 ELSE 0 END) as never_accessed,
|
|
2761
|
+
SUM(CASE WHEN EXTRACT(EPOCH FROM (now() - COALESCE(last_accessed::timestamp, created_at))) / 86400 > 90
|
|
2762
|
+
THEN 1 ELSE 0 END) as stale_unused_90d,
|
|
2763
|
+
AVG(EXTRACT(EPOCH FROM (now() - created_at)) / 86400) as avg_age_days,
|
|
2764
|
+
AVG(access_count) as avg_access
|
|
2765
|
+
FROM memories WHERE ${scopeCond} AND invalidated_by IS NULL`, scopeParams);
|
|
2766
|
+
const row = result.rows[0];
|
|
2767
|
+
return {
|
|
2768
|
+
total: parseInt(row.total) || 0,
|
|
2769
|
+
never_accessed: parseInt(row.never_accessed) || 0,
|
|
2770
|
+
stale_unused_90d: parseInt(row.stale_unused_90d) || 0,
|
|
2771
|
+
avg_age_days: parseFloat(row.avg_age_days ?? '0') || 0,
|
|
2772
|
+
avg_access: parseFloat(row.avg_access ?? '0') || 0,
|
|
2773
|
+
};
|
|
2774
|
+
}
|
|
2137
2775
|
async deleteMemoriesOlderThan(date) {
|
|
2138
2776
|
const pool = await this.getPool();
|
|
2139
2777
|
const result = await pool.query('DELETE FROM memories WHERE created_at < $1', [
|
|
@@ -2169,7 +2807,8 @@ export class PostgresBackend {
|
|
|
2169
2807
|
: 'project_id IS NULL';
|
|
2170
2808
|
const scopeParams = this.projectId ? [this.projectId] : [];
|
|
2171
2809
|
const result = await pool.query(`SELECT id, project_id, content, tags, source, type, quality_score, quality_factors,
|
|
2172
|
-
access_count, last_accessed, valid_from, valid_until,
|
|
2810
|
+
access_count, last_accessed, valid_from, valid_until,
|
|
2811
|
+
correction_count, is_invariant, priority_score, confidence, source_type, created_at,
|
|
2173
2812
|
1 - (embedding <=> $1) as similarity
|
|
2174
2813
|
FROM memories
|
|
2175
2814
|
WHERE created_at <= $2
|
|
@@ -2198,8 +2837,10 @@ export class PostgresBackend {
|
|
|
2198
2837
|
priority_score: row.priority_score ?? null,
|
|
2199
2838
|
valid_from: row.valid_from,
|
|
2200
2839
|
valid_until: row.valid_until,
|
|
2840
|
+
confidence: row.confidence ?? null,
|
|
2841
|
+
source_type: (row.source_type ?? null),
|
|
2201
2842
|
created_at: row.created_at,
|
|
2202
|
-
similarity: parseFloat(row.similarity),
|
|
2843
|
+
similarity: parseFloat(String(row.similarity)),
|
|
2203
2844
|
}));
|
|
2204
2845
|
}
|
|
2205
2846
|
async getConsolidationHistory(limit = 20) {
|
|
@@ -2288,7 +2929,11 @@ export class PostgresBackend {
|
|
|
2288
2929
|
return result.rows.map((row) => ({
|
|
2289
2930
|
id: row.id,
|
|
2290
2931
|
content: row.content,
|
|
2291
|
-
tags: row.tags
|
|
2932
|
+
tags: Array.isArray(row.tags)
|
|
2933
|
+
? row.tags
|
|
2934
|
+
: typeof row.tags === 'string'
|
|
2935
|
+
? JSON.parse(row.tags)
|
|
2936
|
+
: [],
|
|
2292
2937
|
source: row.source,
|
|
2293
2938
|
type: row.type,
|
|
2294
2939
|
projectId: row.project_id,
|