hippo-memory 1.71.0 → 2.0.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 +7 -5
- package/dist/agent-memories/apply.d.ts +0 -11
- package/dist/agent-memories/apply.js +11 -26
- 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 +3 -2
- package/dist/api/audit.d.ts +30 -2
- package/dist/api/audit.js +48 -7
- package/dist/api/auth.d.ts +11 -9
- package/dist/api/auth.js +39 -40
- 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-auto.js +4 -2
- package/dist/api/context-render.js +4 -3
- package/dist/api/context-select.js +15 -29
- package/dist/api/context.js +29 -18
- 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 +3 -2
- 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 +7 -3
- package/dist/api/forget.js +24 -14
- package/dist/api/goals.d.ts +1 -1
- package/dist/api/goals.js +5 -9
- package/dist/api/graph.js +2 -1
- 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 +5 -1
- package/dist/api/learn.js +71 -44
- package/dist/api/ledger-db.d.ts +2 -6
- package/dist/api/ledger-db.js +4 -36
- package/dist/api/memories.d.ts +21 -1
- package/dist/api/memories.js +51 -1
- package/dist/api/objects.d.ts +1 -1
- package/dist/api/objects.js +2 -1
- package/dist/api/outcome.d.ts +5 -5
- package/dist/api/outcome.js +17 -16
- 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 +8 -1
- package/dist/api/projects.d.ts +21 -0
- package/dist/api/projects.js +24 -0
- package/dist/api/promote.d.ts +3 -3
- package/dist/api/promote.js +26 -28
- package/dist/api/prompt-hook.d.ts +17 -2
- package/dist/api/prompt-hook.js +45 -4
- package/dist/api/quarantine.js +2 -1
- package/dist/api/recall-cli.d.ts +3 -0
- package/dist/api/recall-cli.js +13 -4
- package/dist/api/recall-core.js +10 -5
- package/dist/api/recall-pipeline.js +23 -24
- package/dist/api/recall-request.js +1 -1
- package/dist/api/recall-types.d.ts +3 -1
- package/dist/api/recall.d.ts +0 -3
- package/dist/api/recall.js +51 -73
- package/dist/api/remember-local.d.ts +5 -2
- package/dist/api/remember-local.js +15 -7
- package/dist/api/remember.d.ts +7 -3
- package/dist/api/remember.js +19 -16
- package/dist/api/session-end.d.ts +46 -0
- package/dist/api/session-end.js +131 -0
- package/dist/api/sleep-run.d.ts +1 -1
- package/dist/api/sleep-run.js +16 -11
- package/dist/api/sleep.js +4 -3
- 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 +2 -2
- package/dist/api/types.d.ts +3 -7
- 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 +7 -7
- package/dist/capture/compaction-spool.js +60 -51
- 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 +3 -2
- package/dist/capture/session-worker.js +22 -10
- 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 +20 -20
- package/dist/cli/auth.d.ts +1 -1
- package/dist/cli/auth.js +16 -17
- 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 +6 -2
- package/dist/cli/client.js +37 -21
- package/dist/cli/compact-resume-payload.js +1 -1
- package/dist/cli/context.d.ts +1 -1
- package/dist/cli/context.js +13 -13
- package/dist/cli/continuity.d.ts +1 -1
- package/dist/cli/continuity.js +36 -39
- package/dist/cli/curate.d.ts +2 -2
- package/dist/cli/curate.js +32 -36
- 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 +7 -285
- 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 +6 -6
- package/dist/cli/hook-runtime.js +9 -36
- 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/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/object-verbs.d.ts +2 -2
- package/dist/cli/object-verbs.js +4 -4
- 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 +174 -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 +18 -17
- package/dist/cli/remember.js +17 -17
- package/dist/cli/scheduler.js +2 -1
- package/dist/cli/serve.js +11 -6
- package/dist/cli/session-hooks.d.ts +1 -5
- package/dist/cli/session-hooks.js +89 -168
- 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 +3 -3
- package/dist/cli/transfer.js +64 -43
- package/dist/cli/usage.d.ts +1 -80
- package/dist/cli/usage.js +2 -696
- package/dist/cli/verb-row.d.ts +34 -0
- package/dist/cli/verb-row.js +6 -0
- package/dist/cli/verbs/health.d.ts +27 -0
- package/dist/cli/verbs/health.js +234 -0
- package/dist/cli/verbs/memory.d.ts +16 -0
- package/dist/cli/verbs/memory.js +236 -0
- package/dist/cli/verbs/objects.d.ts +20 -0
- package/dist/cli/verbs/objects.js +257 -0
- package/dist/cli/verbs/sessions.d.ts +31 -0
- package/dist/cli/verbs/sessions.js +314 -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 +24 -315
- package/dist/connectors/dlq.d.ts +39 -3
- package/dist/connectors/dlq.js +39 -4
- package/dist/connectors/github/backfill.js +12 -3
- package/dist/connectors/github/deletion.js +2 -1
- package/dist/connectors/github/dlq.js +31 -73
- package/dist/connectors/github/ingest.d.ts +2 -6
- package/dist/connectors/github/ingest.js +5 -29
- package/dist/connectors/github/tenant-routing.js +2 -1
- package/dist/connectors/github/webhook.d.ts +2 -1
- package/dist/connectors/github/webhook.js +35 -2
- package/dist/connectors/ingest.d.ts +10 -0
- package/dist/connectors/ingest.js +29 -0
- package/dist/connectors/slack/deletion.js +2 -1
- package/dist/connectors/slack/dlq.js +49 -71
- package/dist/connectors/slack/ingest.d.ts +2 -6
- package/dist/connectors/slack/ingest.js +4 -28
- package/dist/connectors/slack/tenant-routing.js +2 -1
- package/dist/connectors/slack/webhook.d.ts +2 -1
- package/dist/connectors/slack/webhook.js +2 -1
- package/dist/consolidate/conflicts.js +2 -2
- package/dist/consolidate/dag.js +29 -18
- package/dist/consolidate/dedupe.d.ts +7 -0
- package/dist/consolidate/dedupe.js +6 -4
- package/dist/consolidate/llm-passes.js +45 -19
- package/dist/consolidate/memory-value.js +3 -2
- package/dist/consolidate/merge.js +8 -8
- 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 +9 -6
- package/dist/consolidate/traces.js +5 -5
- 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 +22 -18
- 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 +4 -4
- package/dist/core/memory-audit.d.ts +17 -0
- package/dist/core/memory-audit.js +54 -0
- package/dist/core/memory.d.ts +14 -8
- package/dist/core/memory.js +35 -23
- package/dist/{consolidate → core}/merged-row.d.ts +1 -1
- package/dist/{consolidate → core}/merged-row.js +1 -1
- package/dist/core/physics.d.ts +3 -1
- package/dist/core/physics.js +23 -18
- 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 +12 -2
- package/dist/{store → core}/recall-scope.d.ts +1 -10
- package/dist/{store → core}/recall-scope.js +2 -19
- package/dist/core/search-types.d.ts +1 -1
- package/dist/dashboard/dashboard-actions.d.ts +2 -2
- package/dist/dashboard/dashboard-actions.js +5 -5
- package/dist/dashboard/dashboard-queries.js +2 -7
- package/dist/dashboard/dashboard-snapshot.js +1 -3
- package/dist/dashboard/dashboard.js +16 -5
- package/dist/db/busy.d.ts +2 -7
- package/dist/db/busy.js +21 -26
- package/dist/db/connect.d.ts +2 -2
- package/dist/db/connect.js +7 -7
- package/dist/db/index.d.ts +3 -3
- package/dist/db/index.js +3 -3
- package/dist/db/migrate.js +14 -7
- package/dist/db/migrations/index.d.ts +1 -1
- package/dist/db/migrations/index.js +3 -1
- package/dist/db/migrations/v11.js +1 -0
- package/dist/db/migrations/v14.js +1 -0
- 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.d.ts +0 -4
- package/dist/db/physics-state.js +7 -33
- package/dist/db/request-stores.d.ts +1 -0
- package/dist/db/request-stores.js +2 -1
- package/dist/db/tables.d.ts +2 -0
- package/dist/db/tables.js +6 -2
- package/dist/db/vector-store.js +19 -5
- package/dist/db/wal-checkpointer.js +2 -1
- package/dist/doctor.d.ts +3 -0
- package/dist/doctor.js +18 -15
- 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 +19 -25
- 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/entry/store-kit.d.ts +37 -0
- package/dist/entry/store-kit.js +25 -0
- package/dist/entry/testing.d.ts +6 -0
- package/dist/entry/testing.js +7 -0
- package/dist/eval/eval-suite.js +17 -11
- package/dist/eval/eval.js +1 -1
- package/dist/eval/judgment.js +12 -5
- 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 +10 -4
- 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 +4 -4
- package/dist/importers/sources.js +5 -4
- package/dist/importers/vault.d.ts +1 -1
- package/dist/importers/vault.js +25 -23
- package/dist/index.d.ts +5 -4
- package/dist/index.js +4 -3
- package/dist/learn/autolearn.d.ts +15 -3
- package/dist/learn/autolearn.js +38 -16
- package/dist/learn/churn-git.js +13 -4
- package/dist/learn/extract.d.ts +3 -2
- package/dist/learn/extract.js +7 -5
- package/dist/learn/forward-claim-detector.js +4 -2
- package/dist/learn/invalidation.d.ts +2 -1
- package/dist/learn/invalidation.js +18 -9
- 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.d.ts +1 -1
- package/dist/mcp/memory-tools.js +8 -9
- 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 +22 -20
- package/dist/mcp/stdio.js +37 -16
- 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 +71 -98
- 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 +12 -0
- package/dist/search/embed-memories.js +389 -0
- package/dist/search/engine.d.ts +8 -0
- package/dist/search/engine.js +10 -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 +14 -17
- package/dist/server/auth.d.ts +3 -1
- package/dist/server/auth.js +24 -15
- package/dist/server/boot.d.ts +2 -1
- package/dist/server/boot.js +34 -84
- package/dist/server/client-ip.d.ts +9 -6
- package/dist/server/client-ip.js +36 -17
- 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 -133
- 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 +19 -40
- 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/tls.d.ts +7 -2
- package/dist/server/tls.js +17 -5
- package/dist/server/types.d.ts +29 -12
- package/dist/server/validation.d.ts +1 -2
- package/dist/server/validation.js +6 -11
- package/dist/server.d.ts +4 -40
- package/dist/server.js +3 -29
- package/dist/sharing/global-store.d.ts +4 -5
- package/dist/sharing/global-store.js +8 -13
- 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 +10 -3
- package/dist/sharing/share.d.ts +2 -0
- package/dist/sharing/share.js +39 -38
- package/dist/store/ambient.js +7 -7
- 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 +59 -30
- package/dist/store/capture-write.d.ts +17 -0
- package/dist/store/capture-write.js +35 -0
- package/dist/store/cards.js +6 -5
- 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 +418 -0
- package/dist/store/compactions.js +6 -4
- package/dist/store/conflicts.d.ts +7 -1
- package/dist/store/conflicts.js +13 -9
- package/dist/store/connectors/dlq.d.ts +10 -0
- package/dist/store/connectors/dlq.js +33 -0
- package/dist/store/connectors/event-log.d.ts +13 -0
- package/dist/store/connectors/event-log.js +24 -0
- package/dist/store/connectors/github.d.ts +7 -9
- package/dist/store/connectors/github.js +35 -60
- package/dist/store/connectors/slack.d.ts +2 -11
- package/dist/store/connectors/slack.js +24 -57
- package/dist/store/dashboard-reads.js +8 -6
- package/dist/store/delete-and-batch.d.ts +6 -0
- package/dist/store/delete-and-batch.js +40 -6
- package/dist/store/delivery-recorder.d.ts +12 -5
- package/dist/store/delivery-recorder.js +13 -11
- package/dist/store/descendant-page.js +8 -4
- package/dist/store/dormant.d.ts +1 -1
- package/dist/store/dormant.js +10 -4
- package/dist/store/embeddings/index.d.ts +2 -12
- package/dist/store/embeddings/index.js +5 -374
- package/dist/store/entry-reads.d.ts +35 -4
- package/dist/store/entry-reads.js +86 -19
- package/dist/store/entry-row.js +9 -2
- package/dist/store/entry-writes.d.ts +15 -12
- package/dist/store/entry-writes.js +54 -22
- 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 +22 -19
- package/dist/store/graph-reads.js +9 -6
- package/dist/store/graph-view-rows.js +2 -1
- package/dist/store/graph-writes.d.ts +6 -6
- package/dist/store/graph-writes.js +48 -71
- package/dist/store/half-life.js +2 -2
- package/dist/store/handoffs.d.ts +10 -4
- package/dist/store/handoffs.js +10 -9
- package/dist/store/index-and-stats.js +2 -2
- package/dist/store/index.d.ts +2 -2
- package/dist/store/index.js +1 -1
- 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 +19 -10
- package/dist/store/open.d.ts +2 -5
- package/dist/store/open.js +42 -54
- package/dist/store/pilot-arm.d.ts +12 -0
- package/dist/store/pilot-arm.js +42 -0
- package/dist/store/port.d.ts +68 -6
- 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/quality-repair.js +1 -1
- package/dist/store/quarantine.js +2 -1
- package/dist/store/recall-trace.d.ts +2 -0
- package/dist/store/recall-trace.js +20 -3
- package/dist/store/rejected-values.js +16 -10
- 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/rows.js +2 -2
- package/dist/store/rule-sql.d.ts +11 -0
- package/dist/store/rule-sql.js +31 -6
- package/dist/store/search-rows.d.ts +5 -5
- package/dist/store/search-rows.js +18 -16
- 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 +6 -4
- package/dist/store/sqlite/entry-writes-group.d.ts +3 -3
- package/dist/store/sqlite/entry-writes-group.js +3 -3
- 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 +11 -0
- package/dist/store/sqlite/held-connection.js +41 -0
- package/dist/store/sqlite/last-recall-group.d.ts +13 -0
- package/dist/store/sqlite/last-recall-group.js +57 -0
- package/dist/store/sqlite/local.d.ts +0 -17
- package/dist/store/sqlite/local.js +0 -31
- package/dist/store/sqlite/maintenance-groups.d.ts +6 -0
- package/dist/store/sqlite/maintenance-groups.js +60 -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 +15 -7
- package/dist/store/sqlite/store.d.ts +1 -8
- package/dist/store/sqlite/store.js +13 -22
- package/dist/store/summaries.d.ts +10 -3
- package/dist/store/summaries.js +54 -11
- package/dist/store/token-ledger-rows.js +3 -2
- package/dist/store/token-ledger.d.ts +0 -7
- package/dist/store/token-ledger.js +0 -35
- package/dist/store/vector-index.js +8 -4
- 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 +6 -1
- package/dist/util/anthropic-messages.js +26 -7
- package/dist/util/atomic-write.d.ts +5 -2
- package/dist/util/atomic-write.js +12 -8
- 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 +12 -2
- package/dist/util/env.js +12 -2
- package/dist/util/format.d.ts +5 -0
- package/dist/util/format.js +10 -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 +9 -3
- package/dist/util/request-scope.js +18 -9
- package/dist/util/secret-detect.js +6 -1
- package/dist/util/spawn-argv.d.ts +9 -0
- package/dist/util/spawn-argv.js +57 -0
- package/dist/util/sql.d.ts +3 -0
- package/dist/util/sql.js +6 -0
- 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/vector-codec.d.ts +4 -0
- package/dist/util/vector-codec.js +15 -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 +4 -2
- package/dist/api/on-store.d.ts +0 -16
- package/dist/api/on-store.js +0 -38
- 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,7 +1,7 @@
|
|
|
1
1
|
import type { MemoryEntry } from '../core/memory.js';
|
|
2
2
|
import { type PromptRecallGate } from '../core/prompt-recall.js';
|
|
3
3
|
export type DeliveryRuntime = 'claude-code' | 'codex' | 'copilot' | 'unknown';
|
|
4
|
-
export type DeliveryEventType = 'prompt-submit' | 'pinned-manual' | 'pre-compact' | 'compact-resume' | 'session-end' | 'context';
|
|
4
|
+
export type DeliveryEventType = 'prompt-submit' | 'pinned-manual' | 'pre-compact' | 'compact-resume' | 'session-end' | 'context' | 'tool-failure';
|
|
5
5
|
/** True for the boundary types: a compaction's two hooks and the end of a session. */
|
|
6
6
|
export declare const isBoundaryEvent: (type: DeliveryEventType) => boolean;
|
|
7
7
|
export type DeliverySurface = 'hook' | 'context';
|
|
@@ -12,8 +12,9 @@ export type DeliveryPool = 'pin' | 'recent' | 'prompt-recall' | 'strength' | 'se
|
|
|
12
12
|
export type DeliveryStage = 'load' | 'eligible' | 'gate' | 'budget' | 'limit' | 'final';
|
|
13
13
|
export type DeliveryOutcome = 'emitted' | 'reused' | 'rejected';
|
|
14
14
|
export type DeliveryRejectReason = 'budget' | 'gate-below-threshold' | 'gate-max-items' | 'duplicate' | 'scope' | 'quality' | 'limit';
|
|
15
|
-
/** Row format version in `delivery_events.ledger_version`:
|
|
16
|
-
|
|
15
|
+
/** Row format version in `delivery_events.ledger_version`: 4 = written by a binary that can write `tool-failure` rows and hashes the
|
|
16
|
+
* store's resolved root. */
|
|
17
|
+
export declare const DELIVERY_LEDGER_VERSION = 4;
|
|
17
18
|
/** Rejected candidate rows kept per event; the rest only add to `rejected_unlisted`. */
|
|
18
19
|
export declare const DELIVERY_REJECTED_ROW_CAP = 16;
|
|
19
20
|
export interface DeliveryCandidateInput {
|
|
@@ -61,6 +62,10 @@ export interface DeliveryEventInput {
|
|
|
61
62
|
recallHash: string | null;
|
|
62
63
|
emittedHash: string | null;
|
|
63
64
|
elapsedMs: number;
|
|
65
|
+
/** The host's id for the tool call; set on `tool-failure` rows only. */
|
|
66
|
+
toolUseId?: string | null;
|
|
67
|
+
/** The `failure_log` row the failure was logged as; set on `tool-failure` rows only. */
|
|
68
|
+
failureId?: number | null;
|
|
64
69
|
candidates: readonly DeliveryCandidateInput[];
|
|
65
70
|
}
|
|
66
71
|
export interface DeliveryFacts {
|
|
@@ -116,6 +121,8 @@ export interface DeliveryOutcomeInput {
|
|
|
116
121
|
export interface DeliveryRecorder extends DeliveryObserver {
|
|
117
122
|
readonly root: string;
|
|
118
123
|
delivered(outcome: DeliveryOutcomeInput): void;
|
|
124
|
+
/** The failure log row id a `tool-failure` event links to; null when the log write threw. */
|
|
125
|
+
logged(failureId: number | null): void;
|
|
119
126
|
/** Builds the event and hands it to `write` once per call; throws on an injected fault, and writes nothing once broken. */
|
|
120
127
|
flush(write: (input: DeliveryEventInput) => number | null): void;
|
|
121
128
|
}
|
|
@@ -131,10 +138,10 @@ export interface DeliveryRecorderInit {
|
|
|
131
138
|
runtime?: DeliveryRuntime;
|
|
132
139
|
/** Set by hooks that are not prompt or context calls; without it the payload's hook event decides. */
|
|
133
140
|
eventType?: DeliveryEventType;
|
|
141
|
+
/** Makes this recorder throw at one stage; only tests set it, and no CLI flag or environment variable reaches it. */
|
|
142
|
+
fault?: DeliveryFault;
|
|
134
143
|
}
|
|
135
144
|
export type DeliveryFault = 'observe' | 'build' | 'flush';
|
|
136
|
-
/** Makes every recorder created afterwards throw at one stage. Only tests call it; the shipped CLI and server have no route here. */
|
|
137
|
-
export declare function _setDeliveryFaultForTests(fault: DeliveryFault | null): void;
|
|
138
145
|
/** A recorder for one call; every observer method is guarded, and a throw marks it broken instead of escaping. */
|
|
139
146
|
export declare function createDeliveryRecorder(init: DeliveryRecorderInit): DeliveryRecorder;
|
|
140
147
|
//# sourceMappingURL=delivery-recorder.d.ts.map
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { evalNow } from '../core/ablation.js';
|
|
2
2
|
import { scoreOverlap } from '../core/prompt-recall.js';
|
|
3
|
-
import { hookPayloadSessionId, hookPayloadString, isSubagentPayload } from '
|
|
3
|
+
import { hookPayloadSessionId, hookPayloadString, isSubagentPayload } from '../util/hook-payload.js';
|
|
4
4
|
import { blockHash, estimateTokens } from '../util/token-text.js';
|
|
5
5
|
import { errorMessage, log } from '../util/log.js';
|
|
6
6
|
/** True for the boundary types: a compaction's two hooks and the end of a session. */
|
|
7
7
|
export const isBoundaryEvent = (type) => type === 'pre-compact' || type === 'compact-resume' || type === 'session-end';
|
|
8
8
|
/** Only the per-prompt calls keep prompt facts; a boundary or a `hippo context` run is not a prompt. */
|
|
9
9
|
const carriesPrompt = (type) => type === 'prompt-submit' || type === 'pinned-manual';
|
|
10
|
-
/** Row format version in `delivery_events.ledger_version`:
|
|
11
|
-
|
|
10
|
+
/** Row format version in `delivery_events.ledger_version`: 4 = written by a binary that can write `tool-failure` rows and hashes the
|
|
11
|
+
* store's resolved root. */
|
|
12
|
+
export const DELIVERY_LEDGER_VERSION = 4;
|
|
12
13
|
/** Rejected candidate rows kept per event; the rest only add to `rejected_unlisted`. */
|
|
13
14
|
export const DELIVERY_REJECTED_ROW_CAP = 16;
|
|
14
15
|
// Deeper stages were closer to being sent, so the row cap keeps them first.
|
|
@@ -35,10 +36,13 @@ function readPayload(init) {
|
|
|
35
36
|
const rawTurnId = hookPayloadString(init.stdinText, 'turn_id');
|
|
36
37
|
const hostTurnId = rawTurnId !== null && rawTurnId.trim() !== '' ? rawTurnId : null;
|
|
37
38
|
const hookEvent = hookPayloadString(init.stdinText, 'hook_event_name');
|
|
39
|
+
const rawToolUseId = hookPayloadString(init.stdinText, 'tool_use_id')?.trim() ?? '';
|
|
40
|
+
// Cut as failure_log cuts its payload fields: the payload is not trusted to be short.
|
|
41
|
+
const toolUseId = rawToolUseId !== '' ? rawToolUseId.slice(0, 128) : null;
|
|
38
42
|
const sessionState = subagent
|
|
39
43
|
? 'subagent'
|
|
40
44
|
: payloadSession !== null ? 'payload' : envSession !== null ? 'env' : 'missing';
|
|
41
|
-
return { payloadSession, envSession, prompt, hostTurnId, hookEvent, sessionState };
|
|
45
|
+
return { payloadSession, envSession, prompt, hostTurnId, hookEvent, toolUseId, sessionState };
|
|
42
46
|
}
|
|
43
47
|
function rejectId(state, id, rejection) {
|
|
44
48
|
const { stage, reason, score, tokens } = rejection;
|
|
@@ -136,6 +140,8 @@ function buildEvent(init, payload, state, ts, startedMs) {
|
|
|
136
140
|
recallHash: outcome.recallHash ?? null,
|
|
137
141
|
emittedHash: emitted !== null ? blockHash(emitted) : null,
|
|
138
142
|
elapsedMs: Math.max(0, Date.now() - startedMs),
|
|
143
|
+
toolUseId: eventType === 'tool-failure' ? payload.toolUseId : null,
|
|
144
|
+
failureId: eventType === 'tool-failure' ? state.failureId : null,
|
|
139
145
|
candidates: rows,
|
|
140
146
|
};
|
|
141
147
|
}
|
|
@@ -192,20 +198,15 @@ function candidateMethods(state, guard) {
|
|
|
192
198
|
}),
|
|
193
199
|
};
|
|
194
200
|
}
|
|
195
|
-
let injectedFault = null;
|
|
196
|
-
/** Makes every recorder created afterwards throw at one stage. Only tests call it; the shipped CLI and server have no route here. */
|
|
197
|
-
export function _setDeliveryFaultForTests(fault) {
|
|
198
|
-
injectedFault = fault;
|
|
199
|
-
}
|
|
200
201
|
/** A recorder for one call; every observer method is guarded, and a throw marks it broken instead of escaping. */
|
|
201
202
|
export function createDeliveryRecorder(init) {
|
|
202
203
|
const startedMs = Date.now();
|
|
203
204
|
const ts = evalNow().toISOString();
|
|
204
|
-
const fault =
|
|
205
|
+
const fault = init.fault ?? null;
|
|
205
206
|
const payload = readPayload(init);
|
|
206
207
|
const state = {
|
|
207
208
|
candidates: new Map(), picked: new Map(), filtered: new Set(), facts: null, shown: 0, dropped: 0,
|
|
208
|
-
disabledSeen: false, outcome: { state: 'empty' }, queryHash: null, recallTraceId: null, broken: null, flushed: false,
|
|
209
|
+
disabledSeen: false, outcome: { state: 'empty' }, queryHash: null, recallTraceId: null, failureId: null, broken: null, flushed: false,
|
|
209
210
|
};
|
|
210
211
|
const guard = guardFor(state, fault);
|
|
211
212
|
const { qualityDropped, offer, reject, dropMissing, gated, selected } = candidateMethods(state, guard);
|
|
@@ -229,6 +230,7 @@ export function createDeliveryRecorder(init) {
|
|
|
229
230
|
queried: (query) => guard(() => { state.queryHash = blockHash(query); }),
|
|
230
231
|
traced: (traceId) => guard(() => { state.recallTraceId = traceId; }),
|
|
231
232
|
delivered: (o) => guard(() => { state.outcome = { ...o }; }),
|
|
233
|
+
logged: (failureId) => guard(() => { state.failureId = failureId; }),
|
|
232
234
|
flush: (write) => {
|
|
233
235
|
if (state.flushed)
|
|
234
236
|
return;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { selectEntriesByIds, TENANT_IS } from './entry-reads.js';
|
|
2
|
+
import { chunked } from '../util/chunked.js';
|
|
2
3
|
import { MEMORY_SELECT_COLUMNS, rowToEntry } from './rows.js';
|
|
3
|
-
|
|
4
|
+
import { placeholders } from '../util/sql.js';
|
|
5
|
+
const marks = (ids) => placeholders(ids.length);
|
|
4
6
|
/** The walk `summaryWithDescendants` documents, with `levels` cut to the first `page.rows` rows and each level's full count in `sizes`. */
|
|
5
7
|
export function pagedDescendants(db, walk, summaryAdmitted) {
|
|
6
8
|
const sizes = [];
|
|
@@ -53,7 +55,8 @@ function countedLevel(db, walk, parents, room) {
|
|
|
53
55
|
const perParent = new Map();
|
|
54
56
|
for (const chunk of chunked([...parents])) {
|
|
55
57
|
// SAFETY: the SELECT names exactly ChildGroup's four fields, and the WHERE keeps dag_parent_id a string.
|
|
56
|
-
const groups = db.prepare(`SELECT dag_parent_id, scope, origin_project, COUNT(*) AS n FROM memories
|
|
58
|
+
const groups = db.prepare(`SELECT dag_parent_id, scope, origin_project, COUNT(*) AS n FROM memories ` +
|
|
59
|
+
`WHERE dag_parent_id IN (${marks(chunk)}) AND ${TENANT_IS} AND id <> ? GROUP BY dag_parent_id, scope, origin_project`).all(...chunk, walk.tenantId, walk.summary.id);
|
|
57
60
|
for (const group of groups) {
|
|
58
61
|
if (!walk.page.admit(group))
|
|
59
62
|
return null;
|
|
@@ -85,7 +88,8 @@ function childrenInOrder(db, walk, parents, limit) {
|
|
|
85
88
|
const byParent = new Map();
|
|
86
89
|
for (const chunk of chunked([...parents])) {
|
|
87
90
|
// SAFETY: selects exactly MEMORY_SELECT_COLUMNS, matching MemoryRow's field set.
|
|
88
|
-
const rows = db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE dag_parent_id IN (${marks(chunk)}) AND ${TENANT_IS} AND id <> ?
|
|
91
|
+
const rows = db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE dag_parent_id IN (${marks(chunk)}) AND ${TENANT_IS} AND id <> ? ` +
|
|
92
|
+
`ORDER BY created ASC, id ASC LIMIT ?`).all(...chunk, walk.tenantId, walk.summary.id, limit);
|
|
89
93
|
for (const row of rows) {
|
|
90
94
|
const bucket = byParent.get(row.dag_parent_id ?? '');
|
|
91
95
|
if (bucket)
|
package/dist/store/dormant.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Not the raw archive (`raw_archive`, `archiveRawMemory`), which keeps metadata only. db-taking helpers leave the handle and transaction to the caller. */
|
|
4
4
|
import { type DatabaseSyncLike } from '../db/index.js';
|
|
5
5
|
import type { MemoryEntry } from '../core/memory.js';
|
|
6
|
-
import type { SqlFragment } from './
|
|
6
|
+
import type { SqlFragment } from './rule-sql.js';
|
|
7
7
|
import type { WriteBudget } from '../util/write-budget.js';
|
|
8
8
|
/** Why a memory went dormant: sleep's decay pass, an imported agent memory whose note was deleted, `hippo projects repair`
|
|
9
9
|
* splitting a two-project merge, or `hippo audit repair` setting aside an automatic memory with a certain defect. */
|
package/dist/store/dormant.js
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* Not the raw archive (`raw_archive`, `archiveRawMemory`), which keeps metadata only. db-taking helpers leave the handle and transaction to the caller. */
|
|
4
4
|
import { closeHippoDb, openHippoDb, withWriteScope, withWriteScopeOr } from '../db/index.js';
|
|
5
5
|
import { onHandle } from './open.js';
|
|
6
|
-
import {
|
|
6
|
+
import { rejectionDigest } from './rejection.js';
|
|
7
|
+
import { RejectedValueError } from '../core/api-errors.js';
|
|
7
8
|
import { escapeLike } from '../util/escape.js';
|
|
8
9
|
import { warnDamagedColumn } from '../util/stored-json.js';
|
|
9
10
|
import { DAY_MS } from '../util/time.js';
|
|
@@ -23,15 +24,20 @@ export function insertDormantRow(db, move) {
|
|
|
23
24
|
reason = excluded.reason,
|
|
24
25
|
strength = excluded.strength,
|
|
25
26
|
dormant_at = excluded.dormant_at
|
|
26
|
-
`).run(move.entry.
|
|
27
|
+
`).run(move.entry.tenant_id, move.entry.id, move.entry.content, JSON.stringify(move.entry), move.reason, move.strength, move.dormantAt);
|
|
28
|
+
}
|
|
29
|
+
function toCurrentEntry({ tenantId, summary_dirty, tenant_id, ...rest }) {
|
|
30
|
+
return { ...rest, tenant_id: tenant_id ?? tenantId ?? '', summary_dirty: summary_dirty === true || summary_dirty === 1 };
|
|
27
31
|
}
|
|
28
32
|
/** Parse a stored snapshot back into a MemoryEntry, or null when the row no longer matches it (edited by hand, or truncated). */
|
|
29
33
|
function parseSnapshot(row) {
|
|
30
34
|
try {
|
|
31
35
|
// SAFETY: entry_json is only written by insertDormantRow from a MemoryEntry;
|
|
32
36
|
// the id / tenant / content checks below reject a row edited out of shape.
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
// SAFETY: entry_json came from JSON.stringify of a MemoryEntry (2.0) or a 1.x one; the checks below reject a row edited out of shape.
|
|
38
|
+
const entry = toCurrentEntry(JSON.parse(row.entry_json));
|
|
39
|
+
if (entry.id !== row.id || entry.tenant_id !== row.tenant_id || entry.content !== row.content) {
|
|
40
|
+
warnDamagedColumn({ table: 'dormant_memories', id: row.id, column: 'entry_json' }, 'wrong shape');
|
|
35
41
|
return null;
|
|
36
42
|
}
|
|
37
43
|
return entry;
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
import { MemoryEntry } from '../../core/memory.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. */
|
|
4
3
|
import { EMBEDDING_MODEL_META_KEY } from '../../db/vector-store.js';
|
|
5
|
-
import { type EmbeddingProvider } from './provider.js';
|
|
6
|
-
import type { HippoStore } from '../index.js';
|
|
7
4
|
export { EMBEDDING_MODEL_META_KEY };
|
|
8
5
|
/** Bump when `embeddingInputText` changes existing vectors; folded into the stored index identity (`embeddingIndexIdentity`) so it reindexes like a model
|
|
9
6
|
* change. Format 2 = `path:*` tags excluded; format 1 (implicit, no suffix) included them. */
|
|
@@ -26,11 +23,4 @@ export declare function resolveIndexedEmbeddingModel(hippoRoot: string, index?:
|
|
|
26
23
|
export declare function embeddingModelRequiresReindex(hippoRoot: string, model: string, index?: Record<string, number[]>): boolean;
|
|
27
24
|
/** Cosine similarity; handles unnormalized vectors, returns 0 for empty or mismatched vectors. */
|
|
28
25
|
export declare function cosineSimilarity(a: number[], b: number[]): number;
|
|
29
|
-
/** `cosineSimilarity` over any indexable vectors; a Float32 view reads the same doubles as its number[] copy, so the score is the same. */
|
|
30
|
-
export declare function cosineOf(a: ArrayLike<number>, b: ArrayLike<number>): number;
|
|
31
|
-
/** Embed a single memory entry and cache the result in the embedding index; with `store`, through its vectorWrites, never opening hippo.db. */
|
|
32
|
-
export declare function embedMemory(hippoRoot: string, entry: MemoryEntry, model?: string, store?: HippoStore): Promise<void>;
|
|
33
|
-
/** Embeds every entry with no cached vector, prunes vectors of deleted memories, and returns how many it embedded; `provider` defaults to the
|
|
34
|
-
* configured one. With `store`, every tenant's memories with no vector for the provider's model go through its `vectorWrites`, never hippo.db. */
|
|
35
|
-
export declare function embedAll(hippoRoot: string, model?: string, provider?: EmbeddingProvider, store?: HippoStore): Promise<number>;
|
|
36
26
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -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
|