hippo-memory 1.71.0 → 1.72.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/README.md +1 -1
- package/dist/agent-memories/apply.d.ts +0 -11
- package/dist/agent-memories/apply.js +6 -21
- package/dist/agent-memories/claude-code.js +1 -1
- package/dist/agent-memories/codex.js +1 -1
- package/dist/agent-memories/copilot.d.ts +1 -3
- package/dist/agent-memories/copilot.js +1 -17
- package/dist/agent-memories/project-folds.d.ts +10 -0
- package/dist/agent-memories/project-folds.js +34 -0
- package/dist/agent-memories/sync.js +8 -6
- package/dist/api/assemble.js +1 -1
- package/dist/api/audit.d.ts +29 -1
- package/dist/api/audit.js +44 -5
- package/dist/api/auth.d.ts +7 -4
- package/dist/api/auth.js +12 -8
- package/dist/api/auto-sleep.d.ts +5 -2
- package/dist/api/auto-sleep.js +7 -3
- package/dist/api/availability.js +2 -3
- package/dist/api/cards.d.ts +42 -0
- package/dist/api/cards.js +52 -0
- package/dist/api/conflicts.d.ts +28 -0
- package/dist/api/conflicts.js +61 -0
- package/dist/api/context-render.js +4 -3
- package/dist/api/context-select.js +9 -11
- package/dist/api/context.js +17 -8
- package/dist/api/continuity.d.ts +47 -0
- package/dist/api/continuity.js +66 -0
- package/dist/api/dormant.js +4 -2
- package/dist/api/drill-down.js +1 -1
- package/dist/api/embeddings.d.ts +17 -0
- package/dist/api/embeddings.js +28 -0
- package/dist/api/error-reply.d.ts +8 -0
- package/dist/api/error-reply.js +21 -0
- package/dist/api/forget.d.ts +6 -2
- package/dist/api/forget.js +18 -7
- package/dist/api/goals.d.ts +1 -1
- package/dist/api/goals.js +5 -9
- package/dist/api/index.d.ts +4 -2
- package/dist/api/index.js +4 -2
- package/dist/api/instruction-detect.js +5 -1
- package/dist/api/learn.d.ts +4 -0
- package/dist/api/learn.js +53 -27
- package/dist/api/ledger-db.d.ts +2 -6
- package/dist/api/ledger-db.js +4 -36
- package/dist/api/memories.d.ts +19 -1
- package/dist/api/memories.js +39 -0
- package/dist/api/objects.d.ts +1 -1
- package/dist/api/outcome.js +1 -1
- package/dist/api/pilot-arm.d.ts +1 -11
- package/dist/api/pilot-arm.js +5 -45
- package/dist/api/predictions.d.ts +3 -1
- package/dist/api/predictions.js +6 -0
- package/dist/api/projects.d.ts +21 -0
- package/dist/api/projects.js +24 -0
- package/dist/api/promote.js +1 -1
- package/dist/api/prompt-hook.d.ts +15 -2
- package/dist/api/prompt-hook.js +44 -4
- package/dist/api/recall-cli.d.ts +3 -0
- package/dist/api/recall-cli.js +13 -4
- package/dist/api/recall-core.js +3 -3
- package/dist/api/recall-pipeline.js +6 -7
- package/dist/api/recall-request.js +1 -1
- package/dist/api/recall.d.ts +0 -3
- package/dist/api/recall.js +19 -44
- package/dist/api/remember-local.js +1 -1
- package/dist/api/remember.d.ts +2 -1
- package/dist/api/remember.js +3 -2
- package/dist/api/session-end.d.ts +22 -0
- package/dist/api/session-end.js +101 -0
- package/dist/api/sleep-run.d.ts +1 -1
- package/dist/api/sleep-run.js +13 -9
- package/dist/api/status.d.ts +24 -0
- package/dist/api/status.js +40 -0
- package/dist/api/store-init.d.ts +4 -0
- package/dist/api/store-init.js +9 -0
- package/dist/api/tokens.js +1 -1
- package/dist/api/types.d.ts +2 -0
- package/dist/api/types.js +6 -1
- package/dist/capture/caller-session.d.ts +0 -3
- package/dist/capture/caller-session.js +1 -12
- package/dist/capture/capture-error.d.ts +2 -2
- package/dist/capture/capture-error.js +23 -18
- package/dist/capture/codex-patch.js +3 -1
- package/dist/capture/command.js +36 -95
- package/dist/capture/compact.js +10 -5
- package/dist/capture/compaction-items-caller.js +11 -20
- package/dist/capture/compaction-record.d.ts +7 -93
- package/dist/capture/compaction-record.js +29 -426
- package/dist/capture/compaction-spool.d.ts +4 -2
- package/dist/capture/compaction-spool.js +20 -6
- package/dist/capture/copilot-transcript.d.ts +2 -1
- package/dist/capture/copilot-transcript.js +2 -1
- package/dist/capture/extract.d.ts +5 -2
- package/dist/capture/extract.js +4 -3
- package/dist/capture/failure-caller.d.ts +1 -1
- package/dist/capture/failure-caller.js +21 -16
- package/dist/capture/failure-reading.d.ts +2 -1
- package/dist/capture/failure-reading.js +7 -9
- package/dist/capture/pre-compact-caller.js +5 -4
- package/dist/capture/session-digest.js +4 -3
- package/dist/capture/session-end-caller.js +2 -1
- package/dist/capture/session-worker.js +3 -4
- package/dist/capture/transcript.d.ts +2 -1
- package/dist/capture/transcript.js +6 -3
- package/dist/capture/working-state.d.ts +4 -2
- package/dist/capture/working-state.js +4 -2
- package/dist/cli/audit-prune.d.ts +0 -25
- package/dist/cli/audit-prune.js +1 -27
- package/dist/cli/audit.js +13 -13
- package/dist/cli/auth.d.ts +1 -1
- package/dist/cli/auth.js +12 -13
- package/dist/cli/briefs.d.ts +1 -4
- package/dist/cli/briefs.js +37 -292
- package/dist/cli/card.js +41 -33
- package/dist/cli/client.d.ts +5 -0
- package/dist/cli/client.js +24 -8
- package/dist/cli/compact-resume-payload.js +1 -1
- package/dist/cli/context.d.ts +1 -1
- package/dist/cli/context.js +12 -12
- package/dist/cli/continuity.d.ts +1 -1
- package/dist/cli/continuity.js +36 -39
- package/dist/cli/curate.d.ts +1 -1
- package/dist/cli/curate.js +23 -27
- package/dist/cli/dag.d.ts +1 -1
- package/dist/cli/dag.js +23 -8
- package/dist/cli/decisions.d.ts +0 -2
- package/dist/cli/decisions.js +4 -281
- package/dist/cli/eval.d.ts +1 -1
- package/dist/cli/eval.js +12 -13
- package/dist/cli/explain.d.ts +1 -1
- package/dist/cli/explain.js +21 -25
- package/dist/cli/flag-values.js +16 -6
- package/dist/cli/flags.d.ts +0 -289
- package/dist/cli/flags.js +3 -138
- package/dist/cli/graph.d.ts +3 -0
- package/dist/cli/graph.js +83 -0
- package/dist/cli/hook-runtime.d.ts +3 -5
- package/dist/cli/hook-runtime.js +7 -34
- package/dist/cli/incidents.d.ts +3 -0
- package/dist/cli/incidents.js +112 -0
- package/dist/cli/init.d.ts +1 -1
- package/dist/cli/init.js +28 -19
- package/dist/cli/install-steps.d.ts +2 -1
- package/dist/cli/install-steps.js +11 -3
- package/dist/cli/last-sleep.js +1 -1
- package/dist/cli/lenient-id.d.ts +2 -0
- package/dist/cli/lenient-id.js +13 -0
- package/dist/cli/maintenance.d.ts +3 -3
- package/dist/cli/maintenance.js +29 -30
- package/dist/cli/notes.d.ts +2 -0
- package/dist/cli/notes.js +41 -0
- package/dist/cli/open-browser.d.ts +14 -0
- package/dist/cli/open-browser.js +26 -0
- package/dist/cli/playbooks.d.ts +3 -4
- package/dist/cli/playbooks.js +126 -290
- package/dist/cli/predictions.d.ts +3 -0
- package/dist/cli/predictions.js +175 -0
- package/dist/cli/print.js +2 -1
- package/dist/cli/projects.js +30 -22
- package/dist/cli/quality-repair-once.d.ts +1 -1
- package/dist/cli/quality-repair-once.js +6 -5
- package/dist/cli/recall.js +13 -7
- package/dist/cli/refine-llm.js +17 -17
- package/dist/cli/remember.js +12 -12
- package/dist/cli/scheduler.js +2 -1
- package/dist/cli/serve.js +3 -3
- package/dist/cli/session-hooks.d.ts +1 -1
- package/dist/cli/session-hooks.js +45 -115
- package/dist/cli/setup.d.ts +1 -1
- package/dist/cli/setup.js +17 -10
- package/dist/cli/shared.d.ts +2 -6
- package/dist/cli/shared.js +9 -29
- package/dist/cli/sleep-render.js +6 -3
- package/dist/cli/sleep.js +6 -37
- package/dist/cli/status.d.ts +3 -3
- package/dist/cli/status.js +32 -35
- package/dist/cli/transfer.d.ts +1 -1
- package/dist/cli/transfer.js +33 -31
- package/dist/cli/usage.d.ts +0 -79
- package/dist/cli/usage.js +1 -695
- package/dist/cli/verb-row.d.ts +32 -0
- package/dist/cli/verb-row.js +6 -0
- package/dist/cli/verbs/health.d.ts +27 -0
- package/dist/cli/verbs/health.js +232 -0
- package/dist/cli/verbs/memory.d.ts +16 -0
- package/dist/cli/verbs/memory.js +244 -0
- package/dist/cli/verbs/objects.d.ts +20 -0
- package/dist/cli/verbs/objects.js +254 -0
- package/dist/cli/verbs/sessions.d.ts +31 -0
- package/dist/cli/verbs/sessions.js +312 -0
- package/dist/cli/verbs.d.ts +81 -0
- package/dist/cli/verbs.js +7 -0
- package/dist/cli/versioned-verbs.d.ts +51 -0
- package/dist/cli/versioned-verbs.js +79 -0
- package/dist/cli.d.ts +1 -272
- package/dist/cli.js +23 -313
- package/dist/connectors/dlq.d.ts +39 -3
- package/dist/connectors/dlq.js +37 -3
- package/dist/connectors/github/backfill.js +12 -3
- package/dist/connectors/github/dlq.js +31 -73
- package/dist/connectors/github/ingest.d.ts +2 -6
- package/dist/connectors/github/ingest.js +3 -28
- package/dist/connectors/github/webhook.d.ts +2 -1
- package/dist/connectors/github/webhook.js +33 -2
- package/dist/connectors/ingest.d.ts +10 -0
- package/dist/connectors/ingest.js +28 -0
- package/dist/connectors/slack/dlq.js +49 -71
- package/dist/connectors/slack/ingest.d.ts +2 -6
- package/dist/connectors/slack/ingest.js +2 -27
- package/dist/connectors/slack/webhook.d.ts +2 -1
- package/dist/connectors/slack/webhook.js +2 -1
- package/dist/consolidate/conflicts.js +1 -1
- package/dist/consolidate/dag.js +15 -9
- package/dist/consolidate/dedupe.d.ts +7 -0
- package/dist/consolidate/dedupe.js +5 -3
- package/dist/consolidate/llm-passes.js +45 -19
- package/dist/consolidate/memory-value.js +2 -1
- package/dist/consolidate/merge.js +2 -2
- package/dist/consolidate/physics-pass.d.ts +6 -1
- package/dist/consolidate/physics-pass.js +26 -6
- package/dist/consolidate/replay.js +11 -4
- package/dist/consolidate/sleep.js +8 -5
- package/dist/consolidate/traces.js +3 -3
- package/dist/core/ablation.d.ts +10 -0
- package/dist/core/ablation.js +12 -0
- package/dist/{sharing/scope.d.ts → core/active-scope.d.ts} +1 -1
- package/dist/{sharing/scope.js → core/active-scope.js} +1 -1
- package/dist/core/ambient.d.ts +4 -0
- package/dist/core/ambient.js +39 -18
- package/dist/core/api-errors.d.ts +16 -0
- package/dist/core/api-errors.js +20 -0
- package/dist/core/card.js +3 -1
- package/dist/core/compaction-timing.d.ts +7 -0
- package/dist/core/compaction-timing.js +9 -0
- package/dist/core/config.d.ts +1 -1
- package/dist/core/config.js +7 -2
- package/dist/core/cosine.d.ts +3 -0
- package/dist/core/cosine.js +19 -0
- package/dist/{store → core}/markdown.d.ts +1 -1
- package/dist/{store → core}/markdown.js +2 -2
- package/dist/core/memory-audit.d.ts +17 -0
- package/dist/core/memory-audit.js +54 -0
- package/dist/core/memory.d.ts +10 -4
- package/dist/core/memory.js +25 -18
- package/dist/{consolidate → core}/merged-row.d.ts +1 -1
- package/dist/{consolidate → core}/merged-row.js +1 -1
- package/dist/core/physics.js +6 -16
- package/dist/core/prediction.d.ts +3 -0
- package/dist/core/prediction.js +6 -0
- package/dist/core/project-identity.d.ts +3 -0
- package/dist/core/project-identity.js +10 -1
- package/dist/{store → core}/recall-scope.d.ts +1 -10
- package/dist/{store → core}/recall-scope.js +2 -19
- package/dist/db/busy.d.ts +2 -7
- package/dist/db/busy.js +21 -26
- package/dist/db/connect.d.ts +0 -1
- package/dist/db/connect.js +5 -6
- package/dist/db/index.d.ts +3 -3
- package/dist/db/index.js +3 -3
- package/dist/db/migrations/index.d.ts +1 -1
- package/dist/db/migrations/index.js +3 -1
- package/dist/db/migrations/v55.d.ts +3 -0
- package/dist/db/migrations/v55.js +17 -0
- package/dist/db/open.d.ts +0 -2
- package/dist/db/open.js +6 -10
- package/dist/db/physics-state.js +3 -3
- package/dist/db/tables.d.ts +2 -0
- package/dist/db/tables.js +6 -2
- package/dist/db/vector-store.js +17 -4
- package/dist/db/wal-checkpointer.js +2 -1
- package/dist/doctor.js +11 -8
- package/dist/embeddings/local.d.ts +62 -0
- package/dist/embeddings/local.js +146 -0
- package/dist/{store/embeddings → embeddings}/provider.d.ts +5 -2
- package/dist/{store/embeddings → embeddings}/provider.js +18 -15
- package/dist/embeddings/transformers.d.ts +26 -0
- package/dist/embeddings/transformers.js +89 -0
- package/dist/entry/session-text.d.ts +1 -1
- package/dist/entry/session-text.js +1 -1
- package/dist/eval/eval-suite.js +17 -11
- package/dist/eval/eval.js +1 -1
- package/dist/eval/judgment.js +11 -4
- package/dist/graph/extract.js +3 -3
- package/dist/graph/recall.js +1 -1
- package/dist/graph/view.js +13 -5
- package/dist/graph/write.d.ts +1 -1
- package/dist/graph/write.js +9 -9
- package/dist/hooks/codex-session.js +3 -2
- package/dist/hooks/codex-wrapper.js +2 -1
- package/dist/hooks/json-hooks.js +4 -2
- package/dist/hooks/opencode.js +8 -5
- package/dist/hooks/shared.d.ts +0 -6
- package/dist/hooks/shared.js +4 -15
- package/dist/importers/core.js +1 -1
- package/dist/importers/sources.js +5 -4
- package/dist/importers/vault.js +7 -5
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/learn/autolearn.d.ts +10 -2
- package/dist/learn/autolearn.js +32 -12
- package/dist/learn/churn-git.js +13 -4
- package/dist/learn/extract.d.ts +3 -2
- package/dist/learn/extract.js +6 -4
- package/dist/learn/forward-claim-detector.js +4 -2
- package/dist/learn/invalidation.js +10 -6
- package/dist/mcp/admin-tools.d.ts +2 -2
- package/dist/mcp/admin-tools.js +27 -48
- package/dist/mcp/format.js +3 -2
- package/dist/mcp/framing.d.ts +1 -0
- package/dist/mcp/framing.js +1 -1
- package/dist/mcp/memory-tools.js +3 -1
- package/dist/mcp/protocol.d.ts +11 -7
- package/dist/mcp/protocol.js +25 -12
- package/dist/mcp/recall-tools.js +12 -9
- package/dist/mcp/request.js +2 -1
- package/dist/mcp/stdio.js +24 -8
- package/dist/mcp/tool-args.js +2 -1
- package/dist/mcp/tools.js +44 -21
- package/dist/objects/customer-notes.d.ts +2 -2
- package/dist/objects/decisions.d.ts +2 -2
- package/dist/objects/descriptor.d.ts +1 -1
- package/dist/objects/incidents.d.ts +2 -2
- package/dist/objects/lifecycle.d.ts +1 -1
- package/dist/objects/lifecycle.js +2 -1
- package/dist/objects/policies.d.ts +2 -2
- package/dist/objects/policies.js +2 -1
- package/dist/objects/processes.d.ts +2 -2
- package/dist/objects/project-briefs.d.ts +2 -2
- package/dist/objects/project-briefs.js +2 -3
- package/dist/objects/skills.d.ts +2 -2
- package/dist/postinstall.js +1 -1
- package/dist/rerankers/clef.js +3 -6
- package/dist/rerankers/cross-encoder.d.ts +31 -2
- package/dist/rerankers/cross-encoder.js +63 -97
- package/dist/rerankers/index.js +5 -4
- package/dist/rerankers/jev.js +2 -4
- package/dist/rerankers/outage-warning.d.ts +3 -1
- package/dist/rerankers/outage-warning.js +5 -3
- package/dist/search/bm25-search.js +1 -1
- package/dist/search/boosts.js +1 -1
- package/dist/search/embed-memories.d.ts +9 -0
- package/dist/search/embed-memories.js +377 -0
- package/dist/search/goal-boost.d.ts +1 -1
- package/dist/search/goal-boost.js +6 -3
- package/dist/search/hybrid.d.ts +9 -2
- package/dist/search/hybrid.js +9 -4
- package/dist/search/physics-search.d.ts +6 -3
- package/dist/search/physics-search.js +22 -15
- package/dist/search/rerank.js +1 -1
- package/dist/search/vector.d.ts +8 -6
- package/dist/search/vector.js +10 -13
- package/dist/server/auth.d.ts +2 -0
- package/dist/server/auth.js +17 -7
- package/dist/server/boot.js +7 -66
- package/dist/server/client-ip.d.ts +0 -2
- package/dist/server/client-ip.js +3 -2
- package/dist/server/deadline.d.ts +0 -2
- package/dist/server/deadline.js +1 -7
- package/dist/server/defaults.d.ts +1 -0
- package/dist/server/defaults.js +1 -0
- package/dist/server/mcp-http.js +16 -11
- package/dist/server/request.js +7 -2
- package/dist/server/route-checks.d.ts +7 -0
- package/dist/server/route-checks.js +42 -0
- package/dist/server/route-table.d.ts +9 -8
- package/dist/server/route-table.js +127 -132
- package/dist/server/routes/admin.d.ts +8 -8
- package/dist/server/routes/admin.js +13 -21
- package/dist/server/routes/customer-notes.d.ts +6 -6
- package/dist/server/routes/customer-notes.js +1 -2
- package/dist/server/routes/decisions.d.ts +6 -6
- package/dist/server/routes/decisions.js +2 -4
- package/dist/server/routes/incidents.d.ts +6 -6
- package/dist/server/routes/incidents.js +2 -5
- package/dist/server/routes/memories.d.ts +9 -9
- package/dist/server/routes/memories.js +15 -38
- package/dist/server/routes/object-routes.d.ts +6 -6
- package/dist/server/routes/object-routes.js +4 -7
- package/dist/server/routes/policies.d.ts +7 -7
- package/dist/server/routes/policies.js +2 -4
- package/dist/server/routes/predictions.d.ts +6 -6
- package/dist/server/routes/predictions.js +9 -13
- package/dist/server/routes/processes.d.ts +6 -6
- package/dist/server/routes/processes.js +1 -2
- package/dist/server/routes/project-briefs.d.ts +7 -7
- package/dist/server/routes/project-briefs.js +2 -4
- package/dist/server/routes/recall.d.ts +5 -5
- package/dist/server/routes/recall.js +6 -10
- package/dist/server/routes/skills.d.ts +7 -7
- package/dist/server/routes/skills.js +2 -4
- package/dist/server/server-detect.d.ts +7 -1
- package/dist/server/server-detect.js +36 -11
- package/dist/server/sleep-child.js +26 -7
- package/dist/server/sleep-offload.d.ts +11 -5
- package/dist/server/sleep-offload.js +17 -11
- package/dist/server/types.d.ts +22 -10
- package/dist/server/validation.d.ts +1 -2
- package/dist/server/validation.js +6 -11
- package/dist/server.d.ts +5 -4
- package/dist/server.js +5 -4
- package/dist/sharing/global-store.js +2 -2
- package/dist/sharing/global-sync.js +38 -45
- package/dist/sharing/project-merge.d.ts +7 -14
- package/dist/sharing/project-merge.js +57 -121
- package/dist/sharing/search-both.js +1 -1
- package/dist/sharing/share.d.ts +2 -0
- package/dist/sharing/share.js +37 -34
- package/dist/store/ambient.js +5 -6
- package/dist/store/audit-event.d.ts +1 -1
- package/dist/store/audit.d.ts +0 -16
- package/dist/store/audit.js +4 -55
- package/dist/store/auth.d.ts +4 -2
- package/dist/store/auth.js +7 -3
- package/dist/store/candidates.d.ts +3 -2
- package/dist/store/candidates.js +42 -23
- package/dist/store/capture-write.d.ts +17 -0
- package/dist/store/capture-write.js +35 -0
- package/dist/store/cards.js +1 -1
- package/dist/store/compaction-caller.d.ts +18 -0
- package/dist/store/compaction-caller.js +29 -0
- package/dist/store/compactions-record.d.ts +115 -0
- package/dist/store/compactions-record.js +417 -0
- package/dist/store/compactions.js +6 -4
- package/dist/store/conflicts.js +7 -4
- package/dist/store/connectors/dlq.d.ts +10 -0
- package/dist/store/connectors/dlq.js +32 -0
- package/dist/store/connectors/github.d.ts +7 -9
- package/dist/store/connectors/github.js +30 -53
- package/dist/store/connectors/slack.d.ts +2 -11
- package/dist/store/connectors/slack.js +18 -47
- package/dist/store/dashboard-reads.js +7 -6
- package/dist/store/delete-and-batch.d.ts +6 -0
- package/dist/store/delete-and-batch.js +36 -3
- package/dist/store/delivery-recorder.d.ts +10 -3
- package/dist/store/delivery-recorder.js +12 -5
- package/dist/store/descendant-page.js +6 -3
- package/dist/store/dormant.d.ts +1 -1
- package/dist/store/dormant.js +3 -1
- package/dist/store/embeddings/index.d.ts +2 -12
- package/dist/store/embeddings/index.js +5 -374
- package/dist/store/entry-reads.d.ts +13 -4
- package/dist/store/entry-reads.js +47 -11
- package/dist/store/entry-writes.d.ts +5 -5
- package/dist/store/entry-writes.js +32 -12
- package/dist/store/failure-log-at.d.ts +8 -0
- package/dist/store/failure-log-at.js +16 -0
- package/dist/store/failure-log.d.ts +2 -2
- package/dist/store/failure-log.js +5 -3
- package/dist/{trust → store}/gated-write.d.ts +3 -1
- package/dist/{trust → store}/gated-write.js +7 -5
- package/dist/store/goals.js +20 -18
- package/dist/store/graph-reads.js +4 -2
- package/dist/store/graph-writes.d.ts +6 -6
- package/dist/store/graph-writes.js +48 -71
- package/dist/store/handoffs.d.ts +1 -1
- package/dist/store/handoffs.js +6 -6
- package/dist/store/index-and-stats.js +2 -2
- package/dist/store/key-writes.js +1 -1
- package/dist/store/ledger-turn.d.ts +9 -0
- package/dist/store/ledger-turn.js +38 -0
- package/dist/store/mirrors.js +10 -6
- package/dist/store/open.d.ts +0 -4
- package/dist/store/open.js +40 -53
- package/dist/store/pilot-arm.d.ts +12 -0
- package/dist/store/pilot-arm.js +42 -0
- package/dist/store/port.d.ts +9 -3
- package/dist/store/port.js +8 -0
- package/dist/store/predictions.d.ts +1 -2
- package/dist/store/predictions.js +3 -6
- package/dist/store/project-tags.d.ts +62 -0
- package/dist/store/project-tags.js +67 -0
- package/dist/store/quarantine.js +2 -1
- package/dist/store/recall-trace.d.ts +2 -0
- package/dist/store/recall-trace.js +18 -2
- package/dist/store/rejected-values.js +2 -1
- package/dist/store/rejection-probe.d.ts +4 -0
- package/dist/store/rejection-probe.js +19 -0
- package/dist/store/rejection.d.ts +0 -17
- package/dist/store/rejection.js +1 -21
- package/dist/store/rule-sql.d.ts +9 -0
- package/dist/store/rule-sql.js +21 -2
- package/dist/store/search-rows.js +9 -4
- package/dist/store/sessions.js +4 -2
- package/dist/store/set-aside.d.ts +14 -0
- package/dist/store/set-aside.js +23 -0
- package/dist/store/sqlite/busy.d.ts +3 -0
- package/dist/store/sqlite/busy.js +8 -0
- package/dist/store/sqlite/connector-events-group.js +4 -7
- package/dist/store/sqlite/connector-writes-group.js +3 -3
- package/dist/store/sqlite/entry-writes-group.js +2 -2
- package/dist/store/sqlite/error-codec.js +4 -3
- package/dist/store/sqlite/executor.js +10 -4
- package/dist/store/sqlite/graph-reads-group.js +1 -1
- package/dist/store/sqlite/held-connection.d.ts +9 -0
- package/dist/store/sqlite/held-connection.js +41 -0
- package/dist/store/sqlite/object-rows.d.ts +1 -1
- package/dist/store/sqlite/objects-group.d.ts +1 -1
- package/dist/store/sqlite/objects-group.js +1 -1
- package/dist/store/sqlite/store.js +14 -12
- package/dist/store/summaries.d.ts +3 -3
- package/dist/store/summaries.js +5 -4
- package/dist/store/token-ledger.d.ts +0 -7
- package/dist/store/token-ledger.js +0 -35
- package/dist/store/vector-index.js +3 -2
- package/dist/support-bundle.js +2 -3
- package/dist/trust/reject-flow.js +2 -2
- package/dist/util/agent-homes.d.ts +15 -0
- package/dist/util/agent-homes.js +34 -0
- package/dist/util/anthropic-messages.d.ts +3 -0
- package/dist/util/anthropic-messages.js +22 -5
- package/dist/util/chunked.d.ts +3 -0
- package/dist/util/chunked.js +10 -0
- package/dist/util/ellipsize.d.ts +3 -0
- package/dist/util/ellipsize.js +5 -0
- package/dist/util/env.d.ts +2 -1
- package/dist/util/env.js +2 -1
- package/dist/util/format.d.ts +3 -0
- package/dist/util/format.js +6 -0
- package/dist/util/hook-payload.d.ts +8 -0
- package/dist/util/hook-payload.js +37 -0
- package/dist/util/http-retry.d.ts +6 -0
- package/dist/util/http-retry.js +43 -5
- package/dist/util/http-util.d.ts +0 -8
- package/dist/util/http-util.js +10 -21
- package/dist/util/limits.d.ts +7 -0
- package/dist/util/limits.js +14 -0
- package/dist/util/log.d.ts +4 -0
- package/dist/util/log.js +9 -0
- package/dist/util/request-scope.d.ts +3 -1
- package/dist/util/request-scope.js +11 -6
- package/dist/util/secret-detect.js +6 -1
- package/dist/util/sqlite-blocked.d.ts +2 -0
- package/dist/util/sqlite-blocked.js +5 -0
- package/dist/util/stream-tee.d.ts +3 -0
- package/dist/util/stream-tee.js +35 -0
- package/dist/util/time.d.ts +4 -0
- package/dist/util/time.js +4 -0
- package/dist/util/version.d.ts +1 -1
- package/dist/util/version.js +1 -1
- package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
- package/extensions/openclaw-plugin/package.json +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +2 -2
- package/dist/store/embeddings/local.d.ts +0 -15
- package/dist/store/embeddings/local.js +0 -175
- /package/dist/{cli → api}/correction-latency.d.ts +0 -0
- /package/dist/{cli → api}/correction-latency.js +0 -0
- /package/dist/{cli → api}/provenance-coverage.d.ts +0 -0
- /package/dist/{cli → api}/provenance-coverage.js +0 -0
- /package/dist/{store → core}/object-types.d.ts +0 -0
- /package/dist/{store → core}/object-types.js +0 -0
- /package/dist/{capture → util}/compaction-items.d.ts +0 -0
- /package/dist/{capture → util}/compaction-items.js +0 -0
- /package/dist/{store → util}/yaml.d.ts +0 -0
- /package/dist/{store → util}/yaml.js +0 -0
|
@@ -1,19 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
import {
|
|
4
|
-
import * as fs from 'fs';
|
|
5
|
-
import * as path from 'path';
|
|
6
|
-
import { randomUUID } from 'crypto';
|
|
7
|
-
import { chunked, loadAllEntryIds, loadEntriesByIds } from '../entry-reads.js';
|
|
8
|
-
import { rethrowIfSqliteBlocked } from '../../db/index.js';
|
|
1
|
+
/** The stored vector index's identity: which model and text format built it, and when a provider change means a rebuild.
|
|
2
|
+
* Picking a provider and computing vectors sit in src/search/embed-memories.ts; this module only reads and writes what is stored. */
|
|
3
|
+
import { cosineOf } from '../../core/cosine.js';
|
|
9
4
|
import { EMBEDDING_MODEL_META_KEY } from '../../db/vector-store.js';
|
|
10
|
-
import {
|
|
11
|
-
import { indexedModel, pruneStoredVectors, replacesIndex, resetStoredParticles, saveEmbeddingIndex, saveIndexIdentity, saveStoredVectors, seedStoredParticle, storedIndexState, } from '../vector-index.js';
|
|
12
|
-
import { loadConfig } from '../../core/config.js';
|
|
13
|
-
import { resolveEmbeddingProvider } from './provider.js';
|
|
14
|
-
import { redactSecretsStrict } from '../../util/secret-detect.js';
|
|
5
|
+
import { indexedModel, saveIndexIdentity, storedIndexState } from '../vector-index.js';
|
|
15
6
|
import { errorMessage, log } from '../../util/log.js';
|
|
16
|
-
import {
|
|
7
|
+
import { rethrowIfSqliteBlocked } from '../../util/sqlite-blocked.js';
|
|
17
8
|
export { EMBEDDING_MODEL_META_KEY };
|
|
18
9
|
/** Bump when `embeddingInputText` changes existing vectors; folded into the stored index identity (`embeddingIndexIdentity`) so it reindexes like a model
|
|
19
10
|
* change. Format 2 = `path:*` tags excluded; format 1 (implicit, no suffix) included them. */
|
|
@@ -57,368 +48,8 @@ export function resolveIndexedEmbeddingModel(hippoRoot, index) {
|
|
|
57
48
|
export function embeddingModelRequiresReindex(hippoRoot, model, index) {
|
|
58
49
|
return indexNeedsRebuild(resolveIndexedEmbeddingModel(hippoRoot, index), model);
|
|
59
50
|
}
|
|
60
|
-
const STORE_PAGE = 64;
|
|
61
|
-
/** The rows for `ids`, a page at a time and in that order; an id deleted since it was listed is skipped. */
|
|
62
|
-
function* entryPages(hippoRoot, ids) {
|
|
63
|
-
for (const page of chunked(ids, STORE_PAGE)) {
|
|
64
|
-
const byId = new Map(loadEntriesByIds(hippoRoot, page).map((e) => [e.id, e]));
|
|
65
|
-
const entries = page.flatMap((id) => byId.get(id) ?? []);
|
|
66
|
-
if (entries.length > 0)
|
|
67
|
-
yield entries;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
async function rebuildEmbeddingIndex(hippoRoot, ids, provider) {
|
|
71
|
-
const rebuilt = {};
|
|
72
|
-
for (const entries of entryPages(hippoRoot, ids)) {
|
|
73
|
-
// On a hard transport/auth failure provider.embed throws, so the caller aborts WITHOUT saving
|
|
74
|
-
// a partial index (atomic reindex: the old index + stored identity are preserved).
|
|
75
|
-
const vectors = await provider.embed(entries.map((e) => embeddingInputText(e)), 'passage');
|
|
76
|
-
for (let i = 0; i < entries.length; i++) {
|
|
77
|
-
const vec = vectors[i];
|
|
78
|
-
if (vec && vec.length > 0) {
|
|
79
|
-
rebuilt[entries[i].id] = vec;
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
noteSkippedEmbedding(entries[i].id);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
return rebuilt;
|
|
87
|
-
}
|
|
88
|
-
function resetPhysicsFromIndex(hippoRoot, ids, index) {
|
|
89
|
-
try {
|
|
90
|
-
resetStoredParticles(hippoRoot, ids, index);
|
|
91
|
-
}
|
|
92
|
-
catch (err) {
|
|
93
|
-
// Best effort: retrieval still falls back without physics state.
|
|
94
|
-
log.warn(`physics reset after reindex failed: ${errorMessage(err)}`);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
/** A provider's `[]` row is a swallowed per-item failure; name the memory so the gap can be traced. */
|
|
98
|
-
function noteSkippedEmbedding(id) {
|
|
99
|
-
log.warn('memory not embedded; the next embed run retries it', { id });
|
|
100
|
-
}
|
|
101
51
|
/** Cosine similarity; handles unnormalized vectors, returns 0 for empty or mismatched vectors. */
|
|
102
52
|
export function cosineSimilarity(a, b) {
|
|
103
53
|
return cosineOf(a, b);
|
|
104
54
|
}
|
|
105
|
-
/** `cosineSimilarity` over any indexable vectors; a Float32 view reads the same doubles as its number[] copy, so the score is the same. */
|
|
106
|
-
export function cosineOf(a, b) {
|
|
107
|
-
if (a.length === 0 || b.length === 0 || a.length !== b.length)
|
|
108
|
-
return 0;
|
|
109
|
-
let dot = 0;
|
|
110
|
-
let normA = 0;
|
|
111
|
-
let normB = 0;
|
|
112
|
-
for (let i = 0; i < a.length; i++) {
|
|
113
|
-
dot += a[i] * b[i];
|
|
114
|
-
normA += a[i] * a[i];
|
|
115
|
-
normB += b[i] * b[i];
|
|
116
|
-
}
|
|
117
|
-
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
118
|
-
if (denom < 1e-10)
|
|
119
|
-
return 0;
|
|
120
|
-
// Clamp to [-1, 1] to handle floating point drift
|
|
121
|
-
return Math.min(1, Math.max(-1, dot / denom));
|
|
122
|
-
}
|
|
123
|
-
const EMBED_LOCK_FILE = 'embeddings.lock';
|
|
124
|
-
const EMBED_LOCK_WAIT_MS = 10_000;
|
|
125
|
-
const EMBED_LOCK_OWNER = `${process.pid}:${randomUUID()}`;
|
|
126
|
-
// In-process mutex plus an O_EXCL "<pid>:<token>" lock file: our token is a lock we leaked; our PID
|
|
127
|
-
// with another token is a live worker thread, unless the lock predates this process (a reused PID).
|
|
128
|
-
let _embedWriteLock = Promise.resolve();
|
|
129
|
-
function embedLockHolderAlive(lockPath) {
|
|
130
|
-
let raw;
|
|
131
|
-
try {
|
|
132
|
-
raw = fs.readFileSync(lockPath, 'utf8');
|
|
133
|
-
}
|
|
134
|
-
catch (err) {
|
|
135
|
-
return !(err instanceof Error && 'code' in err && err.code === 'ENOENT');
|
|
136
|
-
}
|
|
137
|
-
const mtimeMs = fs.statSync(lockPath, { throwIfNoEntry: false })?.mtimeMs ?? 0;
|
|
138
|
-
const pid = Number(raw.split(':')[0]);
|
|
139
|
-
// An empty lock is a holder between create and write; after 5 s it is a crashed one.
|
|
140
|
-
if (!Number.isInteger(pid) || pid <= 0)
|
|
141
|
-
return Date.now() - mtimeMs < 5_000;
|
|
142
|
-
if (raw === EMBED_LOCK_OWNER)
|
|
143
|
-
return false;
|
|
144
|
-
if (pid === process.pid)
|
|
145
|
-
return mtimeMs >= Date.now() - process.uptime() * 1000;
|
|
146
|
-
try {
|
|
147
|
-
process.kill(pid, 0);
|
|
148
|
-
return true;
|
|
149
|
-
}
|
|
150
|
-
catch (err) {
|
|
151
|
-
return err instanceof Error && 'code' in err && err.code === 'EPERM';
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
// After the double-break race below the file can be another writer's lock, so remove only our own.
|
|
155
|
-
function releaseEmbedFileLock(lockPath) {
|
|
156
|
-
let raw;
|
|
157
|
-
try {
|
|
158
|
-
raw = fs.readFileSync(lockPath, 'utf8');
|
|
159
|
-
}
|
|
160
|
-
catch (err) {
|
|
161
|
-
if (err instanceof Error && 'code' in err && err.code === 'ENOENT')
|
|
162
|
-
return;
|
|
163
|
-
throw err;
|
|
164
|
-
}
|
|
165
|
-
if (raw === EMBED_LOCK_OWNER)
|
|
166
|
-
fs.rmSync(lockPath, { force: true });
|
|
167
|
-
}
|
|
168
|
-
async function acquireEmbedFileLock(hippoRoot) {
|
|
169
|
-
const lockPath = path.join(hippoRoot, EMBED_LOCK_FILE);
|
|
170
|
-
const deadline = Date.now() + EMBED_LOCK_WAIT_MS;
|
|
171
|
-
for (;;) {
|
|
172
|
-
try {
|
|
173
|
-
fs.writeFileSync(lockPath, EMBED_LOCK_OWNER, { flag: 'wx' });
|
|
174
|
-
return () => releaseEmbedFileLock(lockPath);
|
|
175
|
-
}
|
|
176
|
-
catch (err) {
|
|
177
|
-
if (!(err instanceof Error && 'code' in err && err.code === 'EEXIST'))
|
|
178
|
-
throw err;
|
|
179
|
-
}
|
|
180
|
-
// SHORTCUT: two waiters can both break one dead holder's lock; a vector lost that way is backfilled by the next `hippo embed`.
|
|
181
|
-
if (!embedLockHolderAlive(lockPath)) {
|
|
182
|
-
fs.rmSync(lockPath, { force: true });
|
|
183
|
-
continue;
|
|
184
|
-
}
|
|
185
|
-
if (Date.now() >= deadline)
|
|
186
|
-
throw new Error(`the embedding index is busy: another hippo process holds ${lockPath}`);
|
|
187
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
async function withEmbedLock(hippoRoot, fn) {
|
|
191
|
-
return withProcessEmbedLock(async () => {
|
|
192
|
-
const release = await acquireEmbedFileLock(hippoRoot);
|
|
193
|
-
try {
|
|
194
|
-
return await fn();
|
|
195
|
-
}
|
|
196
|
-
finally {
|
|
197
|
-
release();
|
|
198
|
-
}
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
// A store keeps each write whole in its own transaction, and a lock file in hippoRoot would not reach a worker on another host.
|
|
202
|
-
async function withProcessEmbedLock(fn) {
|
|
203
|
-
let resolve;
|
|
204
|
-
const next = new Promise(r => { resolve = r; });
|
|
205
|
-
const prev = _embedWriteLock;
|
|
206
|
-
_embedWriteLock = next;
|
|
207
|
-
await prev;
|
|
208
|
-
try {
|
|
209
|
-
return await fn();
|
|
210
|
-
}
|
|
211
|
-
finally {
|
|
212
|
-
resolve();
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
// A bad key fails every write; one warning tells the user, N would bury the command's own output.
|
|
216
|
-
let _embedFailureWarned = false;
|
|
217
|
-
function warnEmbedFailureOnce(source, rawMessage) {
|
|
218
|
-
if (_embedFailureWarned)
|
|
219
|
-
return;
|
|
220
|
-
_embedFailureWarned = true;
|
|
221
|
-
// Strict scrub: this line can land in a hook log file, and an API may echo the key back in its error body.
|
|
222
|
-
const message = redactSecretsStrict(rawMessage).replace(/\s+/g, ' ').replace(/\.+$/, '');
|
|
223
|
-
log.warn(`embedding failed (${source}): ${message}. Memories are stored without embeddings until this is fixed.`);
|
|
224
|
-
}
|
|
225
|
-
// requireGroup (src/store/port.ts) would close an import cycle with this module.
|
|
226
|
-
function vectorGroups(store) {
|
|
227
|
-
if (store.vectors === undefined)
|
|
228
|
-
throw new StoreNotPortedError(store.kind, 'vectors');
|
|
229
|
-
if (store.vectorWrites === undefined)
|
|
230
|
-
throw new StoreNotPortedError(store.kind, 'vectorWrites');
|
|
231
|
-
return [store.vectors, store.vectorWrites];
|
|
232
|
-
}
|
|
233
|
-
/** Writes one page's vectors, one writeVectors per tenant, and stops at the first refused write; `withParticles` seeds each memory's particle. */
|
|
234
|
-
async function writeVectorPage(writes, write, page, vectors, withParticles) {
|
|
235
|
-
const byTenant = new Map();
|
|
236
|
-
page.forEach((entry, i) => {
|
|
237
|
-
const vector = vectors[i];
|
|
238
|
-
if (!vector || vector.length === 0)
|
|
239
|
-
return noteSkippedEmbedding(entry.id);
|
|
240
|
-
const row = withParticles ? { memoryId: entry.id, vector, particle: initializeParticle(entry, vector) } : { memoryId: entry.id, vector };
|
|
241
|
-
byTenant.set(entry.tenantId, [...(byTenant.get(entry.tenantId) ?? []), row]);
|
|
242
|
-
});
|
|
243
|
-
let written = 0;
|
|
244
|
-
for (const [tenantId, rows] of byTenant) {
|
|
245
|
-
const result = await writes.writeVectors({ ...write, tenantId, rows });
|
|
246
|
-
if (result.modelMismatch)
|
|
247
|
-
return { written, modelMismatch: true };
|
|
248
|
-
written += result.written;
|
|
249
|
-
}
|
|
250
|
-
return { written, modelMismatch: false };
|
|
251
|
-
}
|
|
252
|
-
const OTHER_MODEL_INDEX = "the vector index was built by another embedding model; run 'hippo embed' to rebuild it";
|
|
253
|
-
/** Embeds every memory with no vector under `model`, page by page, so a provider failure keeps the pages written; a rebuild reseeds the particles it drops. */
|
|
254
|
-
async function backfillInStore(writes, provider, model, rebuild) {
|
|
255
|
-
let count = 0;
|
|
256
|
-
let afterId;
|
|
257
|
-
for (;;) {
|
|
258
|
-
const page = await writes.entriesWithoutVector({ model, afterId, limit: STORE_PAGE });
|
|
259
|
-
if (page.length === 0)
|
|
260
|
-
return count;
|
|
261
|
-
afterId = page[page.length - 1].id;
|
|
262
|
-
const vectors = await provider.embed(page.map((e) => embeddingInputText(e)), 'passage');
|
|
263
|
-
const result = await writeVectorPage(writes, { model, replaceIndex: rebuild }, page, vectors, rebuild);
|
|
264
|
-
// Another process rebuilt the index mid-run; every later page would be refused too, after paying the provider for it.
|
|
265
|
-
if (result.modelMismatch)
|
|
266
|
-
throw new Error(OTHER_MODEL_INDEX);
|
|
267
|
-
count += result.written;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
/** embedAll on a store: a model change re-embeds every memory, and the first page written drops the old index. */
|
|
271
|
-
async function embedAllInStore(store, provider) {
|
|
272
|
-
const [reads, writes] = vectorGroups(store);
|
|
273
|
-
const rebuild = indexNeedsRebuild(indexedModel(await reads.embeddingIndexState()), provider.id);
|
|
274
|
-
return backfillInStore(writes, provider, embeddingIndexIdentity(provider.id), rebuild);
|
|
275
|
-
}
|
|
276
|
-
/** embedMemory on a store; like the hippo.db path it resolves whatever fails. Unlike it, it leaves a model change to embedAll, so a stale
|
|
277
|
-
* read here never drops another process's rebuild; the read only saves a paid embed call, as the write refuses another model itself. */
|
|
278
|
-
function embedMemoryInStore(store, provider, entry) {
|
|
279
|
-
return withProcessEmbedLock(async () => {
|
|
280
|
-
const [reads, writes] = vectorGroups(store);
|
|
281
|
-
const model = embeddingIndexIdentity(provider.id);
|
|
282
|
-
try {
|
|
283
|
-
const refused = replacesIndex(await reads.embeddingIndexState(), model)
|
|
284
|
-
|| (await writeVectorPage(writes, { model, replaceIndex: false }, [entry], await provider.embed([embeddingInputText(entry)], 'passage'), true)).modelMismatch;
|
|
285
|
-
if (refused)
|
|
286
|
-
warnEmbedFailureOnce('index', OTHER_MODEL_INDEX);
|
|
287
|
-
}
|
|
288
|
-
catch (err) {
|
|
289
|
-
warnEmbedFailureOnce(provider.kind, errorMessage(err));
|
|
290
|
-
}
|
|
291
|
-
}).catch((err) => {
|
|
292
|
-
log.warn(`skipped embedding ${entry.id} (${errorMessage(err)})`);
|
|
293
|
-
});
|
|
294
|
-
}
|
|
295
|
-
/** Embed a single memory entry and cache the result in the embedding index; with `store`, through its vectorWrites, never opening hippo.db. */
|
|
296
|
-
export async function embedMemory(hippoRoot, entry, model, store) {
|
|
297
|
-
let provider;
|
|
298
|
-
try {
|
|
299
|
-
provider = resolveEmbeddingProvider(hippoRoot, { model });
|
|
300
|
-
}
|
|
301
|
-
catch (err) {
|
|
302
|
-
// Callers fire and forget, so this must resolve: a bad config warns once instead of rejecting.
|
|
303
|
-
warnEmbedFailureOnce('config', errorMessage(err));
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
if (!provider.isAvailable())
|
|
307
|
-
return;
|
|
308
|
-
if (store)
|
|
309
|
-
return embedMemoryInStore(store, provider, entry);
|
|
310
|
-
return withEmbedLock(hippoRoot, async () => {
|
|
311
|
-
// Best-effort: an embedding failure (API down, bad key, 5xx) must not reject the caller's write; the existing index is left as-is.
|
|
312
|
-
// Failures surface on the explicit `hippo embed` / `embedAll` path.
|
|
313
|
-
try {
|
|
314
|
-
const identity = provider.id;
|
|
315
|
-
if (embeddingModelRequiresReindex(hippoRoot, identity)) {
|
|
316
|
-
await rebuildIndexForProvider(hippoRoot, provider);
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
const text = embeddingInputText(entry);
|
|
320
|
-
const [vector] = await provider.embed([text], 'passage');
|
|
321
|
-
if (!vector || vector.length === 0)
|
|
322
|
-
return;
|
|
323
|
-
saveStoredVectors(hippoRoot, [[entry.id, vector]], embeddingIndexIdentity(identity));
|
|
324
|
-
saveStoredEmbeddingModel(hippoRoot, identity);
|
|
325
|
-
// Initialize physics state for this memory
|
|
326
|
-
initializePhysicsIfMissing(hippoRoot, entry, vector);
|
|
327
|
-
}
|
|
328
|
-
catch (err) {
|
|
329
|
-
// Provider failure (API down / bad key). Best-effort: leave the index as-is, but say so once.
|
|
330
|
-
warnEmbedFailureOnce(provider.kind, errorMessage(err));
|
|
331
|
-
}
|
|
332
|
-
}).catch((err) => {
|
|
333
|
-
log.warn(`skipped embedding ${entry.id} (${errorMessage(err)}); run 'hippo embed' to backfill`);
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
// Every page is embedded before one transaction replaces the index, and the identity is saved only after that: a provider failure or a crash
|
|
337
|
-
// at any earlier point leaves the old identity, so embeddingModelRequiresReindex has the next run rebuild from the start.
|
|
338
|
-
async function rebuildIndexForProvider(hippoRoot, provider) {
|
|
339
|
-
const identity = provider.id;
|
|
340
|
-
// Host-wide rebuild: one embedding index per hippoRoot, keyed by tenant-scoped entry.id; per-tenant indices would be a larger architecture change.
|
|
341
|
-
const ids = loadAllEntryIds(hippoRoot);
|
|
342
|
-
const rebuiltIndex = await rebuildEmbeddingIndex(hippoRoot, ids, provider);
|
|
343
|
-
saveEmbeddingIndex(hippoRoot, rebuiltIndex, embeddingIndexIdentity(identity));
|
|
344
|
-
saveStoredEmbeddingModel(hippoRoot, identity);
|
|
345
|
-
resetPhysicsFromIndex(hippoRoot, ids, rebuiltIndex);
|
|
346
|
-
return Object.keys(rebuiltIndex).length;
|
|
347
|
-
}
|
|
348
|
-
function initializePhysicsIfMissing(hippoRoot, entry, vector) {
|
|
349
|
-
try {
|
|
350
|
-
seedStoredParticle(hippoRoot, entry, vector);
|
|
351
|
-
}
|
|
352
|
-
catch (err) {
|
|
353
|
-
// Physics init is best-effort and must not fail the embedding that just landed.
|
|
354
|
-
log.debug(`physics state not initialised for ${entry.id}: ${errorMessage(err)}`);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
/** Throws when an unavailable provider is a misconfiguration rather than an intentional no-op. */
|
|
358
|
-
function throwIfProviderKeyMissing(hippoRoot, provider) {
|
|
359
|
-
// A configured API provider with a missing key is a misconfiguration: surface it so callers of the exported embedAll() learn nothing was written.
|
|
360
|
-
// Local-not-installed and an explicit enabled=false stay silent no-ops.
|
|
361
|
-
const cfg = loadConfig(hippoRoot).embeddings;
|
|
362
|
-
if (provider.kind !== 'local' &&
|
|
363
|
-
cfg.enabled !== false &&
|
|
364
|
-
provider.keyEnv &&
|
|
365
|
-
!envByName(provider.keyEnv)?.trim()) {
|
|
366
|
-
throw new Error(`Embedding provider '${provider.kind}' is configured but ${provider.keyEnv} is not set.`);
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
// Embeds entries without a cached vector in save-checkpointed chunks; a `[]` row means that item could not be embedded and is retried next run.
|
|
370
|
-
// On a hard provider failure mid-backfill the chunks already embedded are persisted (paid progress), then it stops and resumes next run.
|
|
371
|
-
async function backfillPending(hippoRoot, provider, pending, model) {
|
|
372
|
-
let count = 0;
|
|
373
|
-
// Initialized to `undefined` so it stays a plain `unknown` binding for the arbitrary caught value; falsy either way.
|
|
374
|
-
let backfillError = undefined;
|
|
375
|
-
for (const chunk of entryPages(hippoRoot, pending)) {
|
|
376
|
-
let vectors;
|
|
377
|
-
try {
|
|
378
|
-
vectors = await provider.embed(chunk.map((e) => embeddingInputText(e)), 'passage');
|
|
379
|
-
}
|
|
380
|
-
catch (err) {
|
|
381
|
-
// Preserve the chunks already saved this run, then surface the failure
|
|
382
|
-
// below so the explicit `hippo embed` path never reports a false success.
|
|
383
|
-
backfillError = err;
|
|
384
|
-
break;
|
|
385
|
-
}
|
|
386
|
-
const rows = [];
|
|
387
|
-
for (let j = 0; j < chunk.length; j++) {
|
|
388
|
-
const vec = vectors[j];
|
|
389
|
-
if (vec && vec.length > 0)
|
|
390
|
-
rows.push([chunk[j].id, vec]);
|
|
391
|
-
else
|
|
392
|
-
noteSkippedEmbedding(chunk[j].id);
|
|
393
|
-
}
|
|
394
|
-
count += saveStoredVectors(hippoRoot, rows, model);
|
|
395
|
-
}
|
|
396
|
-
return { count, backfillError };
|
|
397
|
-
}
|
|
398
|
-
/** Embeds every entry with no cached vector, prunes vectors of deleted memories, and returns how many it embedded; `provider` defaults to the
|
|
399
|
-
* configured one. With `store`, every tenant's memories with no vector for the provider's model go through its `vectorWrites`, never hippo.db. */
|
|
400
|
-
export async function embedAll(hippoRoot, model, provider = resolveEmbeddingProvider(hippoRoot, { model }), store) {
|
|
401
|
-
if (!provider.isAvailable()) {
|
|
402
|
-
throwIfProviderKeyMissing(hippoRoot, provider);
|
|
403
|
-
return 0;
|
|
404
|
-
}
|
|
405
|
-
if (store)
|
|
406
|
-
return withProcessEmbedLock(() => embedAllInStore(store, provider));
|
|
407
|
-
return withEmbedLock(hippoRoot, async () => {
|
|
408
|
-
const identity = provider.id;
|
|
409
|
-
if (embeddingModelRequiresReindex(hippoRoot, identity))
|
|
410
|
-
return rebuildIndexForProvider(hippoRoot, provider);
|
|
411
|
-
// Host-wide by design: embedAll backfills all tenants' entries into the per-host index; per-tenant filtering would produce partial indices and break
|
|
412
|
-
// recall.
|
|
413
|
-
const embedded = pruneStoredVectors(hippoRoot);
|
|
414
|
-
const pending = loadAllEntryIds(hippoRoot).filter((id) => !embedded.has(id));
|
|
415
|
-
const { count, backfillError } = await backfillPending(hippoRoot, provider, pending, embeddingIndexIdentity(identity));
|
|
416
|
-
saveStoredEmbeddingModel(hippoRoot, identity);
|
|
417
|
-
// Partial progress is now persisted; surface a hard backfill failure so the
|
|
418
|
-
// explicit embed path reports it (best-effort callers go via embedMemory).
|
|
419
|
-
if (backfillError)
|
|
420
|
-
throw backfillError;
|
|
421
|
-
return count;
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
55
|
//# sourceMappingURL=index.js.map
|
|
@@ -3,10 +3,6 @@ import { type DatabaseSyncLike } from '../db/index.js';
|
|
|
3
3
|
export declare const TENANT_IS = "+tenant_id = ?";
|
|
4
4
|
/** Read a memory entry by ID. With `tenantId` the read is tenant-scoped (cross-tenant returns null); omitted, no tenant filter is applied. */
|
|
5
5
|
export declare function readEntry(hippoRoot: string, id: string, tenantId?: string): MemoryEntry | null;
|
|
6
|
-
/** Ids per `IN (...)` list: far under SQLite's bound-parameter limit, with room for the tenant filter. */
|
|
7
|
-
export declare const ID_CHUNK = 500;
|
|
8
|
-
/** `items` in consecutive slices of at most `size`. */
|
|
9
|
-
export declare function chunked<T>(items: readonly T[], size?: number): T[][];
|
|
10
6
|
/** Answers whether a tenant's rows in this store hold an id: `ids` are looked up now, one query per chunk, and any other id on its first ask. */
|
|
11
7
|
export declare function heldIdLookup(hippoRoot: string, tenantId: string, ids: readonly string[]): (id: string) => boolean;
|
|
12
8
|
/** Rows by id on the caller's handle, one query per chunk; an id missing or in another tenant is absent from the map. */
|
|
@@ -28,6 +24,19 @@ export declare function loadFreshRawMemories(hippoRoot: string, count: number, t
|
|
|
28
24
|
export declare function loadAllEntries(hippoRoot: string, tenantId?: string): MemoryEntry[];
|
|
29
25
|
/** Every tenant's memory ids in loadAllEntries' order, for a caller that reads the rows a page at a time instead of holding them all. */
|
|
30
26
|
export declare function loadAllEntryIds(hippoRoot: string): string[];
|
|
27
|
+
/** How many memory rows the store holds across every tenant. */
|
|
28
|
+
export declare function countAllEntries(hippoRoot: string): number;
|
|
29
|
+
/** A superset, in loadAllEntries' order, of the rows whose lowercased content holds any of `tokens` or whose lowercased tags hold `tag`.
|
|
30
|
+
* The caller re-checks each row exactly; this only spares decoding rows that cannot match. */
|
|
31
|
+
export declare function loadEntriesMentioning(hippoRoot: string, tokens: readonly string[], tag: string, tenantId?: string): MemoryEntry[];
|
|
32
|
+
export interface EntryTextKey {
|
|
33
|
+
id: string;
|
|
34
|
+
tenantId: string;
|
|
35
|
+
content: string;
|
|
36
|
+
source: string;
|
|
37
|
+
}
|
|
38
|
+
/** Every tenant's rows in loadAllEntries' order, with only the columns that say whether another store already holds each. */
|
|
39
|
+
export declare function loadEntryTextKeys(hippoRoot: string): EntryTextKey[];
|
|
31
40
|
export interface EntriesWithBase {
|
|
32
41
|
entries: MemoryEntry[];
|
|
33
42
|
base: {
|
|
@@ -3,7 +3,9 @@ import { MEMORY_SELECT_COLUMNS, rowToEntry, parseJsonArray } from './rows.js';
|
|
|
3
3
|
import { onHandle, openStore } from './open.js';
|
|
4
4
|
import { escapeLike } from '../util/escape.js';
|
|
5
5
|
import { originInSql } from '../core/project-identity.js';
|
|
6
|
-
import { scopeAdmitSql } from './
|
|
6
|
+
import { scopeAdmitSql } from './rule-sql.js';
|
|
7
|
+
import { chunked } from '../util/chunked.js';
|
|
8
|
+
import { DEFAULT_TENANT_ID } from '../util/env.js';
|
|
7
9
|
const MAX_IDS_PER_READ = 500;
|
|
8
10
|
// The plus keeps SQLite on the primary key for an id list: with a bare tenant_id it walks every row of the tenant instead.
|
|
9
11
|
export const TENANT_IS = '+tenant_id = ?';
|
|
@@ -18,15 +20,6 @@ export function readEntry(hippoRoot, id, tenantId) {
|
|
|
18
20
|
return row ? rowToEntry(row) : null;
|
|
19
21
|
}, openStore);
|
|
20
22
|
}
|
|
21
|
-
/** Ids per `IN (...)` list: far under SQLite's bound-parameter limit, with room for the tenant filter. */
|
|
22
|
-
export const ID_CHUNK = 500;
|
|
23
|
-
/** `items` in consecutive slices of at most `size`. */
|
|
24
|
-
export function chunked(items, size = ID_CHUNK) {
|
|
25
|
-
const out = [];
|
|
26
|
-
for (let i = 0; i < items.length; i += size)
|
|
27
|
-
out.push(items.slice(i, i + size));
|
|
28
|
-
return out;
|
|
29
|
-
}
|
|
30
23
|
/** Answers whether a tenant's rows in this store hold an id: `ids` are looked up now, one query per chunk, and any other id on its first ask. */
|
|
31
24
|
export function heldIdLookup(hippoRoot, tenantId, ids) {
|
|
32
25
|
const held = new Map();
|
|
@@ -201,6 +194,48 @@ export function loadAllEntryIds(hippoRoot) {
|
|
|
201
194
|
return db.prepare('SELECT id FROM memories ORDER BY created ASC, id ASC').all().map((row) => row.id);
|
|
202
195
|
}, openStore);
|
|
203
196
|
}
|
|
197
|
+
/** How many memory rows the store holds across every tenant. */
|
|
198
|
+
export function countAllEntries(hippoRoot) {
|
|
199
|
+
return onHandle(hippoRoot, (db) => {
|
|
200
|
+
// SAFETY: row's shape matches the single `COUNT(*) AS c` column.
|
|
201
|
+
const row = db.prepare('SELECT COUNT(*) AS c FROM memories').get();
|
|
202
|
+
return Number(row?.c ?? 0);
|
|
203
|
+
}, openStore);
|
|
204
|
+
}
|
|
205
|
+
// The only code points whose JS toLowerCase yields ASCII, which SQLite's ASCII-only LIKE folding cannot see.
|
|
206
|
+
const FOLDS_TO_ASCII = ['\u0130', '\u212A'];
|
|
207
|
+
/** A superset, in loadAllEntries' order, of the rows whose lowercased content holds any of `tokens` or whose lowercased tags hold `tag`.
|
|
208
|
+
* The caller re-checks each row exactly; this only spares decoding rows that cannot match. */
|
|
209
|
+
export function loadEntriesMentioning(hippoRoot, tokens, tag, tenantId) {
|
|
210
|
+
const likes = [...new Set(tokens)].map((t) => `%${escapeLike(t)}%`);
|
|
211
|
+
const clauses = likes.map(() => `content LIKE ? ESCAPE '\\'`);
|
|
212
|
+
const params = [...likes];
|
|
213
|
+
// A tag needing JSON escapes or case folding past ASCII is not found by a substring of tags_json, so every tagged row stays in.
|
|
214
|
+
if (/^[\x20-\x7e]*$/.test(tag) && !/["\\]/.test(tag)) {
|
|
215
|
+
clauses.push(`tags_json LIKE ? ESCAPE '\\'`);
|
|
216
|
+
params.push(`%${escapeLike(tag)}%`);
|
|
217
|
+
}
|
|
218
|
+
else
|
|
219
|
+
clauses.push(`COALESCE(tags_json, '[]') != '[]'`);
|
|
220
|
+
for (const ch of FOLDS_TO_ASCII) {
|
|
221
|
+
clauses.push('instr(content, ?) > 0', 'instr(tags_json, ?) > 0');
|
|
222
|
+
params.push(ch, ch);
|
|
223
|
+
}
|
|
224
|
+
const tenantClause = tenantId !== undefined ? 'tenant_id = ? AND ' : '';
|
|
225
|
+
return onHandle(hippoRoot, (db) => {
|
|
226
|
+
// SAFETY: selects exactly MEMORY_SELECT_COLUMNS, matching MemoryRow's field set.
|
|
227
|
+
const rows = db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE ${tenantClause}(${clauses.join(' OR ')}) ORDER BY created ASC, id ASC`).all(...(tenantId !== undefined ? [tenantId] : []), ...params);
|
|
228
|
+
return rows.map(rowToEntry);
|
|
229
|
+
}, openStore);
|
|
230
|
+
}
|
|
231
|
+
/** Every tenant's rows in loadAllEntries' order, with only the columns that say whether another store already holds each. */
|
|
232
|
+
export function loadEntryTextKeys(hippoRoot) {
|
|
233
|
+
return onHandle(hippoRoot, (db) => {
|
|
234
|
+
// SAFETY: rows' shape matches the four columns named in the SELECT.
|
|
235
|
+
const rows = db.prepare('SELECT id, tenant_id, content, source FROM memories ORDER BY created ASC, id ASC').all();
|
|
236
|
+
return rows.map((row) => ({ id: row.id, tenantId: row.tenant_id ?? DEFAULT_TENANT_ID, content: row.content, source: row.source }));
|
|
237
|
+
}, openStore);
|
|
238
|
+
}
|
|
204
239
|
/** Every entry plus a by-id lookup of each as loaded, rebuilt from its row on first use, so no deep copy of the store is kept. */
|
|
205
240
|
export function loadAllEntriesWithBase(hippoRoot) {
|
|
206
241
|
const db = openStore(hippoRoot);
|
|
@@ -265,7 +300,8 @@ export function loadVaultRawRows(hippoRoot, likeParam, tenantId) {
|
|
|
265
300
|
export function selectPreviewRows(db, columns, tenantId) {
|
|
266
301
|
const tenant = columns.has('tenant_id') ? ' WHERE tenant_id = ?' : '';
|
|
267
302
|
const column = (name, fallback) => (columns.has(name) ? name : `${fallback} AS ${name}`);
|
|
268
|
-
const statement = db.prepare(`SELECT id, content, ${column('source', "''")}, ${column('confidence', 'NULL')}, ${column('extracted_from', 'NULL')},
|
|
303
|
+
const statement = db.prepare(`SELECT id, content, ${column('source', "''")}, ${column('confidence', 'NULL')}, ${column('extracted_from', 'NULL')}, ` +
|
|
304
|
+
`${column('dag_level', '0')}, ${column('tags_json', "'[]'")} FROM memories${tenant}`);
|
|
269
305
|
// SAFETY: the SELECT names every PreviewRow field, each a literal fallback when its column is missing.
|
|
270
306
|
return (tenant ? statement.all(tenantId) : statement.all());
|
|
271
307
|
}
|
|
@@ -10,11 +10,11 @@ export interface WriteEntryOptions {
|
|
|
10
10
|
* `opts.actor` defaults to 'cli'; the api layer passes the resolved actor so the audit log gets one row per write. */
|
|
11
11
|
export declare function writeEntry(hippoRoot: string, entry: MemoryEntry, opts?: WriteEntryOptions): void;
|
|
12
12
|
/** writeEntry for each of `entries` in one transaction, so all land or none; each
|
|
13
|
-
* keeps its audit row, and mirrors follow the commit. Returns how many were written. */
|
|
13
|
+
* keeps its audit row, and mirrors follow the commit. Returns how many were written; an empty list opens no store. */
|
|
14
14
|
export declare function writeEntriesTogether(hippoRoot: string, entries: readonly MemoryEntry[]): number;
|
|
15
|
-
/**
|
|
16
|
-
*
|
|
17
|
-
export declare function
|
|
15
|
+
/** Runs `fill` in one transaction; each `put` writes one entry and returns false when the store rejects its value,
|
|
16
|
+
* leaving that row's refusal audit and the rest of the batch. Mirrors follow the commit. Returns how many were written. */
|
|
17
|
+
export declare function writeEntriesSkippingRejected(hippoRoot: string, fill: (put: (entry: MemoryEntry) => boolean) => void, actor?: string): number;
|
|
18
18
|
/** Adds `tag` to each of a tenant's rows that lacks it, in `ids` order, each read fresh
|
|
19
19
|
* on one open store and committed alone; an id the tenant does not hold is skipped. */
|
|
20
20
|
export declare function addTagToEntries(hippoRoot: string, tenantId: string, ids: readonly string[], tag: string): void;
|
|
@@ -36,7 +36,7 @@ export interface StrengthenOptions {
|
|
|
36
36
|
/** Strengthen what a read returned: update only the four retrieval columns on the live row, never a stale copy.
|
|
37
37
|
* Best effort: a failure logs and never fails the read. Returns the ids found in this store. */
|
|
38
38
|
export declare function strengthenRetrieved(hippoRoot: string, ids: readonly string[], opts: StrengthenOptions): Set<string>;
|
|
39
|
-
/** strengthenRetrieved on an open handle
|
|
39
|
+
/** strengthenRetrieved on an open handle in its own write scope, a savepoint inside a caller's transaction, so a recall's last writes share one handle. */
|
|
40
40
|
export declare function strengthenRetrievedInOwnTx(db: DatabaseSyncLike, ids: readonly string[], opts: StrengthenOptions): Set<string>;
|
|
41
41
|
/** strengthenRetrieved on the caller's handle, inside the caller's transaction. Throws; the caller decides. */
|
|
42
42
|
export declare function strengthenRetrievedOn(db: DatabaseSyncLike, ids: readonly string[], opts: StrengthenOptions): Set<string>;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { strengthAblation } from '../core/ablation.js';
|
|
1
2
|
import { markRetrieved } from '../core/memory.js';
|
|
2
|
-
import { closeHippoDb, openHippoDb,
|
|
3
|
-
import {
|
|
3
|
+
import { closeHippoDb, openHippoDb, withWriteScope } from '../db/index.js';
|
|
4
|
+
import { rethrowIfSqliteBlocked } from '../util/sqlite-blocked.js';
|
|
5
|
+
import { RejectedValueError } from '../core/api-errors.js';
|
|
4
6
|
import { markSummaryDirtyInTx } from './summary-dirty.js';
|
|
5
7
|
import { errorMessage, log } from '../util/log.js';
|
|
6
8
|
import { auditRejectionRefusal, audit } from './audit-event.js';
|
|
@@ -16,8 +18,10 @@ export function writeEntry(hippoRoot, entry, opts) {
|
|
|
16
18
|
}, openStore);
|
|
17
19
|
}
|
|
18
20
|
/** writeEntry for each of `entries` in one transaction, so all land or none; each
|
|
19
|
-
* keeps its audit row, and mirrors follow the commit. Returns how many were written. */
|
|
21
|
+
* keeps its audit row, and mirrors follow the commit. Returns how many were written; an empty list opens no store. */
|
|
20
22
|
export function writeEntriesTogether(hippoRoot, entries) {
|
|
23
|
+
if (entries.length === 0)
|
|
24
|
+
return 0;
|
|
21
25
|
const stamped = entries.map((entry) => stampOriginProject(hippoRoot, entry));
|
|
22
26
|
const db = openStore(hippoRoot);
|
|
23
27
|
try {
|
|
@@ -39,15 +43,31 @@ export function writeEntriesTogether(hippoRoot, entries) {
|
|
|
39
43
|
}
|
|
40
44
|
return stamped.length;
|
|
41
45
|
}
|
|
42
|
-
/**
|
|
43
|
-
*
|
|
44
|
-
export function
|
|
45
|
-
|
|
46
|
-
return;
|
|
46
|
+
/** Runs `fill` in one transaction; each `put` writes one entry and returns false when the store rejects its value,
|
|
47
|
+
* leaving that row's refusal audit and the rest of the batch. Mirrors follow the commit. Returns how many were written. */
|
|
48
|
+
export function writeEntriesSkippingRejected(hippoRoot, fill, actor = 'cli') {
|
|
49
|
+
const written = [];
|
|
47
50
|
onHandle(hippoRoot, (db) => {
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
withWriteScope(db, 'write_entries_skipping_rejected', () => {
|
|
52
|
+
fill((entry) => {
|
|
53
|
+
const stamped = stampOriginProject(hippoRoot, entry);
|
|
54
|
+
try {
|
|
55
|
+
writeEntryDbOnly(db, stamped, { actor });
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
if (!(error instanceof RejectedValueError))
|
|
59
|
+
throw error;
|
|
60
|
+
auditRejectionRefusal(db, error, actor);
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
written.push(stamped);
|
|
64
|
+
return true;
|
|
65
|
+
});
|
|
66
|
+
});
|
|
50
67
|
}, openStore);
|
|
68
|
+
for (const entry of written)
|
|
69
|
+
writeEntryMirrors(hippoRoot, entry);
|
|
70
|
+
return written.length;
|
|
51
71
|
}
|
|
52
72
|
/** Adds `tag` to each of a tenant's rows that lacks it, in `ids` order, each read fresh
|
|
53
73
|
* on one open store and committed alone; an id the tenant does not hold is skipped. */
|
|
@@ -129,7 +149,7 @@ export function strengthenRetrieved(hippoRoot, ids, opts) {
|
|
|
129
149
|
closeHippoDb(db);
|
|
130
150
|
}
|
|
131
151
|
}
|
|
132
|
-
/** strengthenRetrieved on an open handle
|
|
152
|
+
/** strengthenRetrieved on an open handle in its own write scope, a savepoint inside a caller's transaction, so a recall's last writes share one handle. */
|
|
133
153
|
export function strengthenRetrievedInOwnTx(db, ids, opts) {
|
|
134
154
|
if (ids.length === 0 || opts.recallBoostAblated)
|
|
135
155
|
return new Set();
|
|
@@ -152,7 +172,7 @@ export function strengthenRetrievedOn(db, ids, opts) {
|
|
|
152
172
|
const byId = selectEntriesByIds(db, ids, opts.tenantId);
|
|
153
173
|
const live = [...new Set(ids)].flatMap((id) => byId.get(id) ?? []);
|
|
154
174
|
const update = db.prepare('UPDATE memories SET retrieval_count = ?, last_retrieved = ?, half_life_days = ?, strength = ? WHERE id = ?');
|
|
155
|
-
for (const e of markRetrieved(live)) {
|
|
175
|
+
for (const e of markRetrieved(live, undefined, { ablation: strengthAblation() })) {
|
|
156
176
|
update.run(e.retrieval_count, e.last_retrieved, e.half_life_days, e.strength, e.id);
|
|
157
177
|
found.add(e.id);
|
|
158
178
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type FailureEvent, type FailureOutcome } from './failure-log.js';
|
|
2
|
+
/** {@link recordFailure} on a handle of its own. */
|
|
3
|
+
export declare function recordFailureAt(hippoRoot: string, event: FailureEvent): number;
|
|
4
|
+
/** {@link requestOutcome} on a handle of its own. */
|
|
5
|
+
export declare function requestOutcomeAt(hippoRoot: string, tenantId: string, requestId: string, sessionId: string): FailureOutcome | null;
|
|
6
|
+
/** {@link settleFailureOutcome} on a handle of its own. */
|
|
7
|
+
export declare function settleFailureOutcomeAt(hippoRoot: string, tenantId: string, requestId: string, outcome: FailureOutcome): void;
|
|
8
|
+
//# sourceMappingURL=failure-log-at.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// The failure log on a handle of its own, for code that has a folder and no connection.
|
|
2
|
+
import { onHandle } from './open.js';
|
|
3
|
+
import { recordFailure, requestOutcome, settleFailureOutcome } from './failure-log.js';
|
|
4
|
+
/** {@link recordFailure} on a handle of its own. */
|
|
5
|
+
export function recordFailureAt(hippoRoot, event) {
|
|
6
|
+
return onHandle(hippoRoot, (db) => recordFailure(db, event));
|
|
7
|
+
}
|
|
8
|
+
/** {@link requestOutcome} on a handle of its own. */
|
|
9
|
+
export function requestOutcomeAt(hippoRoot, tenantId, requestId, sessionId) {
|
|
10
|
+
return onHandle(hippoRoot, (db) => requestOutcome(db, tenantId, requestId, sessionId));
|
|
11
|
+
}
|
|
12
|
+
/** {@link settleFailureOutcome} on a handle of its own. */
|
|
13
|
+
export function settleFailureOutcomeAt(hippoRoot, tenantId, requestId, outcome) {
|
|
14
|
+
onHandle(hippoRoot, (db) => settleFailureOutcome(db, tenantId, requestId, outcome));
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=failure-log-at.js.map
|
|
@@ -28,8 +28,8 @@ export interface FailureEvent {
|
|
|
28
28
|
/** Override the timestamp (tests). ISO string. */
|
|
29
29
|
now?: string;
|
|
30
30
|
}
|
|
31
|
-
/**
|
|
32
|
-
export declare function recordFailure(db: DatabaseSyncLike, event: FailureEvent):
|
|
31
|
+
/** Prune rows past {@link FAILURE_LOG_RETENTION_DAYS}, then append one failure row; returns the new row's id. */
|
|
32
|
+
export declare function recordFailure(db: DatabaseSyncLike, event: FailureEvent): number;
|
|
33
33
|
/** The outcome logged for a caller's request id, or null, so a retried send finds the first one; another session's id is a ConflictError. */
|
|
34
34
|
export declare function requestOutcome(db: DatabaseSyncLike, tenantId: string, requestId: string, sessionId: string): FailureOutcome | null;
|
|
35
35
|
/** A retry that stored what the first try could not rewrites that row, since the request id allows one row per tenant. */
|