@stackmemoryai/stackmemory 0.2.8 → 0.3.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/dist/agents/core/agent-task-manager.js +512 -0
- package/dist/agents/core/agent-task-manager.js.map +7 -0
- package/dist/agents/verifiers/base-verifier.js +129 -0
- package/dist/agents/verifiers/base-verifier.js.map +7 -0
- package/dist/agents/verifiers/formatter-verifier.js +126 -0
- package/dist/agents/verifiers/formatter-verifier.js.map +7 -0
- package/dist/agents/verifiers/llm-judge.js +248 -0
- package/dist/agents/verifiers/llm-judge.js.map +7 -0
- package/dist/cli/__tests__/index.test.js +290 -0
- package/dist/cli/__tests__/index.test.js.map +7 -0
- package/dist/cli/auto-detect.js +317 -0
- package/dist/cli/auto-detect.js.map +7 -0
- package/dist/cli/browser-test.js +29 -0
- package/dist/cli/browser-test.js.map +7 -0
- package/dist/cli/claude-sm.js +369 -0
- package/dist/cli/claude-sm.js.map +7 -0
- package/dist/cli/codex-sm.js +283 -0
- package/dist/cli/codex-sm.js.map +7 -0
- package/dist/cli/commands/agent.js +286 -0
- package/dist/cli/commands/agent.js.map +7 -0
- package/dist/cli/commands/config.js +199 -0
- package/dist/cli/commands/config.js.map +7 -0
- package/dist/cli/commands/context.js +327 -0
- package/dist/cli/commands/context.js.map +7 -0
- package/dist/cli/commands/handoff.js +191 -0
- package/dist/cli/commands/handoff.js.map +7 -0
- package/dist/cli/commands/linear-test.js +115 -0
- package/dist/cli/commands/linear-test.js.map +7 -0
- package/dist/cli/commands/linear.js +378 -0
- package/dist/cli/commands/linear.js.map +7 -0
- package/dist/cli/commands/log.js +165 -0
- package/dist/cli/commands/log.js.map +7 -0
- package/dist/cli/commands/onboard.js +349 -0
- package/dist/cli/commands/onboard.js.map +7 -0
- package/dist/cli/commands/projects.js +195 -0
- package/dist/cli/commands/projects.js.map +7 -0
- package/dist/cli/commands/search.js +152 -0
- package/dist/cli/commands/search.js.map +7 -0
- package/dist/cli/commands/session.js +179 -0
- package/dist/cli/commands/session.js.map +7 -0
- package/dist/cli/commands/tasks.js +205 -0
- package/dist/cli/commands/tasks.js.map +7 -0
- package/dist/cli/commands/webhook.js +131 -0
- package/dist/cli/commands/webhook.js.map +7 -0
- package/dist/cli/commands/worktree.js +276 -0
- package/dist/cli/commands/worktree.js.map +7 -0
- package/dist/cli/index.js +953 -0
- package/dist/cli/index.js.map +7 -0
- package/dist/cli/utils/viewer.js +92 -0
- package/dist/cli/utils/viewer.js.map +7 -0
- package/dist/core/config/__tests__/config-manager.test.js +248 -0
- package/dist/core/config/__tests__/config-manager.test.js.map +7 -0
- package/dist/core/config/config-manager.js +368 -0
- package/dist/core/config/config-manager.js.map +7 -0
- package/dist/core/config/types.js +140 -0
- package/dist/core/config/types.js.map +7 -0
- package/dist/core/context/__tests__/frame-manager.test.js +879 -0
- package/dist/core/context/__tests__/frame-manager.test.js.map +7 -0
- package/dist/core/context/auto-context.js +72 -0
- package/dist/core/context/auto-context.js.map +7 -0
- package/dist/core/context/compaction-handler.js +326 -0
- package/dist/core/context/compaction-handler.js.map +7 -0
- package/dist/core/context/frame-database.js +376 -0
- package/dist/core/context/frame-database.js.map +7 -0
- package/dist/core/context/frame-digest.js +239 -0
- package/dist/core/context/frame-digest.js.map +7 -0
- package/dist/core/context/frame-manager.js +682 -0
- package/dist/core/context/frame-manager.js.map +7 -0
- package/dist/core/context/frame-stack.js +270 -0
- package/dist/core/context/frame-stack.js.map +7 -0
- package/dist/core/context/frame-types.js +1 -0
- package/dist/core/context/frame-types.js.map +7 -0
- package/dist/core/context/index.js +33 -0
- package/dist/core/context/index.js.map +7 -0
- package/dist/core/context/model-aware-compaction.js +619 -0
- package/dist/core/context/model-aware-compaction.js.map +7 -0
- package/dist/core/context/refactored-frame-manager.js +393 -0
- package/dist/core/context/refactored-frame-manager.js.map +7 -0
- package/dist/core/database/batch-operations.js +329 -0
- package/dist/core/database/batch-operations.js.map +7 -0
- package/dist/core/database/connection-pool.js +224 -0
- package/dist/core/database/connection-pool.js.map +7 -0
- package/dist/core/database/query-cache.js +284 -0
- package/dist/core/database/query-cache.js.map +7 -0
- package/dist/core/digest/__tests__/enhanced-hybrid-digest.test.js +379 -0
- package/dist/core/digest/__tests__/enhanced-hybrid-digest.test.js.map +7 -0
- package/dist/core/digest/__tests__/frame-digest-integration.test.js +230 -0
- package/dist/core/digest/__tests__/frame-digest-integration.test.js.map +7 -0
- package/dist/core/digest/enhanced-hybrid-digest.js +267 -0
- package/dist/core/digest/enhanced-hybrid-digest.js.map +7 -0
- package/dist/core/digest/frame-digest-integration.js +172 -0
- package/dist/core/digest/frame-digest-integration.js.map +7 -0
- package/dist/core/digest/hybrid-digest-generator.js +549 -0
- package/dist/core/digest/hybrid-digest-generator.js.map +7 -0
- package/dist/core/digest/index.js +5 -0
- package/dist/core/digest/index.js.map +7 -0
- package/dist/core/digest/types.js +21 -0
- package/dist/core/digest/types.js.map +7 -0
- package/dist/core/errors/__tests__/error-handling.test.js +270 -0
- package/dist/core/errors/__tests__/error-handling.test.js.map +7 -0
- package/dist/core/errors/index.js +239 -0
- package/dist/core/errors/index.js.map +7 -0
- package/dist/core/errors/recovery.js +258 -0
- package/dist/core/errors/recovery.js.map +7 -0
- package/dist/core/merge/__tests__/conflict-scenarios.test.js +414 -0
- package/dist/core/merge/__tests__/conflict-scenarios.test.js.map +7 -0
- package/dist/core/merge/conflict-detector.js +424 -0
- package/dist/core/merge/conflict-detector.js.map +7 -0
- package/dist/core/merge/index.js +5 -0
- package/dist/core/merge/index.js.map +7 -0
- package/dist/core/merge/resolution-engine.js +565 -0
- package/dist/core/merge/resolution-engine.js.map +7 -0
- package/dist/core/merge/stack-diff.js +528 -0
- package/dist/core/merge/stack-diff.js.map +7 -0
- package/dist/core/merge/types.js +1 -0
- package/dist/core/merge/types.js.map +7 -0
- package/dist/core/monitoring/error-handler.js +278 -0
- package/dist/core/monitoring/error-handler.js.map +7 -0
- package/dist/core/monitoring/logger.js +115 -0
- package/dist/core/monitoring/logger.js.map +7 -0
- package/dist/core/monitoring/metrics.js +157 -0
- package/dist/core/monitoring/metrics.js.map +7 -0
- package/dist/core/monitoring/progress-tracker.js +174 -0
- package/dist/core/monitoring/progress-tracker.js.map +7 -0
- package/dist/core/performance/context-cache.js +269 -0
- package/dist/core/performance/context-cache.js.map +7 -0
- package/dist/core/performance/index.js +7 -0
- package/dist/core/performance/index.js.map +7 -0
- package/dist/core/performance/lazy-context-loader.js +319 -0
- package/dist/core/performance/lazy-context-loader.js.map +7 -0
- package/dist/core/performance/monitor.js +217 -0
- package/dist/core/performance/monitor.js.map +7 -0
- package/dist/core/performance/optimized-frame-context.js +326 -0
- package/dist/core/performance/optimized-frame-context.js.map +7 -0
- package/dist/core/performance/performance-benchmark.js +269 -0
- package/dist/core/performance/performance-benchmark.js.map +7 -0
- package/dist/core/performance/performance-profiler.js +318 -0
- package/dist/core/performance/performance-profiler.js.map +7 -0
- package/dist/core/performance/streaming-jsonl-parser.js +187 -0
- package/dist/core/performance/streaming-jsonl-parser.js.map +7 -0
- package/dist/core/persistence/postgres-adapter.js +345 -0
- package/dist/core/persistence/postgres-adapter.js.map +7 -0
- package/dist/core/projects/project-manager.js +699 -0
- package/dist/core/projects/project-manager.js.map +7 -0
- package/dist/core/query/__tests__/query-parser.test.js +301 -0
- package/dist/core/query/__tests__/query-parser.test.js.map +7 -0
- package/dist/core/query/__tests__/query-templates.test.js +210 -0
- package/dist/core/query/__tests__/query-templates.test.js.map +7 -0
- package/dist/core/query/query-parser.js +366 -0
- package/dist/core/query/query-parser.js.map +7 -0
- package/dist/core/query/query-templates.js +317 -0
- package/dist/core/query/query-templates.js.map +7 -0
- package/dist/core/retrieval/index.js +4 -0
- package/dist/core/retrieval/index.js.map +7 -0
- package/dist/core/retrieval/llm-context-retrieval.js +577 -0
- package/dist/core/retrieval/llm-context-retrieval.js.map +7 -0
- package/dist/core/retrieval/summary-generator.js +585 -0
- package/dist/core/retrieval/summary-generator.js.map +7 -0
- package/dist/core/retrieval/types.js +17 -0
- package/dist/core/retrieval/types.js.map +7 -0
- package/dist/core/session/index.js +11 -0
- package/dist/core/session/index.js.map +7 -0
- package/dist/core/session/session-manager.js +297 -0
- package/dist/core/session/session-manager.js.map +7 -0
- package/dist/core/trace/cli-trace-wrapper.js +110 -0
- package/dist/core/trace/cli-trace-wrapper.js.map +7 -0
- package/dist/core/trace/db-trace-wrapper.js +215 -0
- package/dist/core/trace/db-trace-wrapper.js.map +7 -0
- package/dist/core/trace/debug-trace.js +385 -0
- package/dist/core/trace/debug-trace.js.map +7 -0
- package/dist/core/trace/index.js +158 -0
- package/dist/core/trace/index.js.map +7 -0
- package/dist/core/trace/linear-api-wrapper.js +169 -0
- package/dist/core/trace/linear-api-wrapper.js.map +7 -0
- package/dist/core/trace/trace-demo.js +135 -0
- package/dist/core/trace/trace-demo.js.map +7 -0
- package/dist/core/trace/trace-detector.demo.js +138 -0
- package/dist/core/trace/trace-detector.demo.js.map +7 -0
- package/dist/core/trace/trace-detector.js +386 -0
- package/dist/core/trace/trace-detector.js.map +7 -0
- package/dist/core/trace/trace-detector.test.js +401 -0
- package/dist/core/trace/trace-detector.test.js.map +7 -0
- package/dist/core/trace/trace-store.js +341 -0
- package/dist/core/trace/trace-store.js.map +7 -0
- package/dist/core/trace/types.js +73 -0
- package/dist/core/trace/types.js.map +7 -0
- package/dist/core/types.js +1 -0
- package/dist/core/types.js.map +7 -0
- package/dist/core/utils/update-checker.js +214 -0
- package/dist/core/utils/update-checker.js.map +7 -0
- package/dist/core/worktree/worktree-manager.js +450 -0
- package/dist/core/worktree/worktree-manager.js.map +7 -0
- package/dist/features/analytics/api/analytics-api.js +283 -0
- package/dist/features/analytics/api/analytics-api.js.map +7 -0
- package/dist/features/analytics/core/analytics-service.js +267 -0
- package/dist/features/analytics/core/analytics-service.js.map +7 -0
- package/dist/features/analytics/index.js +14 -0
- package/dist/features/analytics/index.js.map +7 -0
- package/dist/features/analytics/queries/metrics-queries.js +273 -0
- package/dist/features/analytics/queries/metrics-queries.js.map +7 -0
- package/dist/features/analytics/types/metrics.js +1 -0
- package/dist/features/analytics/types/metrics.js.map +7 -0
- package/dist/features/browser/browser-mcp.js +488 -0
- package/dist/features/browser/browser-mcp.js.map +7 -0
- package/dist/features/tasks/__tests__/pebbles-task-store.test.js +747 -0
- package/dist/features/tasks/__tests__/pebbles-task-store.test.js.map +7 -0
- package/dist/features/tasks/pebbles-task-store.js +647 -0
- package/dist/features/tasks/pebbles-task-store.js.map +7 -0
- package/dist/features/tasks/task-aware-context.js +406 -0
- package/dist/features/tasks/task-aware-context.js.map +7 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +7 -0
- package/dist/integrations/linear/__tests__/auth.test.js +558 -0
- package/dist/integrations/linear/__tests__/auth.test.js.map +7 -0
- package/dist/integrations/linear/__tests__/sync-service.test.js +760 -0
- package/dist/integrations/linear/__tests__/sync-service.test.js.map +7 -0
- package/dist/integrations/linear/auth.js +308 -0
- package/dist/integrations/linear/auth.js.map +7 -0
- package/dist/integrations/linear/auto-sync.js +244 -0
- package/dist/integrations/linear/auto-sync.js.map +7 -0
- package/dist/integrations/linear/client.js +448 -0
- package/dist/integrations/linear/client.js.map +7 -0
- package/dist/integrations/linear/config.js +115 -0
- package/dist/integrations/linear/config.js.map +7 -0
- package/dist/integrations/linear/sync-manager.js +233 -0
- package/dist/integrations/linear/sync-manager.js.map +7 -0
- package/dist/integrations/linear/sync-service.js +214 -0
- package/dist/integrations/linear/sync-service.js.map +7 -0
- package/dist/integrations/linear/sync.js +565 -0
- package/dist/integrations/linear/sync.js.map +7 -0
- package/dist/integrations/linear/types.js +1 -0
- package/dist/integrations/linear/types.js.map +7 -0
- package/dist/integrations/linear/webhook-server.js +204 -0
- package/dist/integrations/linear/webhook-server.js.map +7 -0
- package/dist/integrations/linear/webhook.js +269 -0
- package/dist/integrations/linear/webhook.js.map +7 -0
- package/dist/integrations/mcp/__tests__/server.test.js +798 -0
- package/dist/integrations/mcp/__tests__/server.test.js.map +7 -0
- package/dist/integrations/mcp/handlers/context-handlers.js +253 -0
- package/dist/integrations/mcp/handlers/context-handlers.js.map +7 -0
- package/dist/integrations/mcp/handlers/index.js +134 -0
- package/dist/integrations/mcp/handlers/index.js.map +7 -0
- package/dist/integrations/mcp/handlers/linear-handlers.js +243 -0
- package/dist/integrations/mcp/handlers/linear-handlers.js.map +7 -0
- package/dist/integrations/mcp/handlers/task-handlers.js +235 -0
- package/dist/integrations/mcp/handlers/task-handlers.js.map +7 -0
- package/dist/integrations/mcp/handlers/trace-handlers.js +304 -0
- package/dist/integrations/mcp/handlers/trace-handlers.js.map +7 -0
- package/dist/integrations/mcp/index.js +19 -0
- package/dist/integrations/mcp/index.js.map +7 -0
- package/dist/integrations/mcp/refactored-server.js +331 -0
- package/dist/integrations/mcp/refactored-server.js.map +7 -0
- package/dist/integrations/mcp/server.js +1621 -0
- package/dist/integrations/mcp/server.js.map +7 -0
- package/dist/integrations/mcp/tool-definitions.js +562 -0
- package/dist/integrations/mcp/tool-definitions.js.map +7 -0
- package/dist/integrations/mcp/trace-test.js +44 -0
- package/dist/integrations/mcp/trace-test.js.map +7 -0
- package/dist/integrations/pg-aiguide/embedding-provider.js +174 -0
- package/dist/integrations/pg-aiguide/embedding-provider.js.map +7 -0
- package/dist/integrations/pg-aiguide/semantic-search.js +183 -0
- package/dist/integrations/pg-aiguide/semantic-search.js.map +7 -0
- package/dist/integrations/pg-aiguide/timescale-analytics.js +220 -0
- package/dist/integrations/pg-aiguide/timescale-analytics.js.map +7 -0
- package/dist/mcp/stackmemory-mcp-server.js +550 -0
- package/dist/mcp/stackmemory-mcp-server.js.map +7 -0
- package/dist/middleware/exponential-rate-limiter.js +285 -0
- package/dist/middleware/exponential-rate-limiter.js.map +7 -0
- package/dist/models/user.model.js +351 -0
- package/dist/models/user.model.js.map +7 -0
- package/dist/scripts/benchmark-performance.d.ts +7 -0
- package/dist/scripts/benchmark-performance.d.ts.map +1 -0
- package/dist/scripts/benchmark-performance.js +44 -0
- package/dist/scripts/benchmark-performance.js.map +1 -0
- package/dist/scripts/cleanup-duplicate-tasks.d.ts +12 -0
- package/dist/scripts/cleanup-duplicate-tasks.d.ts.map +1 -0
- package/dist/scripts/cleanup-duplicate-tasks.js +215 -0
- package/dist/scripts/cleanup-duplicate-tasks.js.map +1 -0
- package/dist/servers/production/auth-middleware.js +513 -0
- package/dist/servers/production/auth-middleware.js.map +7 -0
- package/dist/servers/railway/index.js +390 -0
- package/dist/servers/railway/index.js.map +7 -0
- package/dist/services/config-service.js +62 -0
- package/dist/services/config-service.js.map +7 -0
- package/dist/services/context-service.js +191 -0
- package/dist/services/context-service.js.map +7 -0
- package/dist/src/agents/core/agent-task-manager.d.ts +154 -0
- package/dist/src/agents/core/agent-task-manager.d.ts.map +1 -0
- package/dist/src/agents/core/agent-task-manager.js +504 -0
- package/dist/src/agents/core/agent-task-manager.js.map +1 -0
- package/dist/src/agents/verifiers/base-verifier.d.ts +112 -0
- package/dist/src/agents/verifiers/base-verifier.d.ts.map +1 -0
- package/dist/src/agents/verifiers/base-verifier.js +130 -0
- package/dist/src/agents/verifiers/base-verifier.js.map +1 -0
- package/dist/src/agents/verifiers/formatter-verifier.d.ts +14 -0
- package/dist/src/agents/verifiers/formatter-verifier.d.ts.map +1 -0
- package/dist/src/agents/verifiers/formatter-verifier.js +107 -0
- package/dist/src/agents/verifiers/formatter-verifier.js.map +1 -0
- package/dist/src/agents/verifiers/llm-judge.d.ts +46 -0
- package/dist/src/agents/verifiers/llm-judge.d.ts.map +1 -0
- package/dist/src/agents/verifiers/llm-judge.js +248 -0
- package/dist/src/agents/verifiers/llm-judge.js.map +1 -0
- package/dist/src/cli/claude-sm.js +55 -0
- package/dist/src/cli/claude-sm.js.map +1 -1
- package/dist/src/cli/commands/agent.d.ts +9 -0
- package/dist/src/cli/commands/agent.d.ts.map +1 -0
- package/dist/src/cli/commands/agent.js +303 -0
- package/dist/src/cli/commands/agent.js.map +1 -0
- package/dist/src/cli/commands/handoff.d.ts +6 -0
- package/dist/src/cli/commands/handoff.d.ts.map +1 -0
- package/dist/src/cli/commands/handoff.js +212 -0
- package/dist/src/cli/commands/handoff.js.map +1 -0
- package/dist/src/cli/index.d.ts.map +1 -1
- package/dist/src/cli/index.js +4 -0
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/core/context/compaction-handler.d.ts.map +1 -1
- package/dist/src/core/context/compaction-handler.js +1 -1
- package/dist/src/core/context/compaction-handler.js.map +1 -1
- package/dist/src/core/context/frame-database.d.ts +59 -0
- package/dist/src/core/context/frame-database.d.ts.map +1 -0
- package/dist/src/core/context/frame-database.js +333 -0
- package/dist/src/core/context/frame-database.js.map +1 -0
- package/dist/src/core/context/frame-digest.d.ts +59 -0
- package/dist/src/core/context/frame-digest.d.ts.map +1 -0
- package/dist/src/core/context/frame-digest.js +264 -0
- package/dist/src/core/context/frame-digest.js.map +1 -0
- package/dist/src/core/context/frame-manager.d.ts +2 -0
- package/dist/src/core/context/frame-manager.d.ts.map +1 -1
- package/dist/src/core/context/frame-manager.js +7 -0
- package/dist/src/core/context/frame-manager.js.map +1 -1
- package/dist/src/core/context/frame-stack.d.ts +85 -0
- package/dist/src/core/context/frame-stack.d.ts.map +1 -0
- package/dist/src/core/context/frame-stack.js +287 -0
- package/dist/src/core/context/frame-stack.js.map +1 -0
- package/dist/src/core/context/frame-types.d.ts +67 -0
- package/dist/src/core/context/frame-types.d.ts.map +1 -0
- package/dist/src/core/context/frame-types.js +6 -0
- package/dist/src/core/context/frame-types.js.map +1 -0
- package/dist/src/core/context/index.d.ts +11 -0
- package/dist/src/core/context/index.d.ts.map +1 -0
- package/dist/src/core/context/index.js +14 -0
- package/dist/src/core/context/index.js.map +1 -0
- package/dist/src/core/context/refactored-frame-manager.d.ts +99 -0
- package/dist/src/core/context/refactored-frame-manager.d.ts.map +1 -0
- package/dist/src/core/context/refactored-frame-manager.js +340 -0
- package/dist/src/core/context/refactored-frame-manager.js.map +1 -0
- package/dist/src/core/database/batch-operations.d.ts +118 -0
- package/dist/src/core/database/batch-operations.d.ts.map +1 -0
- package/dist/src/core/database/batch-operations.js +339 -0
- package/dist/src/core/database/batch-operations.js.map +1 -0
- package/dist/src/core/database/connection-pool.d.ts +79 -0
- package/dist/src/core/database/connection-pool.d.ts.map +1 -0
- package/dist/src/core/database/connection-pool.js +236 -0
- package/dist/src/core/database/connection-pool.js.map +1 -0
- package/dist/src/core/database/query-cache.d.ts +135 -0
- package/dist/src/core/database/query-cache.d.ts.map +1 -0
- package/dist/src/core/database/query-cache.js +294 -0
- package/dist/src/core/database/query-cache.js.map +1 -0
- package/dist/src/core/digest/enhanced-hybrid-digest.d.ts +125 -0
- package/dist/src/core/digest/enhanced-hybrid-digest.d.ts.map +1 -0
- package/dist/src/core/digest/enhanced-hybrid-digest.js +282 -0
- package/dist/src/core/digest/enhanced-hybrid-digest.js.map +1 -0
- package/dist/src/core/digest/frame-digest-integration.d.ts +67 -0
- package/dist/src/core/digest/frame-digest-integration.d.ts.map +1 -0
- package/dist/src/core/digest/frame-digest-integration.js +198 -0
- package/dist/src/core/digest/frame-digest-integration.js.map +1 -0
- package/dist/src/core/digest/hybrid-digest-generator.d.ts +76 -0
- package/dist/src/core/digest/hybrid-digest-generator.d.ts.map +1 -0
- package/dist/src/core/digest/hybrid-digest-generator.js +629 -0
- package/dist/src/core/digest/hybrid-digest-generator.js.map +1 -0
- package/dist/src/core/digest/index.d.ts +9 -0
- package/dist/src/core/digest/index.d.ts.map +1 -0
- package/dist/src/core/digest/index.js +9 -0
- package/dist/src/core/digest/index.js.map +1 -0
- package/dist/src/core/digest/types.d.ts +154 -0
- package/dist/src/core/digest/types.d.ts.map +1 -0
- package/dist/src/core/digest/types.js +18 -0
- package/dist/src/core/digest/types.js.map +1 -0
- package/dist/src/core/errors/index.d.ts +13 -5
- package/dist/src/core/errors/index.d.ts.map +1 -1
- package/dist/src/core/errors/index.js +13 -5
- package/dist/src/core/errors/index.js.map +1 -1
- package/dist/src/core/merge/conflict-detector.d.ts +122 -0
- package/dist/src/core/merge/conflict-detector.d.ts.map +1 -0
- package/dist/src/core/merge/conflict-detector.js +468 -0
- package/dist/src/core/merge/conflict-detector.js.map +1 -0
- package/dist/src/core/merge/index.d.ts +9 -0
- package/dist/src/core/merge/index.d.ts.map +1 -0
- package/dist/src/core/merge/index.js +9 -0
- package/dist/src/core/merge/index.js.map +1 -0
- package/dist/src/core/merge/resolution-engine.d.ts +120 -0
- package/dist/src/core/merge/resolution-engine.d.ts.map +1 -0
- package/dist/src/core/merge/resolution-engine.js +573 -0
- package/dist/src/core/merge/resolution-engine.js.map +1 -0
- package/dist/src/core/merge/stack-diff.d.ts +97 -0
- package/dist/src/core/merge/stack-diff.d.ts.map +1 -0
- package/dist/src/core/merge/stack-diff.js +516 -0
- package/dist/src/core/merge/stack-diff.js.map +1 -0
- package/dist/src/core/merge/types.d.ts +110 -0
- package/dist/src/core/merge/types.d.ts.map +1 -0
- package/dist/src/core/merge/types.js +6 -0
- package/dist/src/core/merge/types.js.map +1 -0
- package/dist/src/core/monitoring/logger.d.ts +2 -2
- package/dist/src/core/monitoring/logger.d.ts.map +1 -1
- package/dist/src/core/monitoring/logger.js +10 -5
- package/dist/src/core/monitoring/logger.js.map +1 -1
- package/dist/src/core/monitoring/metrics.d.ts +3 -0
- package/dist/src/core/monitoring/metrics.d.ts.map +1 -1
- package/dist/src/core/monitoring/metrics.js +142 -3
- package/dist/src/core/monitoring/metrics.js.map +1 -1
- package/dist/src/core/performance/context-cache.d.ts +109 -0
- package/dist/src/core/performance/context-cache.d.ts.map +1 -0
- package/dist/src/core/performance/context-cache.js +280 -0
- package/dist/src/core/performance/context-cache.js.map +1 -0
- package/dist/src/core/performance/index.d.ts +3 -0
- package/dist/src/core/performance/index.d.ts.map +1 -0
- package/dist/src/core/performance/index.js +3 -0
- package/dist/src/core/performance/index.js.map +1 -0
- package/dist/src/core/performance/lazy-context-loader.d.ts +93 -0
- package/dist/src/core/performance/lazy-context-loader.d.ts.map +1 -0
- package/dist/src/core/performance/lazy-context-loader.js +332 -0
- package/dist/src/core/performance/lazy-context-loader.js.map +1 -0
- package/dist/src/core/performance/monitor.d.ts +48 -0
- package/dist/src/core/performance/monitor.d.ts.map +1 -0
- package/dist/src/core/performance/monitor.js +226 -0
- package/dist/src/core/performance/monitor.js.map +1 -0
- package/dist/src/core/performance/optimized-frame-context.d.ts +74 -0
- package/dist/src/core/performance/optimized-frame-context.d.ts.map +1 -0
- package/dist/src/core/performance/optimized-frame-context.js +330 -0
- package/dist/src/core/performance/optimized-frame-context.js.map +1 -0
- package/dist/src/core/performance/performance-benchmark.d.ts +50 -0
- package/dist/src/core/performance/performance-benchmark.d.ts.map +1 -0
- package/dist/src/core/performance/performance-benchmark.js +290 -0
- package/dist/src/core/performance/performance-benchmark.js.map +1 -0
- package/dist/src/core/performance/performance-profiler.d.ts +151 -0
- package/dist/src/core/performance/performance-profiler.d.ts.map +1 -0
- package/dist/src/core/performance/performance-profiler.js +346 -0
- package/dist/src/core/performance/performance-profiler.js.map +1 -0
- package/dist/src/core/performance/streaming-jsonl-parser.d.ts +41 -0
- package/dist/src/core/performance/streaming-jsonl-parser.d.ts.map +1 -0
- package/dist/src/core/performance/streaming-jsonl-parser.js +193 -0
- package/dist/src/core/performance/streaming-jsonl-parser.js.map +1 -0
- package/dist/src/core/persistence/postgres-adapter.d.ts +31 -0
- package/dist/src/core/persistence/postgres-adapter.d.ts.map +1 -0
- package/dist/src/core/persistence/postgres-adapter.js +330 -0
- package/dist/src/core/persistence/postgres-adapter.js.map +1 -0
- package/dist/src/core/query/query-parser.d.ts +5 -0
- package/dist/src/core/query/query-parser.d.ts.map +1 -1
- package/dist/src/core/query/query-parser.js +86 -18
- package/dist/src/core/query/query-parser.js.map +1 -1
- package/dist/src/core/query/query-templates.d.ts +44 -0
- package/dist/src/core/query/query-templates.d.ts.map +1 -0
- package/dist/src/core/query/query-templates.js +326 -0
- package/dist/src/core/query/query-templates.js.map +1 -0
- package/dist/src/core/retrieval/llm-context-retrieval.d.ts +5 -3
- package/dist/src/core/retrieval/llm-context-retrieval.d.ts.map +1 -1
- package/dist/src/core/retrieval/llm-context-retrieval.js +73 -21
- package/dist/src/core/retrieval/llm-context-retrieval.js.map +1 -1
- package/dist/src/core/trace/cli-trace-wrapper.d.ts +23 -0
- package/dist/src/core/trace/cli-trace-wrapper.d.ts.map +1 -0
- package/dist/src/core/trace/cli-trace-wrapper.js +141 -0
- package/dist/src/core/trace/cli-trace-wrapper.js.map +1 -0
- package/dist/src/core/trace/db-trace-wrapper.d.ts +36 -0
- package/dist/src/core/trace/db-trace-wrapper.d.ts.map +1 -0
- package/dist/src/core/trace/db-trace-wrapper.js +252 -0
- package/dist/src/core/trace/db-trace-wrapper.js.map +1 -0
- package/dist/src/core/trace/debug-trace.d.ts +84 -0
- package/dist/src/core/trace/debug-trace.d.ts.map +1 -0
- package/dist/src/core/trace/debug-trace.js +402 -0
- package/dist/src/core/trace/debug-trace.js.map +1 -0
- package/dist/src/core/trace/error-test.d.ts +6 -0
- package/dist/src/core/trace/error-test.d.ts.map +1 -0
- package/dist/src/core/trace/error-test.js +128 -0
- package/dist/src/core/trace/error-test.js.map +1 -0
- package/dist/src/core/trace/index.d.ts +25 -0
- package/dist/src/core/trace/index.d.ts.map +1 -0
- package/dist/src/core/trace/index.js +121 -0
- package/dist/src/core/trace/index.js.map +1 -0
- package/dist/src/core/trace/linear-api-wrapper.d.ts +17 -0
- package/dist/src/core/trace/linear-api-wrapper.d.ts.map +1 -0
- package/dist/src/core/trace/linear-api-wrapper.js +205 -0
- package/dist/src/core/trace/linear-api-wrapper.js.map +1 -0
- package/dist/src/core/trace/performance-test.d.ts +6 -0
- package/dist/src/core/trace/performance-test.d.ts.map +1 -0
- package/dist/src/core/trace/performance-test.js +111 -0
- package/dist/src/core/trace/performance-test.js.map +1 -0
- package/dist/src/core/trace/trace-demo.d.ts +8 -0
- package/dist/src/core/trace/trace-demo.d.ts.map +1 -0
- package/dist/src/core/trace/trace-demo.js +154 -0
- package/dist/src/core/trace/trace-demo.js.map +1 -0
- package/dist/src/core/trace/trace-detector.d.ts +2 -2
- package/dist/src/core/trace/trace-detector.d.ts.map +1 -1
- package/dist/src/core/trace/trace-detector.demo.js +6 -6
- package/dist/src/core/trace/trace-detector.demo.js.map +1 -1
- package/dist/src/core/trace/trace-detector.js +3 -3
- package/dist/src/core/trace/trace-detector.js.map +1 -1
- package/dist/src/core/types.d.ts +35 -0
- package/dist/src/core/types.d.ts.map +1 -0
- package/dist/src/core/types.js +2 -0
- package/dist/src/core/types.js.map +1 -0
- package/dist/src/features/tasks/pebbles-task-store.d.ts +9 -2
- package/dist/src/features/tasks/pebbles-task-store.d.ts.map +1 -1
- package/dist/src/features/tasks/pebbles-task-store.js +97 -18
- package/dist/src/features/tasks/pebbles-task-store.js.map +1 -1
- package/dist/src/integrations/linear/auth.d.ts.map +1 -1
- package/dist/src/integrations/linear/auth.js.map +1 -1
- package/dist/src/integrations/linear/client.d.ts +15 -1
- package/dist/src/integrations/linear/client.d.ts.map +1 -1
- package/dist/src/integrations/linear/client.js +85 -3
- package/dist/src/integrations/linear/client.js.map +1 -1
- package/dist/src/integrations/linear/sync-manager.d.ts +2 -0
- package/dist/src/integrations/linear/sync-manager.d.ts.map +1 -1
- package/dist/src/integrations/linear/sync-manager.js +16 -4
- package/dist/src/integrations/linear/sync-manager.js.map +1 -1
- package/dist/src/integrations/linear/sync-service.d.ts +23 -2
- package/dist/src/integrations/linear/sync-service.d.ts.map +1 -1
- package/dist/src/integrations/linear/sync-service.js +44 -25
- package/dist/src/integrations/linear/sync-service.js.map +1 -1
- package/dist/src/integrations/linear/sync.d.ts +6 -0
- package/dist/src/integrations/linear/sync.d.ts.map +1 -1
- package/dist/src/integrations/linear/sync.js +27 -2
- package/dist/src/integrations/linear/sync.js.map +1 -1
- package/dist/src/integrations/linear/types.d.ts +16 -1
- package/dist/src/integrations/linear/types.d.ts.map +1 -1
- package/dist/src/integrations/linear/webhook-server.d.ts.map +1 -1
- package/dist/src/integrations/linear/webhook-server.js +10 -8
- package/dist/src/integrations/linear/webhook-server.js.map +1 -1
- package/dist/src/integrations/linear/webhook.d.ts +13 -0
- package/dist/src/integrations/linear/webhook.d.ts.map +1 -1
- package/dist/src/integrations/linear/webhook.js +101 -14
- package/dist/src/integrations/linear/webhook.js.map +1 -1
- package/dist/src/integrations/mcp/handlers/context-handlers.d.ts +39 -0
- package/dist/src/integrations/mcp/handlers/context-handlers.d.ts.map +1 -0
- package/dist/src/integrations/mcp/handlers/context-handlers.js +266 -0
- package/dist/src/integrations/mcp/handlers/context-handlers.js.map +1 -0
- package/dist/src/integrations/mcp/handlers/index.d.ts +37 -0
- package/dist/src/integrations/mcp/handlers/index.d.ts.map +1 -0
- package/dist/src/integrations/mcp/handlers/index.js +134 -0
- package/dist/src/integrations/mcp/handlers/index.js.map +1 -0
- package/dist/src/integrations/mcp/handlers/linear-handlers.d.ts +33 -0
- package/dist/src/integrations/mcp/handlers/linear-handlers.d.ts.map +1 -0
- package/dist/src/integrations/mcp/handlers/linear-handlers.js +251 -0
- package/dist/src/integrations/mcp/handlers/linear-handlers.js.map +1 -0
- package/dist/src/integrations/mcp/handlers/task-handlers.d.ts +42 -0
- package/dist/src/integrations/mcp/handlers/task-handlers.d.ts.map +1 -0
- package/dist/src/integrations/mcp/handlers/task-handlers.js +238 -0
- package/dist/src/integrations/mcp/handlers/task-handlers.js.map +1 -0
- package/dist/src/integrations/mcp/handlers/trace-handlers.d.ts +41 -0
- package/dist/src/integrations/mcp/handlers/trace-handlers.d.ts.map +1 -0
- package/dist/src/integrations/mcp/handlers/trace-handlers.js +298 -0
- package/dist/src/integrations/mcp/handlers/trace-handlers.js.map +1 -0
- package/dist/src/integrations/mcp/index.d.ts +13 -0
- package/dist/src/integrations/mcp/index.d.ts.map +1 -0
- package/dist/src/integrations/mcp/index.js +17 -0
- package/dist/src/integrations/mcp/index.js.map +1 -0
- package/dist/src/integrations/mcp/refactored-server.d.ts +76 -0
- package/dist/src/integrations/mcp/refactored-server.d.ts.map +1 -0
- package/dist/src/integrations/mcp/refactored-server.js +351 -0
- package/dist/src/integrations/mcp/refactored-server.js.map +1 -0
- package/dist/src/integrations/mcp/server.js +2 -2
- package/dist/src/integrations/mcp/server.js.map +1 -1
- package/dist/src/integrations/mcp/tool-definitions.d.ts +44 -0
- package/dist/src/integrations/mcp/tool-definitions.d.ts.map +1 -0
- package/dist/src/integrations/mcp/tool-definitions.js +563 -0
- package/dist/src/integrations/mcp/tool-definitions.js.map +1 -0
- package/dist/src/integrations/pg-aiguide/embedding-provider.d.ts +48 -0
- package/dist/src/integrations/pg-aiguide/embedding-provider.d.ts.map +1 -0
- package/dist/src/integrations/pg-aiguide/embedding-provider.js +190 -0
- package/dist/src/integrations/pg-aiguide/embedding-provider.js.map +1 -0
- package/dist/src/integrations/pg-aiguide/semantic-search.d.ts +34 -0
- package/dist/src/integrations/pg-aiguide/semantic-search.d.ts.map +1 -0
- package/dist/src/integrations/pg-aiguide/semantic-search.js +176 -0
- package/dist/src/integrations/pg-aiguide/semantic-search.js.map +1 -0
- package/dist/src/integrations/pg-aiguide/timescale-analytics.d.ts +44 -0
- package/dist/src/integrations/pg-aiguide/timescale-analytics.d.ts.map +1 -0
- package/dist/src/integrations/pg-aiguide/timescale-analytics.js +215 -0
- package/dist/src/integrations/pg-aiguide/timescale-analytics.js.map +1 -0
- package/dist/src/mcp/stackmemory-mcp-server.d.ts +9 -0
- package/dist/src/mcp/stackmemory-mcp-server.d.ts.map +1 -0
- package/dist/src/mcp/stackmemory-mcp-server.js +519 -0
- package/dist/src/mcp/stackmemory-mcp-server.js.map +1 -0
- package/dist/src/middleware/exponential-rate-limiter.d.ts +78 -0
- package/dist/src/middleware/exponential-rate-limiter.d.ts.map +1 -0
- package/dist/src/middleware/exponential-rate-limiter.js +293 -0
- package/dist/src/middleware/exponential-rate-limiter.js.map +1 -0
- package/dist/src/models/user.model.d.ts +62 -0
- package/dist/src/models/user.model.d.ts.map +1 -0
- package/dist/src/models/user.model.js +311 -0
- package/dist/src/models/user.model.js.map +1 -0
- package/dist/src/servers/production/auth-middleware.d.ts +12 -2
- package/dist/src/servers/production/auth-middleware.d.ts.map +1 -1
- package/dist/src/servers/production/auth-middleware.js +240 -28
- package/dist/src/servers/production/auth-middleware.js.map +1 -1
- package/dist/src/servers/railway/index.js.map +1 -1
- package/dist/src/services/context-service.d.ts.map +1 -1
- package/dist/src/services/context-service.js +86 -1
- package/dist/src/services/context-service.js.map +1 -1
- package/dist/src/validation/schemas.d.ts +633 -0
- package/dist/src/validation/schemas.d.ts.map +1 -0
- package/dist/src/validation/schemas.js +347 -0
- package/dist/src/validation/schemas.js.map +1 -0
- package/dist/types/task.js +1 -0
- package/dist/types/task.js.map +7 -0
- package/dist/utils/logger.js +52 -0
- package/dist/utils/logger.js.map +7 -0
- package/dist/validation/schemas.js +218 -0
- package/dist/validation/schemas.js.map +7 -0
- package/package.json +12 -3
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { logger } from "../../core/monitoring/logger.js";
|
|
2
|
+
import crypto from "crypto";
|
|
3
|
+
class OpenAIEmbeddingProvider {
|
|
4
|
+
apiKey;
|
|
5
|
+
model;
|
|
6
|
+
dimensions;
|
|
7
|
+
constructor(model = "text-embedding-ada-002") {
|
|
8
|
+
this.apiKey = process.env.OPENAI_API_KEY;
|
|
9
|
+
this.model = model;
|
|
10
|
+
this.dimensions = model === "text-embedding-ada-002" ? 1536 : 3072;
|
|
11
|
+
}
|
|
12
|
+
async createEmbedding(text) {
|
|
13
|
+
if (!this.apiKey) {
|
|
14
|
+
throw new Error("OPENAI_API_KEY environment variable is not set");
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
const response = await fetch("https://api.openai.com/v1/embeddings", {
|
|
18
|
+
method: "POST",
|
|
19
|
+
headers: {
|
|
20
|
+
"Content-Type": "application/json",
|
|
21
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
22
|
+
},
|
|
23
|
+
body: JSON.stringify({
|
|
24
|
+
input: text,
|
|
25
|
+
model: this.model
|
|
26
|
+
})
|
|
27
|
+
});
|
|
28
|
+
if (!response.ok) {
|
|
29
|
+
throw new Error(`OpenAI API error: ${response.statusText}`);
|
|
30
|
+
}
|
|
31
|
+
const data = await response.json();
|
|
32
|
+
return data.data[0].embedding;
|
|
33
|
+
} catch (error) {
|
|
34
|
+
logger.error(
|
|
35
|
+
"Failed to create OpenAI embedding",
|
|
36
|
+
error instanceof Error ? error : new Error(String(error))
|
|
37
|
+
);
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
getDimensions() {
|
|
42
|
+
return this.dimensions;
|
|
43
|
+
}
|
|
44
|
+
getName() {
|
|
45
|
+
return `OpenAI-${this.model}`;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
class LocalEmbeddingProvider {
|
|
49
|
+
dimensions;
|
|
50
|
+
vocabulary = /* @__PURE__ */ new Map();
|
|
51
|
+
idf = /* @__PURE__ */ new Map();
|
|
52
|
+
documentCount = 0;
|
|
53
|
+
constructor(dimensions = 384) {
|
|
54
|
+
this.dimensions = dimensions;
|
|
55
|
+
}
|
|
56
|
+
tokenize(text) {
|
|
57
|
+
return text.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/).filter((token) => token.length > 2);
|
|
58
|
+
}
|
|
59
|
+
getWordVector(word) {
|
|
60
|
+
const hash = crypto.createHash("sha256").update(word).digest();
|
|
61
|
+
const vector = new Array(this.dimensions).fill(0);
|
|
62
|
+
for (let i = 0; i < Math.min(hash.length, this.dimensions); i++) {
|
|
63
|
+
const value = (hash[i] - 128) / 128;
|
|
64
|
+
vector[i] = value;
|
|
65
|
+
}
|
|
66
|
+
return vector;
|
|
67
|
+
}
|
|
68
|
+
async createEmbedding(text) {
|
|
69
|
+
const tokens = this.tokenize(text);
|
|
70
|
+
const vector = new Array(this.dimensions).fill(0);
|
|
71
|
+
if (tokens.length === 0) {
|
|
72
|
+
return vector;
|
|
73
|
+
}
|
|
74
|
+
const termFreq = /* @__PURE__ */ new Map();
|
|
75
|
+
for (const token of tokens) {
|
|
76
|
+
termFreq.set(token, (termFreq.get(token) || 0) + 1);
|
|
77
|
+
}
|
|
78
|
+
this.documentCount++;
|
|
79
|
+
for (const token of new Set(tokens)) {
|
|
80
|
+
if (!this.vocabulary.has(token)) {
|
|
81
|
+
this.vocabulary.set(token, this.vocabulary.size);
|
|
82
|
+
}
|
|
83
|
+
this.idf.set(token, (this.idf.get(token) || 0) + 1);
|
|
84
|
+
}
|
|
85
|
+
let totalWeight = 0;
|
|
86
|
+
for (const [token, freq] of termFreq.entries()) {
|
|
87
|
+
const tf = freq / tokens.length;
|
|
88
|
+
const docFreq = this.idf.get(token) || 1;
|
|
89
|
+
const idf = Math.log((this.documentCount + 1) / (docFreq + 1));
|
|
90
|
+
const weight = tf * idf;
|
|
91
|
+
const wordVector = this.getWordVector(token);
|
|
92
|
+
for (let i = 0; i < this.dimensions; i++) {
|
|
93
|
+
vector[i] += wordVector[i] * weight;
|
|
94
|
+
}
|
|
95
|
+
totalWeight += weight;
|
|
96
|
+
}
|
|
97
|
+
if (totalWeight > 0) {
|
|
98
|
+
const magnitude = Math.sqrt(
|
|
99
|
+
vector.reduce((sum, val) => sum + val * val, 0)
|
|
100
|
+
);
|
|
101
|
+
if (magnitude > 0) {
|
|
102
|
+
for (let i = 0; i < this.dimensions; i++) {
|
|
103
|
+
vector[i] /= magnitude;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return vector;
|
|
108
|
+
}
|
|
109
|
+
getDimensions() {
|
|
110
|
+
return this.dimensions;
|
|
111
|
+
}
|
|
112
|
+
getName() {
|
|
113
|
+
return "Local-TFIDF";
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
class HybridEmbeddingProvider {
|
|
117
|
+
openai;
|
|
118
|
+
local;
|
|
119
|
+
useOpenAI;
|
|
120
|
+
constructor(dimensions = 1536) {
|
|
121
|
+
this.openai = new OpenAIEmbeddingProvider();
|
|
122
|
+
this.local = new LocalEmbeddingProvider(dimensions);
|
|
123
|
+
this.useOpenAI = !!process.env.OPENAI_API_KEY;
|
|
124
|
+
if (!this.useOpenAI) {
|
|
125
|
+
logger.warn("OPENAI_API_KEY not set, using local embeddings");
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async createEmbedding(text) {
|
|
129
|
+
if (this.useOpenAI) {
|
|
130
|
+
try {
|
|
131
|
+
return await this.openai.createEmbedding(text);
|
|
132
|
+
} catch (error) {
|
|
133
|
+
logger.warn(
|
|
134
|
+
"OpenAI embedding failed, falling back to local",
|
|
135
|
+
error instanceof Error ? error : void 0
|
|
136
|
+
);
|
|
137
|
+
this.useOpenAI = false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const localEmbedding = await this.local.createEmbedding(text);
|
|
141
|
+
const targetDimensions = this.getDimensions();
|
|
142
|
+
if (localEmbedding.length < targetDimensions) {
|
|
143
|
+
return [
|
|
144
|
+
...localEmbedding,
|
|
145
|
+
...new Array(targetDimensions - localEmbedding.length).fill(0)
|
|
146
|
+
];
|
|
147
|
+
}
|
|
148
|
+
return localEmbedding.slice(0, targetDimensions);
|
|
149
|
+
}
|
|
150
|
+
getDimensions() {
|
|
151
|
+
return this.useOpenAI ? this.openai.getDimensions() : this.local.getDimensions();
|
|
152
|
+
}
|
|
153
|
+
getName() {
|
|
154
|
+
return this.useOpenAI ? this.openai.getName() : `Hybrid-${this.local.getName()}`;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function createEmbeddingProvider(type) {
|
|
158
|
+
switch (type) {
|
|
159
|
+
case "openai":
|
|
160
|
+
return new OpenAIEmbeddingProvider();
|
|
161
|
+
case "local":
|
|
162
|
+
return new LocalEmbeddingProvider();
|
|
163
|
+
case "hybrid":
|
|
164
|
+
default:
|
|
165
|
+
return new HybridEmbeddingProvider();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
export {
|
|
169
|
+
HybridEmbeddingProvider,
|
|
170
|
+
LocalEmbeddingProvider,
|
|
171
|
+
OpenAIEmbeddingProvider,
|
|
172
|
+
createEmbeddingProvider
|
|
173
|
+
};
|
|
174
|
+
//# sourceMappingURL=embedding-provider.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/integrations/pg-aiguide/embedding-provider.ts"],
|
|
4
|
+
"sourcesContent": ["import { logger } from '../../core/monitoring/logger.js';\nimport crypto from 'crypto';\n\nexport interface EmbeddingProvider {\n createEmbedding(text: string): Promise<number[]>;\n getDimensions(): number;\n getName(): string;\n}\n\n/**\n * OpenAI Embeddings Provider\n * Requires OPENAI_API_KEY environment variable\n */\nexport class OpenAIEmbeddingProvider implements EmbeddingProvider {\n private apiKey: string | undefined;\n private model: string;\n private dimensions: number;\n\n constructor(model = 'text-embedding-ada-002') {\n this.apiKey = process.env.OPENAI_API_KEY;\n this.model = model;\n this.dimensions = model === 'text-embedding-ada-002' ? 1536 : 3072; // ada-002 vs text-embedding-3-small\n }\n\n async createEmbedding(text: string): Promise<number[]> {\n if (!this.apiKey) {\n throw new Error('OPENAI_API_KEY environment variable is not set');\n }\n\n try {\n const response = await fetch('https://api.openai.com/v1/embeddings', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({\n input: text,\n model: this.model,\n }),\n });\n\n if (!response.ok) {\n throw new Error(`OpenAI API error: ${response.statusText}`);\n }\n\n const data = (await response.json()) as {\n data: Array<{ embedding: number[] }>;\n };\n return data.data[0].embedding;\n } catch (error) {\n logger.error(\n 'Failed to create OpenAI embedding',\n error instanceof Error ? error : new Error(String(error))\n );\n throw error;\n }\n }\n\n getDimensions(): number {\n return this.dimensions;\n }\n\n getName(): string {\n return `OpenAI-${this.model}`;\n }\n}\n\n/**\n * Local Embeddings Provider using simple TF-IDF-like approach\n * Deterministic and doesn't require external APIs\n */\nexport class LocalEmbeddingProvider implements EmbeddingProvider {\n private dimensions: number;\n private vocabulary: Map<string, number> = new Map();\n private idf: Map<string, number> = new Map();\n private documentCount = 0;\n\n constructor(dimensions = 384) {\n this.dimensions = dimensions;\n }\n\n private tokenize(text: string): string[] {\n return text\n .toLowerCase()\n .replace(/[^\\w\\s]/g, ' ')\n .split(/\\s+/)\n .filter((token) => token.length > 2);\n }\n\n private getWordVector(word: string): number[] {\n // Use deterministic hashing to create a vector for each word\n const hash = crypto.createHash('sha256').update(word).digest();\n const vector = new Array(this.dimensions).fill(0);\n\n // Use hash bytes to set vector components\n for (let i = 0; i < Math.min(hash.length, this.dimensions); i++) {\n const value = (hash[i] - 128) / 128; // Normalize to [-1, 1]\n vector[i] = value;\n }\n\n return vector;\n }\n\n async createEmbedding(text: string): Promise<number[]> {\n const tokens = this.tokenize(text);\n const vector = new Array(this.dimensions).fill(0);\n\n if (tokens.length === 0) {\n return vector;\n }\n\n // Calculate TF-IDF weighted average of word vectors\n const termFreq = new Map<string, number>();\n\n // Count term frequencies\n for (const token of tokens) {\n termFreq.set(token, (termFreq.get(token) || 0) + 1);\n }\n\n // Build vocabulary and update document frequency\n this.documentCount++;\n for (const token of new Set(tokens)) {\n if (!this.vocabulary.has(token)) {\n this.vocabulary.set(token, this.vocabulary.size);\n }\n this.idf.set(token, (this.idf.get(token) || 0) + 1);\n }\n\n // Calculate weighted vector\n let totalWeight = 0;\n\n for (const [token, freq] of termFreq.entries()) {\n const tf = freq / tokens.length;\n const docFreq = this.idf.get(token) || 1;\n const idf = Math.log((this.documentCount + 1) / (docFreq + 1));\n const weight = tf * idf;\n\n const wordVector = this.getWordVector(token);\n for (let i = 0; i < this.dimensions; i++) {\n vector[i] += wordVector[i] * weight;\n }\n totalWeight += weight;\n }\n\n // Normalize the vector\n if (totalWeight > 0) {\n const magnitude = Math.sqrt(\n vector.reduce((sum, val) => sum + val * val, 0)\n );\n if (magnitude > 0) {\n for (let i = 0; i < this.dimensions; i++) {\n vector[i] /= magnitude;\n }\n }\n }\n\n return vector;\n }\n\n getDimensions(): number {\n return this.dimensions;\n }\n\n getName(): string {\n return 'Local-TFIDF';\n }\n}\n\n/**\n * Hybrid provider that tries OpenAI first, falls back to local\n */\nexport class HybridEmbeddingProvider implements EmbeddingProvider {\n private openai: OpenAIEmbeddingProvider;\n private local: LocalEmbeddingProvider;\n private useOpenAI: boolean;\n\n constructor(dimensions = 1536) {\n this.openai = new OpenAIEmbeddingProvider();\n this.local = new LocalEmbeddingProvider(dimensions);\n this.useOpenAI = !!process.env.OPENAI_API_KEY;\n\n if (!this.useOpenAI) {\n logger.warn('OPENAI_API_KEY not set, using local embeddings');\n }\n }\n\n async createEmbedding(text: string): Promise<number[]> {\n if (this.useOpenAI) {\n try {\n return await this.openai.createEmbedding(text);\n } catch (error) {\n logger.warn(\n 'OpenAI embedding failed, falling back to local',\n error instanceof Error ? error : undefined\n );\n this.useOpenAI = false; // Disable for future calls\n }\n }\n\n const localEmbedding = await this.local.createEmbedding(text);\n\n // Pad or truncate to match expected dimensions\n const targetDimensions = this.getDimensions();\n if (localEmbedding.length < targetDimensions) {\n return [\n ...localEmbedding,\n ...new Array(targetDimensions - localEmbedding.length).fill(0),\n ];\n }\n return localEmbedding.slice(0, targetDimensions);\n }\n\n getDimensions(): number {\n return this.useOpenAI\n ? this.openai.getDimensions()\n : this.local.getDimensions();\n }\n\n getName(): string {\n return this.useOpenAI\n ? this.openai.getName()\n : `Hybrid-${this.local.getName()}`;\n }\n}\n\n// Factory function\nexport function createEmbeddingProvider(\n type?: 'openai' | 'local' | 'hybrid'\n): EmbeddingProvider {\n switch (type) {\n case 'openai':\n return new OpenAIEmbeddingProvider();\n case 'local':\n return new LocalEmbeddingProvider();\n case 'hybrid':\n default:\n return new HybridEmbeddingProvider();\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { logger } from "../../core/monitoring/logger.js";
|
|
2
|
+
import {
|
|
3
|
+
createEmbeddingProvider
|
|
4
|
+
} from "./embedding-provider.js";
|
|
5
|
+
import { sanitizeSQLIdentifier } from "../../validation/schemas.js";
|
|
6
|
+
class SemanticSearch {
|
|
7
|
+
pool;
|
|
8
|
+
config;
|
|
9
|
+
embeddingProvider;
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.pool = config.pool;
|
|
12
|
+
this.config = config;
|
|
13
|
+
this.embeddingProvider = config.embeddingProvider || createEmbeddingProvider("hybrid");
|
|
14
|
+
if (this.embeddingProvider.getDimensions() !== config.vectorDimensions) {
|
|
15
|
+
logger.warn(
|
|
16
|
+
`Embedding provider dimensions (${this.embeddingProvider.getDimensions()}) don't match config (${config.vectorDimensions}). Using provider dimensions.`
|
|
17
|
+
);
|
|
18
|
+
this.config.vectorDimensions = this.embeddingProvider.getDimensions();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async createEmbedding(text) {
|
|
22
|
+
return this.embeddingProvider.createEmbedding(text);
|
|
23
|
+
}
|
|
24
|
+
async indexContent(id, content, metadata) {
|
|
25
|
+
const embedding = await this.createEmbedding(content);
|
|
26
|
+
const query = `
|
|
27
|
+
INSERT INTO ${this.config.tableName} (id, ${this.config.contentColumn}, ${this.config.embeddingColumn}, metadata)
|
|
28
|
+
VALUES ($1, $2, $3, $4)
|
|
29
|
+
ON CONFLICT (id) DO UPDATE
|
|
30
|
+
SET ${this.config.contentColumn} = $2,
|
|
31
|
+
${this.config.embeddingColumn} = $3,
|
|
32
|
+
metadata = $4
|
|
33
|
+
`;
|
|
34
|
+
await this.pool.query(query, [
|
|
35
|
+
id,
|
|
36
|
+
content,
|
|
37
|
+
`[${embedding.join(",")}]`,
|
|
38
|
+
metadata ? JSON.stringify(metadata) : null
|
|
39
|
+
]);
|
|
40
|
+
}
|
|
41
|
+
async search(query, limit = 10, threshold = 0.7) {
|
|
42
|
+
const queryEmbedding = await this.createEmbedding(query);
|
|
43
|
+
const sanitizedTable = sanitizeSQLIdentifier(this.config.tableName);
|
|
44
|
+
const sanitizedContent = sanitizeSQLIdentifier(this.config.contentColumn);
|
|
45
|
+
const sanitizedEmbedding = sanitizeSQLIdentifier(
|
|
46
|
+
this.config.embeddingColumn
|
|
47
|
+
);
|
|
48
|
+
const searchQuery = `
|
|
49
|
+
SELECT
|
|
50
|
+
id,
|
|
51
|
+
${sanitizedContent} as content,
|
|
52
|
+
metadata,
|
|
53
|
+
1 - (${sanitizedEmbedding} <=> $1::vector) as similarity
|
|
54
|
+
FROM ${sanitizedTable}
|
|
55
|
+
WHERE 1 - (${sanitizedEmbedding} <=> $1::vector) > $2
|
|
56
|
+
ORDER BY ${sanitizedEmbedding} <=> $1::vector
|
|
57
|
+
LIMIT $3
|
|
58
|
+
`;
|
|
59
|
+
const result = await this.pool.query(searchQuery, [
|
|
60
|
+
`[${queryEmbedding.join(",")}]`,
|
|
61
|
+
threshold,
|
|
62
|
+
limit
|
|
63
|
+
]);
|
|
64
|
+
return result.rows.map((row) => ({
|
|
65
|
+
id: row.id,
|
|
66
|
+
content: row.content,
|
|
67
|
+
similarity: row.similarity,
|
|
68
|
+
metadata: row.metadata
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
async findSimilar(id, limit = 10) {
|
|
72
|
+
const sanitizedTable = sanitizeSQLIdentifier(this.config.tableName);
|
|
73
|
+
const sanitizedContent = sanitizeSQLIdentifier(this.config.contentColumn);
|
|
74
|
+
const sanitizedEmbedding = sanitizeSQLIdentifier(
|
|
75
|
+
this.config.embeddingColumn
|
|
76
|
+
);
|
|
77
|
+
const query = `
|
|
78
|
+
WITH target AS (
|
|
79
|
+
SELECT ${sanitizedEmbedding} as embedding
|
|
80
|
+
FROM ${sanitizedTable}
|
|
81
|
+
WHERE id = $1
|
|
82
|
+
)
|
|
83
|
+
SELECT
|
|
84
|
+
t.id,
|
|
85
|
+
t.${sanitizedContent} as content,
|
|
86
|
+
t.metadata,
|
|
87
|
+
1 - (t.${sanitizedEmbedding} <=> target.embedding) as similarity
|
|
88
|
+
FROM ${sanitizedTable} t, target
|
|
89
|
+
WHERE t.id != $1
|
|
90
|
+
ORDER BY t.${this.config.embeddingColumn} <=> target.embedding
|
|
91
|
+
LIMIT $2
|
|
92
|
+
`;
|
|
93
|
+
const result = await this.pool.query(query, [id, limit]);
|
|
94
|
+
return result.rows.map((row) => ({
|
|
95
|
+
id: row.id,
|
|
96
|
+
content: row.content,
|
|
97
|
+
similarity: row.similarity,
|
|
98
|
+
metadata: row.metadata
|
|
99
|
+
}));
|
|
100
|
+
}
|
|
101
|
+
async cluster(k, maxIterations = 10) {
|
|
102
|
+
const sanitizedTable = sanitizeSQLIdentifier(this.config.tableName);
|
|
103
|
+
const sanitizedContent = sanitizeSQLIdentifier(this.config.contentColumn);
|
|
104
|
+
const sanitizedEmbedding = sanitizeSQLIdentifier(
|
|
105
|
+
this.config.embeddingColumn
|
|
106
|
+
);
|
|
107
|
+
const query = `
|
|
108
|
+
WITH clusters AS (
|
|
109
|
+
SELECT
|
|
110
|
+
id,
|
|
111
|
+
${sanitizedContent} as content,
|
|
112
|
+
metadata,
|
|
113
|
+
kmeans(${sanitizedEmbedding}, $1, $2) OVER () as cluster_id
|
|
114
|
+
FROM ${sanitizedTable}
|
|
115
|
+
)
|
|
116
|
+
SELECT * FROM clusters ORDER BY cluster_id
|
|
117
|
+
`;
|
|
118
|
+
const result = await this.pool.query(query, [k, maxIterations]);
|
|
119
|
+
const clusterMap = /* @__PURE__ */ new Map();
|
|
120
|
+
for (const row of result.rows) {
|
|
121
|
+
const clusterId = row.cluster_id;
|
|
122
|
+
if (!clusterMap.has(clusterId)) {
|
|
123
|
+
clusterMap.set(clusterId, []);
|
|
124
|
+
}
|
|
125
|
+
clusterMap.get(clusterId).push({
|
|
126
|
+
id: row.id,
|
|
127
|
+
content: row.content,
|
|
128
|
+
similarity: 1,
|
|
129
|
+
// Cluster membership
|
|
130
|
+
metadata: row.metadata
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return clusterMap;
|
|
134
|
+
}
|
|
135
|
+
async reindex() {
|
|
136
|
+
const sanitizedTable = sanitizeSQLIdentifier(this.config.tableName);
|
|
137
|
+
const indexName = `idx_${sanitizedTable}_embedding`;
|
|
138
|
+
const sanitizedIndex = sanitizeSQLIdentifier(indexName);
|
|
139
|
+
const query = `REINDEX INDEX CONCURRENTLY ${sanitizedIndex}`;
|
|
140
|
+
try {
|
|
141
|
+
await this.pool.query(query);
|
|
142
|
+
logger.info(`Reindexed ${sanitizedTable} embeddings`);
|
|
143
|
+
} catch (error) {
|
|
144
|
+
logger.error(
|
|
145
|
+
"Failed to reindex embeddings",
|
|
146
|
+
error instanceof Error ? error : void 0
|
|
147
|
+
);
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async getStats() {
|
|
152
|
+
const sanitizedTable = sanitizeSQLIdentifier(this.config.tableName);
|
|
153
|
+
const sanitizedEmbedding = sanitizeSQLIdentifier(
|
|
154
|
+
this.config.embeddingColumn
|
|
155
|
+
);
|
|
156
|
+
const indexName = `idx_${sanitizedTable}_embedding`;
|
|
157
|
+
const statsQuery = `
|
|
158
|
+
SELECT
|
|
159
|
+
COUNT(*) as total,
|
|
160
|
+
AVG(
|
|
161
|
+
1 - (${sanitizedEmbedding} <=> (
|
|
162
|
+
SELECT AVG(${sanitizedEmbedding})::vector
|
|
163
|
+
FROM ${sanitizedTable}
|
|
164
|
+
))
|
|
165
|
+
) as avg_similarity,
|
|
166
|
+
pg_size_pretty(
|
|
167
|
+
pg_relation_size($1::regclass)
|
|
168
|
+
) as index_size
|
|
169
|
+
FROM ${sanitizedTable}
|
|
170
|
+
`;
|
|
171
|
+
const result = await this.pool.query(statsQuery, [indexName]);
|
|
172
|
+
const row = result.rows[0];
|
|
173
|
+
return {
|
|
174
|
+
totalDocuments: parseInt(row.total),
|
|
175
|
+
avgSimilarity: parseFloat(row.avg_similarity) || 0,
|
|
176
|
+
indexSize: row.index_size || "0 bytes"
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
export {
|
|
181
|
+
SemanticSearch
|
|
182
|
+
};
|
|
183
|
+
//# sourceMappingURL=semantic-search.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/integrations/pg-aiguide/semantic-search.ts"],
|
|
4
|
+
"sourcesContent": ["import { Pool } from 'pg';\nimport { logger } from '../../core/monitoring/logger.js';\nimport {\n EmbeddingProvider,\n createEmbeddingProvider,\n} from './embedding-provider.js';\nimport { sanitizeSQLIdentifier } from '../../validation/schemas.js';\n\nexport interface SemanticSearchConfig {\n pool: Pool;\n tableName: string;\n embeddingColumn: string;\n contentColumn: string;\n vectorDimensions: number;\n embeddingProvider?: EmbeddingProvider;\n}\n\nexport interface SearchResult {\n id: string;\n content: string;\n similarity: number;\n metadata?: Record<string, any>;\n}\n\nexport class SemanticSearch {\n private pool: Pool;\n private config: SemanticSearchConfig;\n private embeddingProvider: EmbeddingProvider;\n\n constructor(config: SemanticSearchConfig) {\n this.pool = config.pool;\n this.config = config;\n this.embeddingProvider =\n config.embeddingProvider || createEmbeddingProvider('hybrid');\n\n // Verify dimensions match\n if (this.embeddingProvider.getDimensions() !== config.vectorDimensions) {\n logger.warn(\n `Embedding provider dimensions (${this.embeddingProvider.getDimensions()}) ` +\n `don't match config (${config.vectorDimensions}). Using provider dimensions.`\n );\n this.config.vectorDimensions = this.embeddingProvider.getDimensions();\n }\n }\n\n async createEmbedding(text: string): Promise<number[]> {\n return this.embeddingProvider.createEmbedding(text);\n }\n\n async indexContent(\n id: string,\n content: string,\n metadata?: Record<string, any>\n ): Promise<void> {\n const embedding = await this.createEmbedding(content);\n\n const query = `\n INSERT INTO ${this.config.tableName} (id, ${this.config.contentColumn}, ${this.config.embeddingColumn}, metadata)\n VALUES ($1, $2, $3, $4)\n ON CONFLICT (id) DO UPDATE\n SET ${this.config.contentColumn} = $2,\n ${this.config.embeddingColumn} = $3,\n metadata = $4\n `;\n\n await this.pool.query(query, [\n id,\n content,\n `[${embedding.join(',')}]`,\n metadata ? JSON.stringify(metadata) : null,\n ]);\n }\n\n async search(\n query: string,\n limit = 10,\n threshold = 0.7\n ): Promise<SearchResult[]> {\n const queryEmbedding = await this.createEmbedding(query);\n\n // Sanitize all identifiers to prevent SQL injection\n const sanitizedTable = sanitizeSQLIdentifier(this.config.tableName);\n const sanitizedContent = sanitizeSQLIdentifier(this.config.contentColumn);\n const sanitizedEmbedding = sanitizeSQLIdentifier(\n this.config.embeddingColumn\n );\n\n const searchQuery = `\n SELECT \n id,\n ${sanitizedContent} as content,\n metadata,\n 1 - (${sanitizedEmbedding} <=> $1::vector) as similarity\n FROM ${sanitizedTable}\n WHERE 1 - (${sanitizedEmbedding} <=> $1::vector) > $2\n ORDER BY ${sanitizedEmbedding} <=> $1::vector\n LIMIT $3\n `;\n\n const result = await this.pool.query(searchQuery, [\n `[${queryEmbedding.join(',')}]`,\n threshold,\n limit,\n ]);\n\n return result.rows.map((row: any) => ({\n id: row.id,\n content: row.content,\n similarity: row.similarity,\n metadata: row.metadata,\n }));\n }\n\n async findSimilar(id: string, limit = 10): Promise<SearchResult[]> {\n // Sanitize all identifiers to prevent SQL injection\n const sanitizedTable = sanitizeSQLIdentifier(this.config.tableName);\n const sanitizedContent = sanitizeSQLIdentifier(this.config.contentColumn);\n const sanitizedEmbedding = sanitizeSQLIdentifier(\n this.config.embeddingColumn\n );\n\n const query = `\n WITH target AS (\n SELECT ${sanitizedEmbedding} as embedding\n FROM ${sanitizedTable}\n WHERE id = $1\n )\n SELECT \n t.id,\n t.${sanitizedContent} as content,\n t.metadata,\n 1 - (t.${sanitizedEmbedding} <=> target.embedding) as similarity\n FROM ${sanitizedTable} t, target\n WHERE t.id != $1\n ORDER BY t.${this.config.embeddingColumn} <=> target.embedding\n LIMIT $2\n `;\n\n const result = await this.pool.query(query, [id, limit]);\n\n return result.rows.map((row: any) => ({\n id: row.id,\n content: row.content,\n similarity: row.similarity,\n metadata: row.metadata,\n }));\n }\n\n async cluster(\n k: number,\n maxIterations = 10\n ): Promise<Map<number, SearchResult[]>> {\n // Sanitize all identifiers to prevent SQL injection\n const sanitizedTable = sanitizeSQLIdentifier(this.config.tableName);\n const sanitizedContent = sanitizeSQLIdentifier(this.config.contentColumn);\n const sanitizedEmbedding = sanitizeSQLIdentifier(\n this.config.embeddingColumn\n );\n\n // K-means clustering using pgvector\n const query = `\n WITH clusters AS (\n SELECT \n id,\n ${sanitizedContent} as content,\n metadata,\n kmeans(${sanitizedEmbedding}, $1, $2) OVER () as cluster_id\n FROM ${sanitizedTable}\n )\n SELECT * FROM clusters ORDER BY cluster_id\n `;\n\n const result = await this.pool.query(query, [k, maxIterations]);\n\n const clusterMap = new Map<number, SearchResult[]>();\n\n for (const row of result.rows) {\n const clusterId = row.cluster_id;\n if (!clusterMap.has(clusterId)) {\n clusterMap.set(clusterId, []);\n }\n\n clusterMap.get(clusterId)!.push({\n id: row.id,\n content: row.content,\n similarity: 1.0, // Cluster membership\n metadata: row.metadata,\n });\n }\n\n return clusterMap;\n }\n\n async reindex(): Promise<void> {\n // Sanitize table name to prevent SQL injection\n const sanitizedTable = sanitizeSQLIdentifier(this.config.tableName);\n\n // Rebuild the IVFFlat index for better performance\n // Using sanitized identifier prevents SQL injection\n const indexName = `idx_${sanitizedTable}_embedding`;\n const sanitizedIndex = sanitizeSQLIdentifier(indexName);\n\n const query = `REINDEX INDEX CONCURRENTLY ${sanitizedIndex}`;\n\n try {\n await this.pool.query(query);\n logger.info(`Reindexed ${sanitizedTable} embeddings`);\n } catch (error) {\n logger.error(\n 'Failed to reindex embeddings',\n error instanceof Error ? error : undefined\n );\n throw error;\n }\n }\n\n async getStats(): Promise<{\n totalDocuments: number;\n avgSimilarity: number;\n indexSize: string;\n }> {\n // Sanitize all identifiers to prevent SQL injection\n const sanitizedTable = sanitizeSQLIdentifier(this.config.tableName);\n const sanitizedEmbedding = sanitizeSQLIdentifier(\n this.config.embeddingColumn\n );\n const indexName = `idx_${sanitizedTable}_embedding`;\n\n const statsQuery = `\n SELECT \n COUNT(*) as total,\n AVG(\n 1 - (${sanitizedEmbedding} <=> (\n SELECT AVG(${sanitizedEmbedding})::vector \n FROM ${sanitizedTable}\n ))\n ) as avg_similarity,\n pg_size_pretty(\n pg_relation_size($1::regclass)\n ) as index_size\n FROM ${sanitizedTable}\n `;\n\n const result = await this.pool.query(statsQuery, [indexName]);\n const row: any = result.rows[0];\n\n return {\n totalDocuments: parseInt(row.total),\n avgSimilarity: parseFloat(row.avg_similarity) || 0,\n indexSize: row.index_size || '0 bytes',\n };\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { logger } from "../../core/monitoring/logger.js";
|
|
2
|
+
class TimescaleAnalytics {
|
|
3
|
+
pool;
|
|
4
|
+
config;
|
|
5
|
+
constructor(config) {
|
|
6
|
+
this.pool = config.pool;
|
|
7
|
+
this.config = config;
|
|
8
|
+
}
|
|
9
|
+
async createContinuousAggregate(name, interval, columns) {
|
|
10
|
+
const aggregates = columns.map(
|
|
11
|
+
(col) => `
|
|
12
|
+
AVG(${col}) as avg_${col},
|
|
13
|
+
MIN(${col}) as min_${col},
|
|
14
|
+
MAX(${col}) as max_${col},
|
|
15
|
+
SUM(${col}) as sum_${col}
|
|
16
|
+
`
|
|
17
|
+
).join(",\n ");
|
|
18
|
+
const query = `
|
|
19
|
+
CREATE MATERIALIZED VIEW IF NOT EXISTS ${name}
|
|
20
|
+
WITH (timescaledb.continuous) AS
|
|
21
|
+
SELECT
|
|
22
|
+
time_bucket('${interval}', ${this.config.timeColumn}) as bucket,
|
|
23
|
+
COUNT(*) as count,
|
|
24
|
+
${aggregates}
|
|
25
|
+
FROM ${this.config.tableName}
|
|
26
|
+
GROUP BY bucket
|
|
27
|
+
WITH NO DATA
|
|
28
|
+
`;
|
|
29
|
+
await this.pool.query(query);
|
|
30
|
+
await this.pool.query(`
|
|
31
|
+
SELECT add_continuous_aggregate_policy('${name}',
|
|
32
|
+
start_offset => INTERVAL '1 month',
|
|
33
|
+
end_offset => INTERVAL '1 hour',
|
|
34
|
+
schedule_interval => INTERVAL '1 hour',
|
|
35
|
+
if_not_exists => TRUE
|
|
36
|
+
)
|
|
37
|
+
`);
|
|
38
|
+
logger.info(`Created continuous aggregate: ${name}`);
|
|
39
|
+
}
|
|
40
|
+
async getTimeSeries(startTime, endTime, interval, columns) {
|
|
41
|
+
const selectedColumns = columns || this.config.valueColumns;
|
|
42
|
+
const aggregates = selectedColumns.map(
|
|
43
|
+
(col) => `
|
|
44
|
+
AVG(${col})::float as avg_${col},
|
|
45
|
+
MIN(${col})::float as min_${col},
|
|
46
|
+
MAX(${col})::float as max_${col},
|
|
47
|
+
SUM(${col})::float as sum_${col}
|
|
48
|
+
`
|
|
49
|
+
).join(",\n ");
|
|
50
|
+
const query = `
|
|
51
|
+
SELECT
|
|
52
|
+
time_bucket($1, ${this.config.timeColumn}) as period,
|
|
53
|
+
COUNT(*)::int as count,
|
|
54
|
+
${aggregates}
|
|
55
|
+
FROM ${this.config.tableName}
|
|
56
|
+
WHERE ${this.config.timeColumn} >= $2
|
|
57
|
+
AND ${this.config.timeColumn} <= $3
|
|
58
|
+
GROUP BY period
|
|
59
|
+
ORDER BY period
|
|
60
|
+
`;
|
|
61
|
+
const result = await this.pool.query(query, [interval, startTime, endTime]);
|
|
62
|
+
return result.rows.map((row) => {
|
|
63
|
+
const avg = {};
|
|
64
|
+
const min = {};
|
|
65
|
+
const max = {};
|
|
66
|
+
const sum = {};
|
|
67
|
+
selectedColumns.forEach((col) => {
|
|
68
|
+
avg[col] = row[`avg_${col}`];
|
|
69
|
+
min[col] = row[`min_${col}`];
|
|
70
|
+
max[col] = row[`max_${col}`];
|
|
71
|
+
sum[col] = row[`sum_${col}`];
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
period: row.period,
|
|
75
|
+
count: row.count,
|
|
76
|
+
avg,
|
|
77
|
+
min,
|
|
78
|
+
max,
|
|
79
|
+
sum
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
async detectAnomalies(column, sensitivity = 2.5, lookback = "7 days") {
|
|
84
|
+
const query = `
|
|
85
|
+
WITH stats AS (
|
|
86
|
+
SELECT
|
|
87
|
+
AVG(${column})::float as mean,
|
|
88
|
+
STDDEV(${column})::float as stddev
|
|
89
|
+
FROM ${this.config.tableName}
|
|
90
|
+
WHERE ${this.config.timeColumn} >= NOW() - INTERVAL '${lookback}'
|
|
91
|
+
),
|
|
92
|
+
anomalies AS (
|
|
93
|
+
SELECT
|
|
94
|
+
${this.config.timeColumn} as timestamp,
|
|
95
|
+
${column} as value,
|
|
96
|
+
metadata,
|
|
97
|
+
ABS(${column} - stats.mean) / NULLIF(stats.stddev, 0) as z_score
|
|
98
|
+
FROM ${this.config.tableName}, stats
|
|
99
|
+
WHERE ${this.config.timeColumn} >= NOW() - INTERVAL '${lookback}'
|
|
100
|
+
AND ABS(${column} - stats.mean) / NULLIF(stats.stddev, 0) > $1
|
|
101
|
+
)
|
|
102
|
+
SELECT * FROM anomalies
|
|
103
|
+
ORDER BY z_score DESC
|
|
104
|
+
`;
|
|
105
|
+
const result = await this.pool.query(query, [sensitivity]);
|
|
106
|
+
return result.rows.map((row) => ({
|
|
107
|
+
timestamp: row.timestamp,
|
|
108
|
+
values: { [column]: row.value, z_score: row.z_score },
|
|
109
|
+
metadata: row.metadata
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
async forecast(column, periods, method = "linear") {
|
|
113
|
+
if (method === "linear") {
|
|
114
|
+
const query = `
|
|
115
|
+
WITH regression AS (
|
|
116
|
+
SELECT
|
|
117
|
+
regr_slope(${column}, EXTRACT(EPOCH FROM ${this.config.timeColumn}))::float as slope,
|
|
118
|
+
regr_intercept(${column}, EXTRACT(EPOCH FROM ${this.config.timeColumn}))::float as intercept,
|
|
119
|
+
MAX(${this.config.timeColumn}) as last_time,
|
|
120
|
+
EXTRACT(EPOCH FROM MAX(${this.config.timeColumn})) as last_epoch
|
|
121
|
+
FROM ${this.config.tableName}
|
|
122
|
+
WHERE ${this.config.timeColumn} >= NOW() - INTERVAL '30 days'
|
|
123
|
+
),
|
|
124
|
+
forecast_times AS (
|
|
125
|
+
SELECT
|
|
126
|
+
last_time + (INTERVAL '1 hour' * generate_series(1, $1)) as forecast_time
|
|
127
|
+
FROM regression
|
|
128
|
+
)
|
|
129
|
+
SELECT
|
|
130
|
+
forecast_time as timestamp,
|
|
131
|
+
(intercept + slope * EXTRACT(EPOCH FROM forecast_time))::float as forecast_value
|
|
132
|
+
FROM forecast_times, regression
|
|
133
|
+
`;
|
|
134
|
+
const result = await this.pool.query(query, [periods]);
|
|
135
|
+
return result.rows.map((row) => ({
|
|
136
|
+
timestamp: row.timestamp,
|
|
137
|
+
values: { [column]: row.forecast_value, forecast: true }
|
|
138
|
+
}));
|
|
139
|
+
}
|
|
140
|
+
logger.warn("Seasonal forecasting not yet implemented");
|
|
141
|
+
return [];
|
|
142
|
+
}
|
|
143
|
+
async getRetentionPolicy() {
|
|
144
|
+
const query = `
|
|
145
|
+
SELECT
|
|
146
|
+
hypertable_name as table_name,
|
|
147
|
+
drop_after::text as retention_period,
|
|
148
|
+
schedule_interval IS NOT NULL as is_enabled
|
|
149
|
+
FROM timescaledb_information.retention_policies
|
|
150
|
+
WHERE hypertable_name = $1
|
|
151
|
+
`;
|
|
152
|
+
const result = await this.pool.query(query, [this.config.tableName]);
|
|
153
|
+
return result.rows.map((row) => ({
|
|
154
|
+
tableName: row.table_name,
|
|
155
|
+
retentionPeriod: row.retention_period,
|
|
156
|
+
isEnabled: row.is_enabled
|
|
157
|
+
}));
|
|
158
|
+
}
|
|
159
|
+
async setRetentionPolicy(retentionPeriod) {
|
|
160
|
+
const query = `
|
|
161
|
+
SELECT add_retention_policy($1,
|
|
162
|
+
drop_after => INTERVAL '${retentionPeriod}',
|
|
163
|
+
if_not_exists => TRUE
|
|
164
|
+
)
|
|
165
|
+
`;
|
|
166
|
+
await this.pool.query(query, [this.config.tableName]);
|
|
167
|
+
logger.info(
|
|
168
|
+
`Set retention policy for ${this.config.tableName}: ${retentionPeriod}`
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
async compress(olderThan) {
|
|
172
|
+
await this.pool.query(`
|
|
173
|
+
ALTER TABLE ${this.config.tableName}
|
|
174
|
+
SET (timescaledb.compress,
|
|
175
|
+
timescaledb.compress_segmentby = 'type',
|
|
176
|
+
timescaledb.compress_orderby = '${this.config.timeColumn} DESC')
|
|
177
|
+
`);
|
|
178
|
+
await this.pool.query(
|
|
179
|
+
`
|
|
180
|
+
SELECT add_compression_policy($1,
|
|
181
|
+
compress_after => INTERVAL '${olderThan}',
|
|
182
|
+
if_not_exists => TRUE
|
|
183
|
+
)
|
|
184
|
+
`,
|
|
185
|
+
[this.config.tableName]
|
|
186
|
+
);
|
|
187
|
+
logger.info(
|
|
188
|
+
`Enabled compression for ${this.config.tableName} older than ${olderThan}`
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
async getChunkStats() {
|
|
192
|
+
const query = `
|
|
193
|
+
SELECT
|
|
194
|
+
COUNT(*) as total_chunks,
|
|
195
|
+
COUNT(*) FILTER (WHERE is_compressed) as compressed_chunks,
|
|
196
|
+
pg_size_pretty(SUM(total_bytes)) as total_size,
|
|
197
|
+
pg_size_pretty(SUM(total_bytes) FILTER (WHERE is_compressed)) as compressed_size,
|
|
198
|
+
CASE
|
|
199
|
+
WHEN SUM(uncompressed_total_bytes) > 0
|
|
200
|
+
THEN (1 - SUM(total_bytes)::float / SUM(uncompressed_total_bytes))::numeric(4,2)
|
|
201
|
+
ELSE 0
|
|
202
|
+
END as compression_ratio
|
|
203
|
+
FROM timescaledb_information.chunks
|
|
204
|
+
WHERE hypertable_name = $1
|
|
205
|
+
`;
|
|
206
|
+
const result = await this.pool.query(query, [this.config.tableName]);
|
|
207
|
+
const row = result.rows[0];
|
|
208
|
+
return {
|
|
209
|
+
totalChunks: parseInt(row.total_chunks) || 0,
|
|
210
|
+
compressedChunks: parseInt(row.compressed_chunks) || 0,
|
|
211
|
+
totalSize: row.total_size || "0 bytes",
|
|
212
|
+
compressedSize: row.compressed_size || "0 bytes",
|
|
213
|
+
compressionRatio: parseFloat(row.compression_ratio) || 0
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
export {
|
|
218
|
+
TimescaleAnalytics
|
|
219
|
+
};
|
|
220
|
+
//# sourceMappingURL=timescale-analytics.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/integrations/pg-aiguide/timescale-analytics.ts"],
|
|
4
|
+
"sourcesContent": ["import { Pool } from 'pg';\nimport { logger } from '../../core/monitoring/logger.js';\n\nexport interface TimeSeriesConfig {\n pool: Pool;\n tableName: string;\n timeColumn: string;\n valueColumns: string[];\n}\n\nexport interface TimeSeriesData {\n timestamp: Date;\n values: Record<string, any>;\n metadata?: Record<string, any>;\n}\n\nexport interface AggregateResult {\n period: Date;\n count: number;\n avg: Record<string, number>;\n min: Record<string, number>;\n max: Record<string, number>;\n sum: Record<string, number>;\n}\n\nexport class TimescaleAnalytics {\n private pool: Pool;\n private config: TimeSeriesConfig;\n\n constructor(config: TimeSeriesConfig) {\n this.pool = config.pool;\n this.config = config;\n }\n\n async createContinuousAggregate(\n name: string,\n interval: string,\n columns: string[]\n ): Promise<void> {\n const aggregates = columns\n .map(\n (col) => `\n AVG(${col}) as avg_${col},\n MIN(${col}) as min_${col},\n MAX(${col}) as max_${col},\n SUM(${col}) as sum_${col}\n `\n )\n .join(',\\n ');\n\n const query = `\n CREATE MATERIALIZED VIEW IF NOT EXISTS ${name}\n WITH (timescaledb.continuous) AS\n SELECT \n time_bucket('${interval}', ${this.config.timeColumn}) as bucket,\n COUNT(*) as count,\n ${aggregates}\n FROM ${this.config.tableName}\n GROUP BY bucket\n WITH NO DATA\n `;\n\n await this.pool.query(query);\n\n // Add refresh policy\n await this.pool.query(`\n SELECT add_continuous_aggregate_policy('${name}',\n start_offset => INTERVAL '1 month',\n end_offset => INTERVAL '1 hour',\n schedule_interval => INTERVAL '1 hour',\n if_not_exists => TRUE\n )\n `);\n\n logger.info(`Created continuous aggregate: ${name}`);\n }\n\n async getTimeSeries(\n startTime: Date,\n endTime: Date,\n interval: string,\n columns?: string[]\n ): Promise<AggregateResult[]> {\n const selectedColumns = columns || this.config.valueColumns;\n\n const aggregates = selectedColumns\n .map(\n (col) => `\n AVG(${col})::float as avg_${col},\n MIN(${col})::float as min_${col},\n MAX(${col})::float as max_${col},\n SUM(${col})::float as sum_${col}\n `\n )\n .join(',\\n ');\n\n const query = `\n SELECT \n time_bucket($1, ${this.config.timeColumn}) as period,\n COUNT(*)::int as count,\n ${aggregates}\n FROM ${this.config.tableName}\n WHERE ${this.config.timeColumn} >= $2\n AND ${this.config.timeColumn} <= $3\n GROUP BY period\n ORDER BY period\n `;\n\n const result = await this.pool.query(query, [interval, startTime, endTime]);\n\n return result.rows.map((row: any) => {\n const avg: Record<string, number> = {};\n const min: Record<string, number> = {};\n const max: Record<string, number> = {};\n const sum: Record<string, number> = {};\n\n selectedColumns.forEach((col) => {\n avg[col] = row[`avg_${col}`];\n min[col] = row[`min_${col}`];\n max[col] = row[`max_${col}`];\n sum[col] = row[`sum_${col}`];\n });\n\n return {\n period: row.period,\n count: row.count,\n avg,\n min,\n max,\n sum,\n };\n });\n }\n\n async detectAnomalies(\n column: string,\n sensitivity = 2.5,\n lookback = '7 days'\n ): Promise<TimeSeriesData[]> {\n const query = `\n WITH stats AS (\n SELECT \n AVG(${column})::float as mean,\n STDDEV(${column})::float as stddev\n FROM ${this.config.tableName}\n WHERE ${this.config.timeColumn} >= NOW() - INTERVAL '${lookback}'\n ),\n anomalies AS (\n SELECT \n ${this.config.timeColumn} as timestamp,\n ${column} as value,\n metadata,\n ABS(${column} - stats.mean) / NULLIF(stats.stddev, 0) as z_score\n FROM ${this.config.tableName}, stats\n WHERE ${this.config.timeColumn} >= NOW() - INTERVAL '${lookback}'\n AND ABS(${column} - stats.mean) / NULLIF(stats.stddev, 0) > $1\n )\n SELECT * FROM anomalies\n ORDER BY z_score DESC\n `;\n\n const result = await this.pool.query(query, [sensitivity]);\n\n return result.rows.map((row) => ({\n timestamp: row.timestamp,\n values: { [column]: row.value, z_score: row.z_score },\n metadata: row.metadata,\n }));\n }\n\n async forecast(\n column: string,\n periods: number,\n method: 'linear' | 'seasonal' = 'linear'\n ): Promise<TimeSeriesData[]> {\n // Simple linear regression forecast\n if (method === 'linear') {\n const query = `\n WITH regression AS (\n SELECT \n regr_slope(${column}, EXTRACT(EPOCH FROM ${this.config.timeColumn}))::float as slope,\n regr_intercept(${column}, EXTRACT(EPOCH FROM ${this.config.timeColumn}))::float as intercept,\n MAX(${this.config.timeColumn}) as last_time,\n EXTRACT(EPOCH FROM MAX(${this.config.timeColumn})) as last_epoch\n FROM ${this.config.tableName}\n WHERE ${this.config.timeColumn} >= NOW() - INTERVAL '30 days'\n ),\n forecast_times AS (\n SELECT \n last_time + (INTERVAL '1 hour' * generate_series(1, $1)) as forecast_time\n FROM regression\n )\n SELECT \n forecast_time as timestamp,\n (intercept + slope * EXTRACT(EPOCH FROM forecast_time))::float as forecast_value\n FROM forecast_times, regression\n `;\n\n const result = await this.pool.query(query, [periods]);\n\n return result.rows.map((row) => ({\n timestamp: row.timestamp,\n values: { [column]: row.forecast_value, forecast: true },\n }));\n }\n\n // Seasonal decomposition would require more complex logic\n logger.warn('Seasonal forecasting not yet implemented');\n return [];\n }\n\n async getRetentionPolicy(): Promise<\n {\n tableName: string;\n retentionPeriod: string;\n isEnabled: boolean;\n }[]\n > {\n const query = `\n SELECT \n hypertable_name as table_name,\n drop_after::text as retention_period,\n schedule_interval IS NOT NULL as is_enabled\n FROM timescaledb_information.retention_policies\n WHERE hypertable_name = $1\n `;\n\n const result = await this.pool.query(query, [this.config.tableName]);\n\n return result.rows.map((row) => ({\n tableName: row.table_name,\n retentionPeriod: row.retention_period,\n isEnabled: row.is_enabled,\n }));\n }\n\n async setRetentionPolicy(retentionPeriod: string): Promise<void> {\n const query = `\n SELECT add_retention_policy($1, \n drop_after => INTERVAL '${retentionPeriod}',\n if_not_exists => TRUE\n )\n `;\n\n await this.pool.query(query, [this.config.tableName]);\n logger.info(\n `Set retention policy for ${this.config.tableName}: ${retentionPeriod}`\n );\n }\n\n async compress(olderThan: string): Promise<void> {\n // Enable compression\n await this.pool.query(`\n ALTER TABLE ${this.config.tableName} \n SET (timescaledb.compress, \n timescaledb.compress_segmentby = 'type',\n timescaledb.compress_orderby = '${this.config.timeColumn} DESC')\n `);\n\n // Add compression policy\n await this.pool.query(\n `\n SELECT add_compression_policy($1,\n compress_after => INTERVAL '${olderThan}',\n if_not_exists => TRUE\n )\n `,\n [this.config.tableName]\n );\n\n logger.info(\n `Enabled compression for ${this.config.tableName} older than ${olderThan}`\n );\n }\n\n async getChunkStats(): Promise<{\n totalChunks: number;\n compressedChunks: number;\n totalSize: string;\n compressedSize: string;\n compressionRatio: number;\n }> {\n const query = `\n SELECT \n COUNT(*) as total_chunks,\n COUNT(*) FILTER (WHERE is_compressed) as compressed_chunks,\n pg_size_pretty(SUM(total_bytes)) as total_size,\n pg_size_pretty(SUM(total_bytes) FILTER (WHERE is_compressed)) as compressed_size,\n CASE \n WHEN SUM(uncompressed_total_bytes) > 0 \n THEN (1 - SUM(total_bytes)::float / SUM(uncompressed_total_bytes))::numeric(4,2)\n ELSE 0\n END as compression_ratio\n FROM timescaledb_information.chunks\n WHERE hypertable_name = $1\n `;\n\n const result = await this.pool.query(query, [this.config.tableName]);\n const row = result.rows[0];\n\n return {\n totalChunks: parseInt(row.total_chunks) || 0,\n compressedChunks: parseInt(row.compressed_chunks) || 0,\n totalSize: row.total_size || '0 bytes',\n compressedSize: row.compressed_size || '0 bytes',\n compressionRatio: parseFloat(row.compression_ratio) || 0,\n };\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|