@astragenie/astramemory-local 0.7.2
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/CHANGELOG.md +341 -0
- package/README.md +419 -0
- package/dist/backup/retention.d.ts +15 -0
- package/dist/backup/retention.js +62 -0
- package/dist/backup/retention.js.map +1 -0
- package/dist/backup/snapshot.d.ts +21 -0
- package/dist/backup/snapshot.js +55 -0
- package/dist/backup/snapshot.js.map +1 -0
- package/dist/backup/verify.d.ts +23 -0
- package/dist/backup/verify.js +77 -0
- package/dist/backup/verify.js.map +1 -0
- package/dist/budget/tracker.d.ts +58 -0
- package/dist/budget/tracker.js +102 -0
- package/dist/budget/tracker.js.map +1 -0
- package/dist/capture/codex.d.ts +63 -0
- package/dist/capture/codex.js +0 -0
- package/dist/capture/codex.js.map +1 -0
- package/dist/cli/backup.d.ts +1 -0
- package/dist/cli/backup.js +112 -0
- package/dist/cli/backup.js.map +1 -0
- package/dist/cli/budget.d.ts +7 -0
- package/dist/cli/budget.js +44 -0
- package/dist/cli/budget.js.map +1 -0
- package/dist/cli/capture.d.ts +10 -0
- package/dist/cli/capture.js +113 -0
- package/dist/cli/capture.js.map +1 -0
- package/dist/cli/consolidate.d.ts +16 -0
- package/dist/cli/consolidate.js +146 -0
- package/dist/cli/consolidate.js.map +1 -0
- package/dist/cli/doctor.d.ts +1 -0
- package/dist/cli/doctor.js +54 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/entity-backfill.d.ts +10 -0
- package/dist/cli/entity-backfill.js +46 -0
- package/dist/cli/entity-backfill.js.map +1 -0
- package/dist/cli/hook-install.d.ts +45 -0
- package/dist/cli/hook-install.js +77 -0
- package/dist/cli/hook-install.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +312 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +16 -0
- package/dist/cli/init.js +431 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/mcp-stdio.d.ts +18 -0
- package/dist/cli/mcp-stdio.js +67 -0
- package/dist/cli/mcp-stdio.js.map +1 -0
- package/dist/cli/memory.d.ts +15 -0
- package/dist/cli/memory.js +52 -0
- package/dist/cli/memory.js.map +1 -0
- package/dist/cli/open-runtime-db.d.ts +15 -0
- package/dist/cli/open-runtime-db.js +37 -0
- package/dist/cli/open-runtime-db.js.map +1 -0
- package/dist/cli/pair.d.ts +29 -0
- package/dist/cli/pair.js +64 -0
- package/dist/cli/pair.js.map +1 -0
- package/dist/cli/providers.d.ts +10 -0
- package/dist/cli/providers.js +97 -0
- package/dist/cli/providers.js.map +1 -0
- package/dist/cli/queue-purge.d.ts +5 -0
- package/dist/cli/queue-purge.js +92 -0
- package/dist/cli/queue-purge.js.map +1 -0
- package/dist/cli/queue.d.ts +29 -0
- package/dist/cli/queue.js +73 -0
- package/dist/cli/queue.js.map +1 -0
- package/dist/cli/rebuild.d.ts +15 -0
- package/dist/cli/rebuild.js +70 -0
- package/dist/cli/rebuild.js.map +1 -0
- package/dist/cli/reembed-dim.d.ts +21 -0
- package/dist/cli/reembed-dim.js +199 -0
- package/dist/cli/reembed-dim.js.map +1 -0
- package/dist/cli/reinstall.d.ts +1 -0
- package/dist/cli/reinstall.js +205 -0
- package/dist/cli/reinstall.js.map +1 -0
- package/dist/cli/restore.d.ts +1 -0
- package/dist/cli/restore.js +167 -0
- package/dist/cli/restore.js.map +1 -0
- package/dist/cli/retag.d.ts +14 -0
- package/dist/cli/retag.js +62 -0
- package/dist/cli/retag.js.map +1 -0
- package/dist/cli/search.d.ts +66 -0
- package/dist/cli/search.js +174 -0
- package/dist/cli/search.js.map +1 -0
- package/dist/cli/serve.d.ts +9 -0
- package/dist/cli/serve.js +364 -0
- package/dist/cli/serve.js.map +1 -0
- package/dist/cli/service.d.ts +1 -0
- package/dist/cli/service.js +121 -0
- package/dist/cli/service.js.map +1 -0
- package/dist/cli/sync.d.ts +15 -0
- package/dist/cli/sync.js +61 -0
- package/dist/cli/sync.js.map +1 -0
- package/dist/cli/token.d.ts +24 -0
- package/dist/cli/token.js +77 -0
- package/dist/cli/token.js.map +1 -0
- package/dist/cli/wait-health.d.ts +4 -0
- package/dist/cli/wait-health.js +23 -0
- package/dist/cli/wait-health.js.map +1 -0
- package/dist/config/config.d.ts +127 -0
- package/dist/config/config.js +38 -0
- package/dist/config/config.js.map +1 -0
- package/dist/config/datadir.d.ts +30 -0
- package/dist/config/datadir.js +65 -0
- package/dist/config/datadir.js.map +1 -0
- package/dist/config/loader.d.ts +23 -0
- package/dist/config/loader.js +102 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/migrate-dirs.d.ts +36 -0
- package/dist/config/migrate-dirs.js +132 -0
- package/dist/config/migrate-dirs.js.map +1 -0
- package/dist/config/persist-envs.d.ts +23 -0
- package/dist/config/persist-envs.js +119 -0
- package/dist/config/persist-envs.js.map +1 -0
- package/dist/config/resolve-runtime.d.ts +19 -0
- package/dist/config/resolve-runtime.js +53 -0
- package/dist/config/resolve-runtime.js.map +1 -0
- package/dist/config/secrets.d.ts +28 -0
- package/dist/config/secrets.js +38 -0
- package/dist/config/secrets.js.map +1 -0
- package/dist/config/sync-settings.d.ts +16 -0
- package/dist/config/sync-settings.js +34 -0
- package/dist/config/sync-settings.js.map +1 -0
- package/dist/config/writer.d.ts +19 -0
- package/dist/config/writer.js +121 -0
- package/dist/config/writer.js.map +1 -0
- package/dist/consolidate/consolidate.d.ts +80 -0
- package/dist/consolidate/consolidate.js +0 -0
- package/dist/consolidate/consolidate.js.map +1 -0
- package/dist/consolidate/proposals.d.ts +35 -0
- package/dist/consolidate/proposals.js +66 -0
- package/dist/consolidate/proposals.js.map +1 -0
- package/dist/contracts/atom-wire.d.ts +48 -0
- package/dist/contracts/atom-wire.js +55 -0
- package/dist/contracts/atom-wire.js.map +1 -0
- package/dist/contracts/embed.d.ts +41 -0
- package/dist/contracts/embed.js +20 -0
- package/dist/contracts/embed.js.map +1 -0
- package/dist/contracts/index.d.ts +5 -0
- package/dist/contracts/index.js +6 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/job.d.ts +113 -0
- package/dist/contracts/job.js +32 -0
- package/dist/contracts/job.js.map +1 -0
- package/dist/contracts/llm.d.ts +30 -0
- package/dist/contracts/llm.js +2 -0
- package/dist/contracts/llm.js.map +1 -0
- package/dist/contracts/memory.d.ts +47 -0
- package/dist/contracts/memory.js +5 -0
- package/dist/contracts/memory.js.map +1 -0
- package/dist/contracts/vector.d.ts +29 -0
- package/dist/contracts/vector.js +2 -0
- package/dist/contracts/vector.js.map +1 -0
- package/dist/distill/flatten-turns.d.ts +1 -0
- package/dist/distill/flatten-turns.js +50 -0
- package/dist/distill/flatten-turns.js.map +1 -0
- package/dist/distill/pipeline.d.ts +45 -0
- package/dist/distill/pipeline.js +113 -0
- package/dist/distill/pipeline.js.map +1 -0
- package/dist/distill/prompts/extract.d.ts +122 -0
- package/dist/distill/prompts/extract.js +67 -0
- package/dist/distill/prompts/extract.js.map +1 -0
- package/dist/distill/stages/01-cleanup.d.ts +9 -0
- package/dist/distill/stages/01-cleanup.js +67 -0
- package/dist/distill/stages/01-cleanup.js.map +1 -0
- package/dist/distill/stages/02-normalize.d.ts +9 -0
- package/dist/distill/stages/02-normalize.js +76 -0
- package/dist/distill/stages/02-normalize.js.map +1 -0
- package/dist/distill/stages/03-chunk.d.ts +22 -0
- package/dist/distill/stages/03-chunk.js +138 -0
- package/dist/distill/stages/03-chunk.js.map +1 -0
- package/dist/distill/stages/04-compact.d.ts +28 -0
- package/dist/distill/stages/04-compact.js +69 -0
- package/dist/distill/stages/04-compact.js.map +1 -0
- package/dist/distill/stages/05-extract.d.ts +35 -0
- package/dist/distill/stages/05-extract.js +101 -0
- package/dist/distill/stages/05-extract.js.map +1 -0
- package/dist/distill/stages/06-reduce.d.ts +16 -0
- package/dist/distill/stages/06-reduce.js +30 -0
- package/dist/distill/stages/06-reduce.js.map +1 -0
- package/dist/distill/stages/07-memory-normalize.d.ts +27 -0
- package/dist/distill/stages/07-memory-normalize.js +65 -0
- package/dist/distill/stages/07-memory-normalize.js.map +1 -0
- package/dist/distill/stages/08-embed-index.d.ts +31 -0
- package/dist/distill/stages/08-embed-index.js +82 -0
- package/dist/distill/stages/08-embed-index.js.map +1 -0
- package/dist/doctor/checks.d.ts +77 -0
- package/dist/doctor/checks.js +626 -0
- package/dist/doctor/checks.js.map +1 -0
- package/dist/doctor/hardening-checks.d.ts +9 -0
- package/dist/doctor/hardening-checks.js +182 -0
- package/dist/doctor/hardening-checks.js.map +1 -0
- package/dist/doctor/probes/embed-probe.d.ts +19 -0
- package/dist/doctor/probes/embed-probe.js +47 -0
- package/dist/doctor/probes/embed-probe.js.map +1 -0
- package/dist/doctor/probes/llm-chat-probe.d.ts +11 -0
- package/dist/doctor/probes/llm-chat-probe.js +41 -0
- package/dist/doctor/probes/llm-chat-probe.js.map +1 -0
- package/dist/doctor/probes/plugin-coexistence.d.ts +14 -0
- package/dist/doctor/probes/plugin-coexistence.js +60 -0
- package/dist/doctor/probes/plugin-coexistence.js.map +1 -0
- package/dist/doctor/runner.d.ts +17 -0
- package/dist/doctor/runner.js +53 -0
- package/dist/doctor/runner.js.map +1 -0
- package/dist/doctor/types.d.ts +12 -0
- package/dist/doctor/types.js +2 -0
- package/dist/doctor/types.js.map +1 -0
- package/dist/entity/backfill.d.ts +30 -0
- package/dist/entity/backfill.js +55 -0
- package/dist/entity/backfill.js.map +1 -0
- package/dist/entity/extract-entities.d.ts +27 -0
- package/dist/entity/extract-entities.js +86 -0
- package/dist/entity/extract-entities.js.map +1 -0
- package/dist/entity/normalize.d.ts +17 -0
- package/dist/entity/normalize.js +20 -0
- package/dist/entity/normalize.js.map +1 -0
- package/dist/eval/harness.d.ts +96 -0
- package/dist/eval/harness.js +119 -0
- package/dist/eval/harness.js.map +1 -0
- package/dist/eval/metrics.d.ts +23 -0
- package/dist/eval/metrics.js +44 -0
- package/dist/eval/metrics.js.map +1 -0
- package/dist/log/correlation.d.ts +24 -0
- package/dist/log/correlation.js +33 -0
- package/dist/log/correlation.js.map +1 -0
- package/dist/log/logger.d.ts +38 -0
- package/dist/log/logger.js +129 -0
- package/dist/log/logger.js.map +1 -0
- package/dist/log/scrub.d.ts +33 -0
- package/dist/log/scrub.js +91 -0
- package/dist/log/scrub.js.map +1 -0
- package/dist/mcp/server.d.ts +36 -0
- package/dist/mcp/server.js +553 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/memory-tool/adapter.d.ts +73 -0
- package/dist/memory-tool/adapter.js +269 -0
- package/dist/memory-tool/adapter.js.map +1 -0
- package/dist/pipeline/errors.d.ts +21 -0
- package/dist/pipeline/errors.js +34 -0
- package/dist/pipeline/errors.js.map +1 -0
- package/dist/pipeline/failure-classifier.d.ts +13 -0
- package/dist/pipeline/failure-classifier.js +72 -0
- package/dist/pipeline/failure-classifier.js.map +1 -0
- package/dist/pipeline/handler-ctx-ext.d.ts +23 -0
- package/dist/pipeline/handler-ctx-ext.js +19 -0
- package/dist/pipeline/handler-ctx-ext.js.map +1 -0
- package/dist/pipeline/handler.d.ts +20 -0
- package/dist/pipeline/handler.js +2 -0
- package/dist/pipeline/handler.js.map +1 -0
- package/dist/pipeline/handlers/cleanup.d.ts +14 -0
- package/dist/pipeline/handlers/cleanup.js +47 -0
- package/dist/pipeline/handlers/cleanup.js.map +1 -0
- package/dist/pipeline/handlers/consolidate.d.ts +8 -0
- package/dist/pipeline/handlers/consolidate.js +23 -0
- package/dist/pipeline/handlers/consolidate.js.map +1 -0
- package/dist/pipeline/handlers/distill-events.d.ts +15 -0
- package/dist/pipeline/handlers/distill-events.js +134 -0
- package/dist/pipeline/handlers/distill-events.js.map +1 -0
- package/dist/pipeline/handlers/distill.d.ts +17 -0
- package/dist/pipeline/handlers/distill.js +110 -0
- package/dist/pipeline/handlers/distill.js.map +1 -0
- package/dist/pipeline/handlers/reembed.d.ts +10 -0
- package/dist/pipeline/handlers/reembed.js +34 -0
- package/dist/pipeline/handlers/reembed.js.map +1 -0
- package/dist/pipeline/job-repo.d.ts +86 -0
- package/dist/pipeline/job-repo.js +168 -0
- package/dist/pipeline/job-repo.js.map +1 -0
- package/dist/pipeline/mock-providers.d.ts +49 -0
- package/dist/pipeline/mock-providers.js +175 -0
- package/dist/pipeline/mock-providers.js.map +1 -0
- package/dist/pipeline/registry.d.ts +15 -0
- package/dist/pipeline/registry.js +20 -0
- package/dist/pipeline/registry.js.map +1 -0
- package/dist/pipeline/worker.d.ts +41 -0
- package/dist/pipeline/worker.js +167 -0
- package/dist/pipeline/worker.js.map +1 -0
- package/dist/providers/embed/azure-openai.d.ts +25 -0
- package/dist/providers/embed/azure-openai.js +138 -0
- package/dist/providers/embed/azure-openai.js.map +1 -0
- package/dist/providers/embed/ollama.d.ts +17 -0
- package/dist/providers/embed/ollama.js +106 -0
- package/dist/providers/embed/ollama.js.map +1 -0
- package/dist/providers/index.d.ts +19 -0
- package/dist/providers/index.js +72 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/llm/azure-openai.d.ts +20 -0
- package/dist/providers/llm/azure-openai.js +135 -0
- package/dist/providers/llm/azure-openai.js.map +1 -0
- package/dist/providers/llm/ollama.d.ts +13 -0
- package/dist/providers/llm/ollama.js +113 -0
- package/dist/providers/llm/ollama.js.map +1 -0
- package/dist/providers/llm/pricing.d.ts +21 -0
- package/dist/providers/llm/pricing.js +22 -0
- package/dist/providers/llm/pricing.js.map +1 -0
- package/dist/recall/pack.d.ts +32 -0
- package/dist/recall/pack.js +90 -0
- package/dist/recall/pack.js.map +1 -0
- package/dist/recall/policy.d.ts +39 -0
- package/dist/recall/policy.js +96 -0
- package/dist/recall/policy.js.map +1 -0
- package/dist/redact/detectors.d.ts +20 -0
- package/dist/redact/detectors.js +85 -0
- package/dist/redact/detectors.js.map +1 -0
- package/dist/redact/entropy.d.ts +24 -0
- package/dist/redact/entropy.js +77 -0
- package/dist/redact/entropy.js.map +1 -0
- package/dist/redact/index.d.ts +47 -0
- package/dist/redact/index.js +165 -0
- package/dist/redact/index.js.map +1 -0
- package/dist/search/fuse.d.ts +108 -0
- package/dist/search/fuse.js +135 -0
- package/dist/search/fuse.js.map +1 -0
- package/dist/search/query.d.ts +28 -0
- package/dist/search/query.js +70 -0
- package/dist/search/query.js.map +1 -0
- package/dist/search/search.d.ts +164 -0
- package/dist/search/search.js +310 -0
- package/dist/search/search.js.map +1 -0
- package/dist/server/app.d.ts +17 -0
- package/dist/server/app.js +133 -0
- package/dist/server/app.js.map +1 -0
- package/dist/server/health-state.d.ts +29 -0
- package/dist/server/health-state.js +28 -0
- package/dist/server/health-state.js.map +1 -0
- package/dist/server/lib/network.d.ts +12 -0
- package/dist/server/lib/network.js +16 -0
- package/dist/server/lib/network.js.map +1 -0
- package/dist/server/lib/score-contract.d.ts +36 -0
- package/dist/server/lib/score-contract.js +54 -0
- package/dist/server/lib/score-contract.js.map +1 -0
- package/dist/server/lib/stable-stringify.d.ts +10 -0
- package/dist/server/lib/stable-stringify.js +27 -0
- package/dist/server/lib/stable-stringify.js.map +1 -0
- package/dist/server/lib/wire-meta.d.ts +7 -0
- package/dist/server/lib/wire-meta.js +29 -0
- package/dist/server/lib/wire-meta.js.map +1 -0
- package/dist/server/queries/dashboard.d.ts +142 -0
- package/dist/server/queries/dashboard.js +166 -0
- package/dist/server/queries/dashboard.js.map +1 -0
- package/dist/server/routes/consolidation.d.ts +14 -0
- package/dist/server/routes/consolidation.js +67 -0
- package/dist/server/routes/consolidation.js.map +1 -0
- package/dist/server/routes/dashboard-api-html.d.ts +15 -0
- package/dist/server/routes/dashboard-api-html.js +144 -0
- package/dist/server/routes/dashboard-api-html.js.map +1 -0
- package/dist/server/routes/dashboard-consolidation-html.d.ts +26 -0
- package/dist/server/routes/dashboard-consolidation-html.js +202 -0
- package/dist/server/routes/dashboard-consolidation-html.js.map +1 -0
- package/dist/server/routes/dashboard-html.d.ts +15 -0
- package/dist/server/routes/dashboard-html.js +365 -0
- package/dist/server/routes/dashboard-html.js.map +1 -0
- package/dist/server/routes/dashboard-jobs-html.d.ts +18 -0
- package/dist/server/routes/dashboard-jobs-html.js +186 -0
- package/dist/server/routes/dashboard-jobs-html.js.map +1 -0
- package/dist/server/routes/dashboard-search-html.d.ts +18 -0
- package/dist/server/routes/dashboard-search-html.js +189 -0
- package/dist/server/routes/dashboard-search-html.js.map +1 -0
- package/dist/server/routes/dashboard.d.ts +19 -0
- package/dist/server/routes/dashboard.js +68 -0
- package/dist/server/routes/dashboard.js.map +1 -0
- package/dist/server/routes/digest.d.ts +9 -0
- package/dist/server/routes/digest.js +37 -0
- package/dist/server/routes/digest.js.map +1 -0
- package/dist/server/routes/entities.d.ts +12 -0
- package/dist/server/routes/entities.js +46 -0
- package/dist/server/routes/entities.js.map +1 -0
- package/dist/server/routes/health.d.ts +14 -0
- package/dist/server/routes/health.js +100 -0
- package/dist/server/routes/health.js.map +1 -0
- package/dist/server/routes/ingest.d.ts +209 -0
- package/dist/server/routes/ingest.js +454 -0
- package/dist/server/routes/ingest.js.map +1 -0
- package/dist/server/routes/lifecycle.d.ts +21 -0
- package/dist/server/routes/lifecycle.js +132 -0
- package/dist/server/routes/lifecycle.js.map +1 -0
- package/dist/server/routes/mcp.d.ts +15 -0
- package/dist/server/routes/mcp.js +36 -0
- package/dist/server/routes/mcp.js.map +1 -0
- package/dist/server/routes/memory-tool.d.ts +14 -0
- package/dist/server/routes/memory-tool.js +28 -0
- package/dist/server/routes/memory-tool.js.map +1 -0
- package/dist/server/routes/memory.d.ts +7 -0
- package/dist/server/routes/memory.js +19 -0
- package/dist/server/routes/memory.js.map +1 -0
- package/dist/server/routes/recall.d.ts +15 -0
- package/dist/server/routes/recall.js +74 -0
- package/dist/server/routes/recall.js.map +1 -0
- package/dist/server/routes/search.d.ts +12 -0
- package/dist/server/routes/search.js +203 -0
- package/dist/server/routes/search.js.map +1 -0
- package/dist/server/routes/version.d.ts +2 -0
- package/dist/server/routes/version.js +11 -0
- package/dist/server/routes/version.js.map +1 -0
- package/dist/server/routes/why.d.ts +9 -0
- package/dist/server/routes/why.js +38 -0
- package/dist/server/routes/why.js.map +1 -0
- package/dist/service/index.d.ts +10 -0
- package/dist/service/index.js +25 -0
- package/dist/service/index.js.map +1 -0
- package/dist/service/install-flow.d.ts +18 -0
- package/dist/service/install-flow.js +47 -0
- package/dist/service/install-flow.js.map +1 -0
- package/dist/service/instance-lock.d.ts +26 -0
- package/dist/service/instance-lock.js +150 -0
- package/dist/service/instance-lock.js.map +1 -0
- package/dist/service/launchd.d.ts +11 -0
- package/dist/service/launchd.js +196 -0
- package/dist/service/launchd.js.map +1 -0
- package/dist/service/schtasks.d.ts +31 -0
- package/dist/service/schtasks.js +274 -0
- package/dist/service/schtasks.js.map +1 -0
- package/dist/service/shim.d.ts +21 -0
- package/dist/service/shim.js +80 -0
- package/dist/service/shim.js.map +1 -0
- package/dist/service/systemd.d.ts +11 -0
- package/dist/service/systemd.js +150 -0
- package/dist/service/systemd.js.map +1 -0
- package/dist/service/task-xml.d.ts +36 -0
- package/dist/service/task-xml.js +91 -0
- package/dist/service/task-xml.js.map +1 -0
- package/dist/service/types.d.ts +47 -0
- package/dist/service/types.js +2 -0
- package/dist/service/types.js.map +1 -0
- package/dist/storage/archival.d.ts +29 -0
- package/dist/storage/archival.js +47 -0
- package/dist/storage/archival.js.map +1 -0
- package/dist/storage/bearer-keystore.d.ts +34 -0
- package/dist/storage/bearer-keystore.js +75 -0
- package/dist/storage/bearer-keystore.js.map +1 -0
- package/dist/storage/db.d.ts +37 -0
- package/dist/storage/db.js +92 -0
- package/dist/storage/db.js.map +1 -0
- package/dist/storage/entities.d.ts +71 -0
- package/dist/storage/entities.js +141 -0
- package/dist/storage/entities.js.map +1 -0
- package/dist/storage/ingest-idempotency.d.ts +26 -0
- package/dist/storage/ingest-idempotency.js +29 -0
- package/dist/storage/ingest-idempotency.js.map +1 -0
- package/dist/storage/keystore.d.ts +64 -0
- package/dist/storage/keystore.js +194 -0
- package/dist/storage/keystore.js.map +1 -0
- package/dist/storage/memories.d.ts +51 -0
- package/dist/storage/memories.js +67 -0
- package/dist/storage/memories.js.map +1 -0
- package/dist/storage/memory-events.d.ts +145 -0
- package/dist/storage/memory-events.js +287 -0
- package/dist/storage/memory-events.js.map +1 -0
- package/dist/storage/migrate-encrypt.d.ts +16 -0
- package/dist/storage/migrate-encrypt.js +121 -0
- package/dist/storage/migrate-encrypt.js.map +1 -0
- package/dist/storage/migrate.d.ts +27 -0
- package/dist/storage/migrate.js +105 -0
- package/dist/storage/migrate.js.map +1 -0
- package/dist/storage/redaction-log.d.ts +18 -0
- package/dist/storage/redaction-log.js +27 -0
- package/dist/storage/redaction-log.js.map +1 -0
- package/dist/storage/usefulness.d.ts +115 -0
- package/dist/storage/usefulness.js +203 -0
- package/dist/storage/usefulness.js.map +1 -0
- package/dist/sync/conflict-resolve.d.ts +26 -0
- package/dist/sync/conflict-resolve.js +139 -0
- package/dist/sync/conflict-resolve.js.map +1 -0
- package/dist/sync/puller.d.ts +115 -0
- package/dist/sync/puller.js +173 -0
- package/dist/sync/puller.js.map +1 -0
- package/dist/sync/shipper.d.ts +112 -0
- package/dist/sync/shipper.js +189 -0
- package/dist/sync/shipper.js.map +1 -0
- package/dist/tag-hygiene/backfill.d.ts +50 -0
- package/dist/tag-hygiene/backfill.js +117 -0
- package/dist/tag-hygiene/backfill.js.map +1 -0
- package/dist/tag-hygiene/derive-repo.d.ts +9 -0
- package/dist/tag-hygiene/derive-repo.js +19 -0
- package/dist/tag-hygiene/derive-repo.js.map +1 -0
- package/dist/tag-hygiene/tier2-infer.d.ts +28 -0
- package/dist/tag-hygiene/tier2-infer.js +72 -0
- package/dist/tag-hygiene/tier2-infer.js.map +1 -0
- package/dist/vector/sqlite-vec.d.ts +16 -0
- package/dist/vector/sqlite-vec.js +49 -0
- package/dist/vector/sqlite-vec.js.map +1 -0
- package/migrations/001-init.sql +117 -0
- package/migrations/002-wire-v1.sql +16 -0
- package/migrations/003-expand-memory-types.sql +81 -0
- package/migrations/004-provenance.sql +4 -0
- package/migrations/005-security.sql +12 -0
- package/migrations/006-atom-v3.sql +28 -0
- package/migrations/007-memory-events.sql +30 -0
- package/migrations/008-consolidation.sql +31 -0
- package/migrations/009-tag-hygiene.sql +13 -0
- package/migrations/010-sync-pull.sql +53 -0
- package/migrations/011-embed-dim-migration.sql +28 -0
- package/migrations/012-entities.sql +36 -0
- package/migrations/013-archival.sql +50 -0
- package/package.json +50 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync shipper (Wave 3d, ADR-003) — one-way, append-only, idempotent log
|
|
3
|
+
* shipping: local memory_events -> cloud POST /sync/events.
|
|
4
|
+
*
|
|
5
|
+
* The log IS the queue: unsynced rows are `synced_at IS NULL`; a batch is
|
|
6
|
+
* acked by the cloud's `{ acked_seq }` and marked in one UPDATE. Crash-safe
|
|
7
|
+
* by construction — replaying a batch is always safe (cloud dedups on
|
|
8
|
+
* content_hash and (device_id, seq)).
|
|
9
|
+
*
|
|
10
|
+
* Scope filter (ADR-009): only events whose atom is team/org-scoped ship.
|
|
11
|
+
* `personal` atoms never leave the machine. erase_request events for atoms
|
|
12
|
+
* whose row is already hard-deleted ship when their payload carries a
|
|
13
|
+
* team/org scope (3f writes the scope into the erase payload for exactly
|
|
14
|
+
* this reason).
|
|
15
|
+
*
|
|
16
|
+
* Offline: unlimited — failures leave rows unsynced; retry with exponential
|
|
17
|
+
* backoff capped at BACKOFF_CAP_MS.
|
|
18
|
+
*/
|
|
19
|
+
import { randomUUID } from 'node:crypto';
|
|
20
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
21
|
+
import { join } from 'node:path';
|
|
22
|
+
import { logger } from '../log/logger.js';
|
|
23
|
+
export const SYNC_PROTOCOL = 'astramem-sync@1';
|
|
24
|
+
const DEFAULT_BATCH = 200;
|
|
25
|
+
const DEFAULT_INTERVAL_MS = 30_000;
|
|
26
|
+
const BACKOFF_CAP_MS = 15 * 60_000;
|
|
27
|
+
/**
|
|
28
|
+
* Stable per-install device id, persisted as a plain file in the config dir
|
|
29
|
+
* (an identifier, not a secret — the device TOKEN lives in the keystore).
|
|
30
|
+
*/
|
|
31
|
+
export function getOrCreateDeviceId(configDir) {
|
|
32
|
+
const path = join(configDir, 'device-id');
|
|
33
|
+
if (existsSync(path)) {
|
|
34
|
+
const existing = readFileSync(path, 'utf8').trim();
|
|
35
|
+
if (existing)
|
|
36
|
+
return existing;
|
|
37
|
+
}
|
|
38
|
+
const id = randomUUID();
|
|
39
|
+
mkdirSync(configDir, { recursive: true });
|
|
40
|
+
writeFileSync(path, `${id}\n`, 'utf8');
|
|
41
|
+
return id;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Unsynced, ship-eligible events in seq order (ADR-009 scope filter).
|
|
45
|
+
* LEFT JOIN: an erase_request may outlive its memories row — it ships when
|
|
46
|
+
* its payload records a team/org scope.
|
|
47
|
+
*
|
|
48
|
+
* `archive`/`restore` (FEAT-404) are excluded unconditionally, regardless of
|
|
49
|
+
* scope: archival is a per-device retention/storage-hygiene decision (one
|
|
50
|
+
* device's local SQLite-growth mitigation), not a semantic memory change,
|
|
51
|
+
* so it must never leave the machine. The sync wire schema
|
|
52
|
+
* (contracts/schemas/sync-envelope.v1.schema.json) also never lists these
|
|
53
|
+
* two types in its event_type enum — shipping one would fail schema
|
|
54
|
+
* validation on the wire, on top of being the wrong policy.
|
|
55
|
+
*/
|
|
56
|
+
export function listUnsynced(db, batchSize) {
|
|
57
|
+
return db
|
|
58
|
+
.prepare(`
|
|
59
|
+
SELECT e.seq, e.event_id, e.event_type, e.atom_id, e.payload_json, e.content_hash, e.created_at
|
|
60
|
+
FROM memory_events e
|
|
61
|
+
LEFT JOIN memories m ON m.id = e.atom_id
|
|
62
|
+
WHERE e.synced_at IS NULL
|
|
63
|
+
AND e.event_type NOT IN ('archive', 'restore')
|
|
64
|
+
AND (
|
|
65
|
+
m.scope IN ('team', 'org')
|
|
66
|
+
OR (
|
|
67
|
+
e.event_type = 'erase_request'
|
|
68
|
+
AND json_extract(e.payload_json, '$.scope') IN ('team', 'org')
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
ORDER BY e.seq ASC
|
|
72
|
+
LIMIT ?
|
|
73
|
+
`)
|
|
74
|
+
.all(batchSize);
|
|
75
|
+
}
|
|
76
|
+
/** Last acked seq = highest synced event seq (0 when nothing has shipped). */
|
|
77
|
+
export function lastAckedSeq(db) {
|
|
78
|
+
const row = db
|
|
79
|
+
.prepare('SELECT MAX(seq) AS s FROM memory_events WHERE synced_at IS NOT NULL')
|
|
80
|
+
.get();
|
|
81
|
+
return row.s ?? 0;
|
|
82
|
+
}
|
|
83
|
+
export function buildEnvelope(deviceId, workspaceId, cursor, rows) {
|
|
84
|
+
return {
|
|
85
|
+
protocol: SYNC_PROTOCOL,
|
|
86
|
+
device_id: deviceId,
|
|
87
|
+
workspace_id: workspaceId,
|
|
88
|
+
cursor,
|
|
89
|
+
events: rows.map(r => ({
|
|
90
|
+
seq: r.seq,
|
|
91
|
+
event_id: r.event_id,
|
|
92
|
+
event_type: r.event_type,
|
|
93
|
+
atom_id: r.atom_id,
|
|
94
|
+
payload_json: r.payload_json !== null ? JSON.parse(r.payload_json) : null,
|
|
95
|
+
content_hash: r.content_hash,
|
|
96
|
+
created_at: r.created_at,
|
|
97
|
+
})),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* One shipping round: read a batch, POST it, mark acked rows synced.
|
|
102
|
+
* Throws on transport/HTTP failure — the caller owns retry/backoff.
|
|
103
|
+
*/
|
|
104
|
+
export async function shipOnce(opts) {
|
|
105
|
+
const batchSize = opts.batchSize ?? DEFAULT_BATCH;
|
|
106
|
+
const rows = listUnsynced(opts.db, batchSize);
|
|
107
|
+
if (rows.length === 0)
|
|
108
|
+
return { shipped: 0, acked: 0, idle: true };
|
|
109
|
+
const envelope = buildEnvelope(opts.deviceId, opts.workspaceId, lastAckedSeq(opts.db), rows);
|
|
110
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
111
|
+
const res = await doFetch(`${opts.url.replace(/\/$/, '')}/sync/events`, {
|
|
112
|
+
method: 'POST',
|
|
113
|
+
headers: {
|
|
114
|
+
'content-type': 'application/json',
|
|
115
|
+
authorization: `Bearer ${opts.token}`,
|
|
116
|
+
},
|
|
117
|
+
body: JSON.stringify(envelope),
|
|
118
|
+
// A hung cloud socket must not stall shipping rounds indefinitely — abort
|
|
119
|
+
// and let the caller's backoff handle it.
|
|
120
|
+
signal: AbortSignal.timeout(10_000),
|
|
121
|
+
});
|
|
122
|
+
if (!res.ok) {
|
|
123
|
+
throw new Error(`sync ship failed: HTTP ${res.status}`);
|
|
124
|
+
}
|
|
125
|
+
const body = (await res.json());
|
|
126
|
+
const ackedSeq = typeof body.acked_seq === 'number' ? body.acked_seq : -1;
|
|
127
|
+
if (ackedSeq < 0) {
|
|
128
|
+
throw new Error('sync ship failed: response missing acked_seq');
|
|
129
|
+
}
|
|
130
|
+
// Mark ONLY the rows we actually shipped and the server acked. A partial
|
|
131
|
+
// ack (acked_seq mid-batch) leaves the tail unsynced for the next round.
|
|
132
|
+
const shippedAcked = rows.filter(r => r.seq <= ackedSeq).map(r => r.seq);
|
|
133
|
+
if (shippedAcked.length > 0) {
|
|
134
|
+
const now = Date.now();
|
|
135
|
+
const placeholders = shippedAcked.map(() => '?').join(', ');
|
|
136
|
+
opts.db
|
|
137
|
+
.prepare(`UPDATE memory_events SET synced_at = ? WHERE seq IN (${placeholders}) AND synced_at IS NULL`)
|
|
138
|
+
.run(now, ...shippedAcked);
|
|
139
|
+
}
|
|
140
|
+
return { shipped: rows.length, acked: shippedAcked.length, idle: false };
|
|
141
|
+
}
|
|
142
|
+
/** Pure backoff schedule: base * 2^failures, capped. Exported for tests. */
|
|
143
|
+
export function backoffDelay(baseMs, consecutiveFailures) {
|
|
144
|
+
return Math.min(baseMs * 2 ** consecutiveFailures, BACKOFF_CAP_MS);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Start the periodic shipper loop. Ships immediately, then every intervalMs;
|
|
148
|
+
* on failure the next attempt is delayed by exponential backoff.
|
|
149
|
+
*/
|
|
150
|
+
export function startShipper(opts) {
|
|
151
|
+
const intervalMs = opts.intervalMs ?? DEFAULT_INTERVAL_MS;
|
|
152
|
+
let stopped = false;
|
|
153
|
+
let timer = null;
|
|
154
|
+
let inFlight = null;
|
|
155
|
+
let failures = 0;
|
|
156
|
+
async function round() {
|
|
157
|
+
if (stopped)
|
|
158
|
+
return;
|
|
159
|
+
try {
|
|
160
|
+
const result = await shipOnce(opts);
|
|
161
|
+
failures = 0;
|
|
162
|
+
if (!result.idle) {
|
|
163
|
+
logger.info({ shipped: result.shipped, acked: result.acked }, 'sync: batch shipped');
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
failures++;
|
|
168
|
+
logger.warn({ err: err.message, consecutive_failures: failures }, 'sync: ship failed — will retry with backoff');
|
|
169
|
+
}
|
|
170
|
+
if (!stopped) {
|
|
171
|
+
const delay = failures > 0 ? backoffDelay(intervalMs, failures) : intervalMs;
|
|
172
|
+
timer = setTimeout(launch, delay);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function launch() {
|
|
176
|
+
inFlight = round().finally(() => { inFlight = null; });
|
|
177
|
+
}
|
|
178
|
+
launch();
|
|
179
|
+
return {
|
|
180
|
+
async stop() {
|
|
181
|
+
stopped = true;
|
|
182
|
+
if (timer)
|
|
183
|
+
clearTimeout(timer);
|
|
184
|
+
if (inFlight)
|
|
185
|
+
await inFlight;
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=shipper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shipper.js","sourceRoot":"","sources":["../../src/sync/shipper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* runTagHygieneBackfill — FEAT-403 core (DB-only, CLI-independent so it's
|
|
3
|
+
* directly unit-testable without spawning the daemon).
|
|
4
|
+
*
|
|
5
|
+
* Candidate selection (`WHERE repo blank OR project blank`) is what makes
|
|
6
|
+
* this idempotent by construction (AC-3): a fully-tagged row is never
|
|
7
|
+
* selected on a re-run, so re-running on a fully-tagged corpus is always a
|
|
8
|
+
* pure no-op scan with zero mutation.
|
|
9
|
+
*
|
|
10
|
+
* Tier-1 (session cwd/project_id) and tier-2 (prompt-assisted, confidence-
|
|
11
|
+
* gated) are applied per-row; tier-2 only runs when tier-1 found nothing at
|
|
12
|
+
* all for that row (never mixes a tier-1 partial fill with a tier-2 guess in
|
|
13
|
+
* the same row — keeps the mis-tag blast radius per row to one tier).
|
|
14
|
+
*
|
|
15
|
+
* Mutations run outside a single wrapping transaction, one UPDATE per
|
|
16
|
+
* applied row: tier-2 calls an LLM per candidate (async), and better-sqlite3
|
|
17
|
+
* transactions must be synchronous. This is also a deliberately resumable
|
|
18
|
+
* shape (durability principle) — if the process is interrupted mid-run, a
|
|
19
|
+
* re-run picks up exactly where it left off via the same WHERE clause.
|
|
20
|
+
*/
|
|
21
|
+
import type { DB } from '../storage/db.js';
|
|
22
|
+
import type { Tier2InferFn } from './tier2-infer.js';
|
|
23
|
+
export interface BackfillOptions {
|
|
24
|
+
dryRun: boolean;
|
|
25
|
+
limit?: number;
|
|
26
|
+
tier2Enabled: boolean;
|
|
27
|
+
/** Minimum tier-2 confidence required to apply an inferred tag (AC-4). */
|
|
28
|
+
confidenceThreshold: number;
|
|
29
|
+
}
|
|
30
|
+
export interface BackfillCandidate {
|
|
31
|
+
id: string;
|
|
32
|
+
currentRepo: string | null;
|
|
33
|
+
currentProject: string | null;
|
|
34
|
+
proposedRepo: string | null;
|
|
35
|
+
proposedProject: string | null;
|
|
36
|
+
/** null when the memory was left untagged (no signal, or tier-2 confidence too low). */
|
|
37
|
+
tier: 1 | 2 | null;
|
|
38
|
+
/** Only set when tier-2 ran for this row. */
|
|
39
|
+
confidence?: number;
|
|
40
|
+
applied: boolean;
|
|
41
|
+
}
|
|
42
|
+
export interface BackfillResult {
|
|
43
|
+
scanned: number;
|
|
44
|
+
tier1Applied: number;
|
|
45
|
+
tier2Applied: number;
|
|
46
|
+
leftUntagged: number;
|
|
47
|
+
dryRun: boolean;
|
|
48
|
+
candidates: BackfillCandidate[];
|
|
49
|
+
}
|
|
50
|
+
export declare function runTagHygieneBackfill(db: DB, opts: BackfillOptions, tier2Infer?: Tier2InferFn): Promise<BackfillResult>;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* runTagHygieneBackfill — FEAT-403 core (DB-only, CLI-independent so it's
|
|
3
|
+
* directly unit-testable without spawning the daemon).
|
|
4
|
+
*
|
|
5
|
+
* Candidate selection (`WHERE repo blank OR project blank`) is what makes
|
|
6
|
+
* this idempotent by construction (AC-3): a fully-tagged row is never
|
|
7
|
+
* selected on a re-run, so re-running on a fully-tagged corpus is always a
|
|
8
|
+
* pure no-op scan with zero mutation.
|
|
9
|
+
*
|
|
10
|
+
* Tier-1 (session cwd/project_id) and tier-2 (prompt-assisted, confidence-
|
|
11
|
+
* gated) are applied per-row; tier-2 only runs when tier-1 found nothing at
|
|
12
|
+
* all for that row (never mixes a tier-1 partial fill with a tier-2 guess in
|
|
13
|
+
* the same row — keeps the mis-tag blast radius per row to one tier).
|
|
14
|
+
*
|
|
15
|
+
* Mutations run outside a single wrapping transaction, one UPDATE per
|
|
16
|
+
* applied row: tier-2 calls an LLM per candidate (async), and better-sqlite3
|
|
17
|
+
* transactions must be synchronous. This is also a deliberately resumable
|
|
18
|
+
* shape (durability principle) — if the process is interrupted mid-run, a
|
|
19
|
+
* re-run picks up exactly where it left off via the same WHERE clause.
|
|
20
|
+
*/
|
|
21
|
+
import { deriveRepoFromCwd } from './derive-repo.js';
|
|
22
|
+
function isBlank(v) {
|
|
23
|
+
return v === null || v === undefined || v.trim() === '';
|
|
24
|
+
}
|
|
25
|
+
export async function runTagHygieneBackfill(db, opts, tier2Infer) {
|
|
26
|
+
const limitClause = opts.limit !== undefined ? ` LIMIT ${Math.max(1, opts.limit)}` : '';
|
|
27
|
+
const rows = db.prepare(`
|
|
28
|
+
SELECT id, text, repo, project, session_id FROM memories
|
|
29
|
+
WHERE (repo IS NULL OR TRIM(repo) = '') OR (project IS NULL OR TRIM(project) = '')
|
|
30
|
+
ORDER BY created_at ASC${limitClause}
|
|
31
|
+
`).all();
|
|
32
|
+
const sessionStmt = db.prepare('SELECT repo, project, cwd FROM sessions WHERE id = ?');
|
|
33
|
+
const updateStmt = db.prepare('UPDATE memories SET repo = ?, project = ?, updated_at = ? WHERE id = ?');
|
|
34
|
+
// Known (repo, project) vocabulary from the already-tagged corpus — grounds
|
|
35
|
+
// tier-2 inference so it can only choose among names actually seen
|
|
36
|
+
// elsewhere, never invent one (see tier2-infer.ts). Queried once per run.
|
|
37
|
+
const knownPairs = opts.tier2Enabled && tier2Infer
|
|
38
|
+
? db.prepare(`
|
|
39
|
+
SELECT DISTINCT repo, project FROM memories
|
|
40
|
+
WHERE repo IS NOT NULL AND TRIM(repo) != '' AND project IS NOT NULL AND TRIM(project) != ''
|
|
41
|
+
`).all()
|
|
42
|
+
: [];
|
|
43
|
+
const candidates = [];
|
|
44
|
+
let tier1Applied = 0;
|
|
45
|
+
let tier2Applied = 0;
|
|
46
|
+
let leftUntagged = 0;
|
|
47
|
+
for (const row of rows) {
|
|
48
|
+
let proposedRepo = isBlank(row.repo) ? null : row.repo;
|
|
49
|
+
let proposedProject = isBlank(row.project) ? null : row.project;
|
|
50
|
+
let tier = null;
|
|
51
|
+
let confidence;
|
|
52
|
+
// ---- Tier 1: session metadata (repo, or project_id, or cwd) ----
|
|
53
|
+
if (row.session_id) {
|
|
54
|
+
const session = sessionStmt.get(row.session_id);
|
|
55
|
+
if (session) {
|
|
56
|
+
const sessionRepo = session.repo ?? deriveRepoFromCwd(session.cwd);
|
|
57
|
+
const sessionProject = session.project;
|
|
58
|
+
let changed = false;
|
|
59
|
+
if (isBlank(proposedRepo) && !isBlank(sessionRepo)) {
|
|
60
|
+
proposedRepo = sessionRepo;
|
|
61
|
+
changed = true;
|
|
62
|
+
}
|
|
63
|
+
if (isBlank(proposedProject) && !isBlank(sessionProject)) {
|
|
64
|
+
proposedProject = sessionProject;
|
|
65
|
+
changed = true;
|
|
66
|
+
}
|
|
67
|
+
if (changed)
|
|
68
|
+
tier = 1;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// ---- Tier 2: prompt-assisted inference — only when tier-1 found nothing ----
|
|
72
|
+
if (tier === null && opts.tier2Enabled && tier2Infer && (isBlank(proposedRepo) || isBlank(proposedProject))) {
|
|
73
|
+
const inferred = await tier2Infer(row.text, knownPairs);
|
|
74
|
+
confidence = inferred.confidence;
|
|
75
|
+
if (inferred.confidence >= opts.confidenceThreshold) {
|
|
76
|
+
if (isBlank(proposedRepo) && !isBlank(inferred.repo))
|
|
77
|
+
proposedRepo = inferred.repo;
|
|
78
|
+
if (isBlank(proposedProject) && !isBlank(inferred.project))
|
|
79
|
+
proposedProject = inferred.project;
|
|
80
|
+
if (!isBlank(proposedRepo) || !isBlank(proposedProject))
|
|
81
|
+
tier = 2;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const willApply = tier !== null && (proposedRepo !== row.repo || proposedProject !== row.project);
|
|
85
|
+
if (willApply) {
|
|
86
|
+
if (!opts.dryRun) {
|
|
87
|
+
updateStmt.run(proposedRepo, proposedProject, Date.now(), row.id);
|
|
88
|
+
}
|
|
89
|
+
if (tier === 1)
|
|
90
|
+
tier1Applied++;
|
|
91
|
+
else if (tier === 2)
|
|
92
|
+
tier2Applied++;
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
leftUntagged++;
|
|
96
|
+
}
|
|
97
|
+
candidates.push({
|
|
98
|
+
id: row.id,
|
|
99
|
+
currentRepo: row.repo,
|
|
100
|
+
currentProject: row.project,
|
|
101
|
+
proposedRepo,
|
|
102
|
+
proposedProject,
|
|
103
|
+
tier: willApply ? tier : null,
|
|
104
|
+
confidence,
|
|
105
|
+
applied: willApply && !opts.dryRun,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
scanned: rows.length,
|
|
110
|
+
tier1Applied,
|
|
111
|
+
tier2Applied,
|
|
112
|
+
leftUntagged,
|
|
113
|
+
dryRun: opts.dryRun,
|
|
114
|
+
candidates,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=backfill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backfill.js","sourceRoot":"","sources":["../../src/tag-hygiene/backfill.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* deriveRepoFromCwd — tier-1 heuristic (FEAT-403 AC-1).
|
|
3
|
+
*
|
|
4
|
+
* Given a session's working directory, derive a repo name as the last path
|
|
5
|
+
* segment. Handles both POSIX and Windows separators (the daemon runs
|
|
6
|
+
* cross-platform — see CLAUDE.md) and trailing slashes. Returns null for
|
|
7
|
+
* empty/root-only paths rather than guessing.
|
|
8
|
+
*/
|
|
9
|
+
export declare function deriveRepoFromCwd(cwd: string | null | undefined): string | null;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* deriveRepoFromCwd — tier-1 heuristic (FEAT-403 AC-1).
|
|
3
|
+
*
|
|
4
|
+
* Given a session's working directory, derive a repo name as the last path
|
|
5
|
+
* segment. Handles both POSIX and Windows separators (the daemon runs
|
|
6
|
+
* cross-platform — see CLAUDE.md) and trailing slashes. Returns null for
|
|
7
|
+
* empty/root-only paths rather than guessing.
|
|
8
|
+
*/
|
|
9
|
+
export function deriveRepoFromCwd(cwd) {
|
|
10
|
+
if (!cwd)
|
|
11
|
+
return null;
|
|
12
|
+
const trimmed = cwd.replace(/[\\/]+$/, '');
|
|
13
|
+
if (!trimmed)
|
|
14
|
+
return null;
|
|
15
|
+
const segments = trimmed.split(/[\\/]/).filter(Boolean);
|
|
16
|
+
const last = segments[segments.length - 1];
|
|
17
|
+
return last && last.length > 0 ? last : null;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=derive-repo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive-repo.js","sourceRoot":"","sources":["../../src/tag-hygiene/derive-repo.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAA8B;IAC9D,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IAC3C,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACxD,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC3C,OAAO,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tier-2 "prompt-assisted" inference (FEAT-403 AC-4).
|
|
3
|
+
*
|
|
4
|
+
* Runs only when tier-1 (session cwd/project_id, see derive-repo.ts +
|
|
5
|
+
* backfill.ts) found nothing to derive from. Grounded ONLY in the
|
|
6
|
+
* (repo, project) vocabulary already present elsewhere in the tagged
|
|
7
|
+
* corpus — the model is told to pick among known pairs or return null, never
|
|
8
|
+
* to invent a new name from thin air. Any parse failure, empty vocabulary,
|
|
9
|
+
* or provider error resolves to confidence 0 so the caller (backfill.ts)
|
|
10
|
+
* leaves the memory untagged rather than risk a mis-tag.
|
|
11
|
+
*/
|
|
12
|
+
import type { LLMProvider } from '../contracts/index.js';
|
|
13
|
+
export interface TagCandidatePair {
|
|
14
|
+
repo: string | null;
|
|
15
|
+
project: string | null;
|
|
16
|
+
}
|
|
17
|
+
export interface Tier2InferResult {
|
|
18
|
+
repo: string | null;
|
|
19
|
+
project: string | null;
|
|
20
|
+
confidence: number;
|
|
21
|
+
}
|
|
22
|
+
export type Tier2InferFn = (memoryText: string, knownPairs: TagCandidatePair[]) => Promise<Tier2InferResult>;
|
|
23
|
+
/**
|
|
24
|
+
* Default tier-2 inference, backed by an injected LLMProvider (reuse the
|
|
25
|
+
* existing provider abstraction — src/contracts/llm.ts, src/providers/index.ts
|
|
26
|
+
* — rather than a bespoke HTTP call).
|
|
27
|
+
*/
|
|
28
|
+
export declare function makeLlmTier2Infer(llm: LLMProvider): Tier2InferFn;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tier-2 "prompt-assisted" inference (FEAT-403 AC-4).
|
|
3
|
+
*
|
|
4
|
+
* Runs only when tier-1 (session cwd/project_id, see derive-repo.ts +
|
|
5
|
+
* backfill.ts) found nothing to derive from. Grounded ONLY in the
|
|
6
|
+
* (repo, project) vocabulary already present elsewhere in the tagged
|
|
7
|
+
* corpus — the model is told to pick among known pairs or return null, never
|
|
8
|
+
* to invent a new name from thin air. Any parse failure, empty vocabulary,
|
|
9
|
+
* or provider error resolves to confidence 0 so the caller (backfill.ts)
|
|
10
|
+
* leaves the memory untagged rather than risk a mis-tag.
|
|
11
|
+
*/
|
|
12
|
+
import { z } from 'zod';
|
|
13
|
+
const Tier2ResponseSchema = z.object({
|
|
14
|
+
repo: z.string().nullable().optional(),
|
|
15
|
+
project: z.string().nullable().optional(),
|
|
16
|
+
confidence: z.number().min(0).max(1),
|
|
17
|
+
});
|
|
18
|
+
const TIER2_SYSTEM_PROMPT = `You infer which repository/project a memory (fact, decision, or lesson) belongs to. You will be given a list of already-known repo/project pairs seen elsewhere in this corpus, and the memory's text. Pick ONE known pair if the text gives clear evidence, or return null for both fields if there is no clear signal — never invent a new repo/project name. Respond with strict JSON only: {"repo": string|null, "project": string|null, "confidence": number between 0 and 1}. confidence reflects how certain you are the memory belongs to the chosen pair; use a low confidence (< 0.5) when unsure.`;
|
|
19
|
+
/**
|
|
20
|
+
* Default tier-2 inference, backed by an injected LLMProvider (reuse the
|
|
21
|
+
* existing provider abstraction — src/contracts/llm.ts, src/providers/index.ts
|
|
22
|
+
* — rather than a bespoke HTTP call).
|
|
23
|
+
*/
|
|
24
|
+
export function makeLlmTier2Infer(llm) {
|
|
25
|
+
return async (memoryText, knownPairs) => {
|
|
26
|
+
if (knownPairs.length === 0) {
|
|
27
|
+
// No known vocabulary to ground a guess in — never invent from thin air.
|
|
28
|
+
return { repo: null, project: null, confidence: 0 };
|
|
29
|
+
}
|
|
30
|
+
const pairsList = knownPairs
|
|
31
|
+
.map(p => `- repo=${p.repo ?? 'null'} project=${p.project ?? 'null'}`)
|
|
32
|
+
.join('\n');
|
|
33
|
+
const messages = [
|
|
34
|
+
{ role: 'system', content: TIER2_SYSTEM_PROMPT },
|
|
35
|
+
{
|
|
36
|
+
role: 'user',
|
|
37
|
+
content: `Known repo/project pairs seen in this corpus:\n${pairsList}\n\nMemory text:\n${memoryText}\n\nWhich pair (if any) does this memory belong to?`,
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
try {
|
|
41
|
+
const result = await llm.chat(messages, { temperature: 0, json: true, maxTokens: 200 });
|
|
42
|
+
return parseTier2Response(result.text) ?? { repo: null, project: null, confidence: 0 };
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return { repo: null, project: null, confidence: 0 };
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/** Parse the LLM's JSON response, stripping markdown fences if present. */
|
|
50
|
+
function parseTier2Response(raw) {
|
|
51
|
+
try {
|
|
52
|
+
let text = raw.trim();
|
|
53
|
+
text = text.replace(/^```(?:json)?\n?/i, '').replace(/\n?```\s*$/i, '').trim();
|
|
54
|
+
const start = text.indexOf('{');
|
|
55
|
+
const end = text.lastIndexOf('}');
|
|
56
|
+
if (start === -1 || end === -1)
|
|
57
|
+
return null;
|
|
58
|
+
const parsedJson = JSON.parse(text.slice(start, end + 1));
|
|
59
|
+
const validated = Tier2ResponseSchema.safeParse(parsedJson);
|
|
60
|
+
if (!validated.success)
|
|
61
|
+
return null;
|
|
62
|
+
return {
|
|
63
|
+
repo: validated.data.repo ?? null,
|
|
64
|
+
project: validated.data.project ?? null,
|
|
65
|
+
confidence: validated.data.confidence,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=tier2-infer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tier2-infer.js","sourceRoot":"","sources":["../../src/tag-hygiene/tier2-infer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DB } from '../storage/db.js';
|
|
2
|
+
import type { VectorStore, VecFilter, VecHit } from '../contracts/index.js';
|
|
3
|
+
export declare class SqliteVecStore implements VectorStore {
|
|
4
|
+
private db;
|
|
5
|
+
private dim;
|
|
6
|
+
readonly name: "sqlite-vec";
|
|
7
|
+
/** FEAT-409: dim defaults to DEFAULT_EMBED_DIM (1024) for backward
|
|
8
|
+
* compat — callers with a config/provider in scope should pass
|
|
9
|
+
* embed.dim (or cfg.embedding.dim) explicitly so this matches the
|
|
10
|
+
* memories_vec schema the DB was actually created with. */
|
|
11
|
+
constructor(db: DB, dim?: number);
|
|
12
|
+
upsert(id: string, vec: Float32Array): Promise<void>;
|
|
13
|
+
search(vec: Float32Array, k: number, filter?: VecFilter): Promise<VecHit[]>;
|
|
14
|
+
clear(): Promise<void>;
|
|
15
|
+
private allocateRowid;
|
|
16
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { DEFAULT_EMBED_DIM } from '../contracts/embed.js';
|
|
2
|
+
export class SqliteVecStore {
|
|
3
|
+
db;
|
|
4
|
+
dim;
|
|
5
|
+
name = 'sqlite-vec';
|
|
6
|
+
/** FEAT-409: dim defaults to DEFAULT_EMBED_DIM (1024) for backward
|
|
7
|
+
* compat — callers with a config/provider in scope should pass
|
|
8
|
+
* embed.dim (or cfg.embedding.dim) explicitly so this matches the
|
|
9
|
+
* memories_vec schema the DB was actually created with. */
|
|
10
|
+
constructor(db, dim = DEFAULT_EMBED_DIM) {
|
|
11
|
+
this.db = db;
|
|
12
|
+
this.dim = dim;
|
|
13
|
+
}
|
|
14
|
+
async upsert(id, vec) {
|
|
15
|
+
if (vec.length !== this.dim)
|
|
16
|
+
throw new Error(`expected dim ${this.dim}, got ${vec.length}`);
|
|
17
|
+
const memoriesRowid = this.allocateRowid(id);
|
|
18
|
+
const buf = Buffer.from(vec.buffer);
|
|
19
|
+
const existing = this.db.prepare('SELECT rowid FROM memories_vec WHERE rowid = ?').get(BigInt(memoriesRowid));
|
|
20
|
+
if (existing) {
|
|
21
|
+
this.db.prepare('UPDATE memories_vec SET embedding = ? WHERE rowid = ?').run(buf, BigInt(memoriesRowid));
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
this.db.prepare('INSERT INTO memories_vec (rowid, embedding) VALUES (?, ?)').run(BigInt(memoriesRowid), buf);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async search(vec, k, filter) {
|
|
28
|
+
if (filter && (filter.type?.length || filter.repo || filter.project || filter.since !== undefined)) {
|
|
29
|
+
throw new Error('VecFilter not yet implemented in sqlite-vec adapter — apply filters in the search layer instead');
|
|
30
|
+
}
|
|
31
|
+
const rows = this.db.prepare(`
|
|
32
|
+
SELECT m.id, mv.distance
|
|
33
|
+
FROM memories_vec mv
|
|
34
|
+
JOIN memories m ON m.rowid = mv.rowid
|
|
35
|
+
WHERE mv.embedding MATCH ? AND mv.k = ?
|
|
36
|
+
`).all(Buffer.from(vec.buffer), k);
|
|
37
|
+
return rows.map(r => ({ id: r.id, score: 1 / (1 + r.distance) }));
|
|
38
|
+
}
|
|
39
|
+
async clear() {
|
|
40
|
+
this.db.exec('DELETE FROM memories_vec');
|
|
41
|
+
}
|
|
42
|
+
allocateRowid(id) {
|
|
43
|
+
const r = this.db.prepare('SELECT rowid FROM memories WHERE id = ?').get(id);
|
|
44
|
+
if (!r)
|
|
45
|
+
throw new Error(`memory ${id} not in memories table — insert there first`);
|
|
46
|
+
return r.rowid;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=sqlite-vec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-vec.js","sourceRoot":"","sources":["../../src/vector/sqlite-vec.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE1D,MAAM,OAAO,cAAc;IAML;IAAgB;IAL3B,IAAI,GAAG,YAAqB,CAAC;IACtC;;;+DAG2D;IAC3D,YAAoB,EAAM,EAAU,MAAc,iBAAiB;QAA/C,OAAE,GAAF,EAAE,CAAI;QAAU,QAAG,GAAH,GAAG,CAA4B;IAAG,CAAC;IAEvE,KAAK,CAAC,MAAM,CAAC,EAAU,EAAE,GAAiB;QACxC,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,IAAI,CAAC,GAAG,SAAS,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QAC5F,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QAC7C,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,gDAAgD,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,CAAgC,CAAC;QAC7I,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,uDAAuD,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC;QAC3G,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,2DAA2D,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,GAAG,CAAC,CAAC;QAC/G,CAAC;IACH,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAiB,EAAE,CAAS,EAAE,MAAkB;QAC3D,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,CAAC,EAAE,CAAC;YACnG,MAAM,IAAI,KAAK,CAAC,iGAAiG,CAAC,CAAC;QACrH,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC;;;;;KAK5B,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAqC,CAAC;QACvE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;IACpE,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IAC3C,CAAC;IAEO,aAAa,CAAC,EAAU;QAC9B,MAAM,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,yCAAyC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAgC,CAAC;QAC5G,IAAI,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,UAAU,EAAE,6CAA6C,CAAC,CAAC;QACnF,OAAO,CAAC,CAAC,KAAK,CAAC;IACjB,CAAC;CACF"}
|