hippo-memory 1.60.0 → 1.61.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/ablation.js +9 -27
- package/dist/agent-memories/apply.js +4 -1
- package/dist/agent-memories/claude-code.js +1 -1
- package/dist/agent-memories/codex.js +1 -1
- package/dist/agent-memories/gemini.js +1 -1
- package/dist/agent-memories/legacy.js +1 -1
- package/dist/agent-memories/source.js +1 -1
- package/dist/agent-memories/sync.js +8 -3
- package/dist/ambient-store.d.ts +14 -0
- package/dist/ambient-store.js +90 -0
- package/dist/ambient.d.ts +23 -0
- package/dist/ambient.js +72 -50
- package/dist/api/assemble.d.ts +93 -0
- package/dist/api/assemble.js +152 -0
- package/dist/api/audit.d.ts +17 -0
- package/dist/api/audit.js +23 -0
- package/dist/api/auth.d.ts +79 -0
- package/dist/api/auth.js +178 -0
- package/dist/api/context-types.d.ts +105 -0
- package/dist/api/context-types.js +3 -0
- package/dist/api/context.d.ts +31 -0
- package/dist/api/context.js +705 -0
- package/dist/api/dormant.d.ts +30 -0
- package/dist/api/dormant.js +140 -0
- package/dist/api/drill-down.d.ts +84 -0
- package/dist/api/drill-down.js +123 -0
- package/dist/api/forget.d.ts +57 -0
- package/dist/api/forget.js +87 -0
- package/dist/api/goals.d.ts +18 -0
- package/dist/api/goals.js +33 -0
- package/dist/api/learn.d.ts +31 -0
- package/dist/api/learn.js +88 -0
- package/dist/api/outcome.d.ts +61 -0
- package/dist/api/outcome.js +66 -0
- package/dist/api/promote.d.ts +54 -0
- package/dist/api/promote.js +203 -0
- package/dist/api/quarantine.d.ts +24 -0
- package/dist/api/quarantine.js +121 -0
- package/dist/api/recall-types.d.ts +390 -0
- package/dist/api/recall-types.js +3 -0
- package/dist/api/recall.d.ts +36 -0
- package/dist/api/recall.js +634 -0
- package/dist/api/remember.d.ts +35 -0
- package/dist/api/remember.js +43 -0
- package/dist/api/sleep.d.ts +136 -0
- package/dist/api/sleep.js +271 -0
- package/dist/api/tokens.d.ts +26 -0
- package/dist/api/tokens.js +60 -0
- package/dist/api/types.d.ts +56 -0
- package/dist/api/types.js +38 -0
- package/dist/api.d.ts +20 -1262
- package/dist/api.js +25 -2727
- package/dist/audit.d.ts +3 -0
- package/dist/audit.js +6 -3
- package/dist/auth.d.ts +45 -4
- package/dist/auth.js +125 -48
- package/dist/autolearn.js +2 -2
- package/dist/capture/command.d.ts +33 -0
- package/dist/capture/command.js +264 -0
- package/dist/capture/compact.d.ts +44 -0
- package/dist/capture/compact.js +354 -0
- package/dist/capture/extract.d.ts +21 -0
- package/dist/capture/extract.js +464 -0
- package/dist/capture/transcript.d.ts +40 -0
- package/dist/capture/transcript.js +193 -0
- package/dist/capture-error.js +2 -1
- package/dist/churn-git.js +4 -2
- package/dist/cli/audit.d.ts +3 -0
- package/dist/cli/audit.js +159 -0
- package/dist/cli/auth.d.ts +2 -0
- package/dist/cli/auth.js +171 -0
- package/dist/cli/briefs.d.ts +4 -0
- package/dist/cli/briefs.js +435 -0
- package/dist/cli/card.d.ts +3 -0
- package/dist/cli/card.js +333 -0
- package/dist/cli/context.d.ts +15 -0
- package/dist/cli/context.js +366 -0
- package/dist/cli/continuity.d.ts +6 -0
- package/dist/cli/continuity.js +445 -0
- package/dist/cli/curate.d.ts +19 -0
- package/dist/cli/curate.js +555 -0
- package/dist/cli/dag.d.ts +5 -0
- package/dist/cli/dag.js +177 -0
- package/dist/cli/decisions.d.ts +4 -0
- package/dist/cli/decisions.js +528 -0
- package/dist/cli/eval.d.ts +5 -0
- package/dist/cli/eval.js +213 -0
- package/dist/cli/explain.d.ts +4 -0
- package/dist/cli/explain.js +150 -0
- package/dist/cli/goals.d.ts +2 -0
- package/dist/cli/goals.js +196 -0
- package/dist/cli/hook-blocks.d.ts +18 -0
- package/dist/cli/hook-blocks.js +233 -0
- package/dist/cli/init.d.ts +2 -0
- package/dist/cli/init.js +305 -0
- package/dist/cli/maintenance.d.ts +4 -0
- package/dist/cli/maintenance.js +179 -0
- package/dist/cli/playbooks.d.ts +4 -0
- package/dist/cli/playbooks.js +556 -0
- package/dist/cli/projects.js +1 -1
- package/dist/cli/recall.d.ts +7 -0
- package/dist/cli/recall.js +597 -0
- package/dist/cli/remember.d.ts +5 -0
- package/dist/cli/remember.js +442 -0
- package/dist/cli/serve.d.ts +5 -0
- package/dist/cli/serve.js +40 -0
- package/dist/cli/session-hooks.d.ts +29 -0
- package/dist/cli/session-hooks.js +637 -0
- package/dist/cli/setup.d.ts +4 -0
- package/dist/cli/setup.js +376 -0
- package/dist/cli/shared.d.ts +15 -19
- package/dist/cli/shared.js +50 -341
- package/dist/cli/slack.d.ts +2 -0
- package/dist/cli/slack.js +171 -0
- package/dist/cli/status.d.ts +18 -0
- package/dist/cli/status.js +400 -0
- package/dist/cli/transfer.d.ts +10 -0
- package/dist/cli/transfer.js +438 -0
- package/dist/cli/usage.d.ts +85 -0
- package/dist/cli/usage.js +741 -0
- package/dist/cli.d.ts +71 -120
- package/dist/cli.js +170 -8469
- package/dist/client.js +15 -8
- package/dist/compaction-record.js +6 -4
- package/dist/connectors/github/backfill.js +94 -87
- package/dist/connectors/github/cli-impl.js +4 -3
- package/dist/connectors/github/dlq.js +67 -54
- package/dist/connectors/github/ingest.js +34 -35
- package/dist/connectors/github/tenant-routing.js +3 -2
- package/dist/connectors/github/webhook.js +135 -216
- package/dist/connectors/slack/dlq.js +49 -61
- package/dist/connectors/slack/ingest.js +55 -48
- package/dist/connectors/slack/tenant-routing.js +4 -3
- package/dist/connectors/slack/webhook.js +72 -74
- package/dist/consolidate/conflicts.d.ts +10 -0
- package/dist/consolidate/conflicts.js +178 -0
- package/dist/consolidate/decay.d.ts +12 -0
- package/dist/consolidate/decay.js +145 -0
- package/dist/consolidate/llm-passes.d.ts +3 -0
- package/dist/consolidate/llm-passes.js +141 -0
- package/dist/consolidate/merge.d.ts +7 -0
- package/dist/consolidate/merge.js +251 -0
- package/dist/consolidate/physics-pass.d.ts +3 -0
- package/dist/consolidate/physics-pass.js +60 -0
- package/dist/consolidate/run.d.ts +69 -0
- package/dist/consolidate/run.js +76 -0
- package/dist/consolidate/sleep.d.ts +18 -0
- package/dist/consolidate/sleep.js +209 -0
- package/dist/consolidate/traces.d.ts +4 -0
- package/dist/consolidate/traces.js +178 -0
- package/dist/context-auto.js +7 -11
- package/dist/context-render.d.ts +1 -1
- package/dist/context-render.js +1 -1
- package/dist/customer-notes.d.ts +3 -0
- package/dist/customer-notes.js +6 -4
- package/dist/dag.js +6 -5
- package/dist/dashboard-actions.d.ts +20 -0
- package/dist/dashboard-actions.js +88 -0
- package/dist/dashboard-params.d.ts +45 -0
- package/dist/dashboard-params.js +127 -0
- package/dist/dashboard-queries.d.ts +15 -0
- package/dist/dashboard-queries.js +355 -0
- package/dist/dashboard-snapshot.d.ts +138 -0
- package/dist/dashboard-snapshot.js +308 -0
- package/dist/dashboard-types.d.ts +163 -0
- package/dist/dashboard-types.js +3 -0
- package/dist/dashboard.d.ts +6 -7
- package/dist/dashboard.js +228 -202
- package/dist/db/busy.d.ts +5 -0
- package/dist/db/busy.js +24 -0
- package/dist/db/continuity.d.ts +5 -0
- package/dist/db/continuity.js +145 -0
- package/dist/db/meta.d.ts +8 -0
- package/dist/db/meta.js +35 -0
- package/dist/db/migrate.d.ts +9 -0
- package/dist/db/migrate.js +138 -0
- package/dist/db/migrations/index.d.ts +5 -0
- package/dist/db/migrations/index.js +109 -0
- package/dist/db/migrations/types.d.ts +14 -0
- package/dist/db/migrations/types.js +2 -0
- package/dist/db/migrations/v01.d.ts +3 -0
- package/dist/db/migrations/v01.js +38 -0
- package/dist/db/migrations/v02.d.ts +3 -0
- package/dist/db/migrations/v02.js +21 -0
- package/dist/db/migrations/v03.d.ts +3 -0
- package/dist/db/migrations/v03.js +22 -0
- package/dist/db/migrations/v04.d.ts +3 -0
- package/dist/db/migrations/v04.js +28 -0
- package/dist/db/migrations/v05.d.ts +3 -0
- package/dist/db/migrations/v05.js +21 -0
- package/dist/db/migrations/v06.d.ts +3 -0
- package/dist/db/migrations/v06.js +25 -0
- package/dist/db/migrations/v07.d.ts +3 -0
- package/dist/db/migrations/v07.js +13 -0
- package/dist/db/migrations/v08.d.ts +3 -0
- package/dist/db/migrations/v08.js +8 -0
- package/dist/db/migrations/v09.d.ts +3 -0
- package/dist/db/migrations/v09.js +13 -0
- package/dist/db/migrations/v10.d.ts +3 -0
- package/dist/db/migrations/v10.js +17 -0
- package/dist/db/migrations/v11.d.ts +3 -0
- package/dist/db/migrations/v11.js +15 -0
- package/dist/db/migrations/v12.d.ts +3 -0
- package/dist/db/migrations/v12.js +11 -0
- package/dist/db/migrations/v13.d.ts +3 -0
- package/dist/db/migrations/v13.js +15 -0
- package/dist/db/migrations/v14.d.ts +3 -0
- package/dist/db/migrations/v14.js +66 -0
- package/dist/db/migrations/v15.d.ts +3 -0
- package/dist/db/migrations/v15.js +42 -0
- package/dist/db/migrations/v16.d.ts +3 -0
- package/dist/db/migrations/v16.js +61 -0
- package/dist/db/migrations/v17.d.ts +3 -0
- package/dist/db/migrations/v17.js +46 -0
- package/dist/db/migrations/v18.d.ts +3 -0
- package/dist/db/migrations/v18.js +60 -0
- package/dist/db/migrations/v19.d.ts +3 -0
- package/dist/db/migrations/v19.js +28 -0
- package/dist/db/migrations/v20.d.ts +3 -0
- package/dist/db/migrations/v20.js +42 -0
- package/dist/db/migrations/v21.d.ts +3 -0
- package/dist/db/migrations/v21.js +16 -0
- package/dist/db/migrations/v22.d.ts +3 -0
- package/dist/db/migrations/v22.js +82 -0
- package/dist/db/migrations/v23.d.ts +3 -0
- package/dist/db/migrations/v23.js +48 -0
- package/dist/db/migrations/v24.d.ts +3 -0
- package/dist/db/migrations/v24.js +72 -0
- package/dist/db/migrations/v25.d.ts +3 -0
- package/dist/db/migrations/v25.js +46 -0
- package/dist/db/migrations/v26.d.ts +3 -0
- package/dist/db/migrations/v26.js +23 -0
- package/dist/db/migrations/v27.d.ts +3 -0
- package/dist/db/migrations/v27.js +57 -0
- package/dist/db/migrations/v28.d.ts +3 -0
- package/dist/db/migrations/v28.js +38 -0
- package/dist/db/migrations/v29.d.ts +3 -0
- package/dist/db/migrations/v29.js +78 -0
- package/dist/db/migrations/v30.d.ts +3 -0
- package/dist/db/migrations/v30.js +92 -0
- package/dist/db/migrations/v31.d.ts +3 -0
- package/dist/db/migrations/v31.js +74 -0
- package/dist/db/migrations/v32.d.ts +3 -0
- package/dist/db/migrations/v32.js +102 -0
- package/dist/db/migrations/v33.d.ts +3 -0
- package/dist/db/migrations/v33.js +104 -0
- package/dist/db/migrations/v34.d.ts +3 -0
- package/dist/db/migrations/v34.js +93 -0
- package/dist/db/migrations/v35.d.ts +3 -0
- package/dist/db/migrations/v35.js +98 -0
- package/dist/db/migrations/v36.d.ts +3 -0
- package/dist/db/migrations/v36.js +98 -0
- package/dist/db/migrations/v37.d.ts +3 -0
- package/dist/db/migrations/v37.js +219 -0
- package/dist/db/migrations/v38.d.ts +3 -0
- package/dist/db/migrations/v38.js +277 -0
- package/dist/db/migrations/v39.d.ts +3 -0
- package/dist/db/migrations/v39.js +59 -0
- package/dist/db/migrations/v40.d.ts +3 -0
- package/dist/db/migrations/v40.js +74 -0
- package/dist/db/migrations/v41.d.ts +3 -0
- package/dist/db/migrations/v41.js +43 -0
- package/dist/db/migrations/v42.d.ts +3 -0
- package/dist/db/migrations/v42.js +41 -0
- package/dist/db/migrations/v43.d.ts +3 -0
- package/dist/db/migrations/v43.js +67 -0
- package/dist/db/migrations/v44.d.ts +3 -0
- package/dist/db/migrations/v44.js +28 -0
- package/dist/db/migrations/v45.d.ts +3 -0
- package/dist/db/migrations/v45.js +30 -0
- package/dist/db/migrations/v46.d.ts +3 -0
- package/dist/db/migrations/v46.js +25 -0
- package/dist/db/migrations/v47.d.ts +3 -0
- package/dist/db/migrations/v47.js +17 -0
- package/dist/db/migrations/v48.d.ts +3 -0
- package/dist/db/migrations/v48.js +10 -0
- package/dist/db/migrations/v49.d.ts +3 -0
- package/dist/db/migrations/v49.js +31 -0
- package/dist/db/migrations/v50.d.ts +3 -0
- package/dist/db/migrations/v50.js +67 -0
- package/dist/db/migrations/v51.d.ts +3 -0
- package/dist/db/migrations/v51.js +14 -0
- package/dist/db/migrations/v52.d.ts +3 -0
- package/dist/db/migrations/v52.js +7 -0
- package/dist/db/open.d.ts +23 -0
- package/dist/db/open.js +146 -0
- package/dist/db/sqlite.d.ts +21 -0
- package/dist/db/sqlite.js +8 -0
- package/dist/db/tables.d.ts +7 -0
- package/dist/db/tables.js +38 -0
- package/dist/db.d.ts +6 -46
- package/dist/db.js +5 -3049
- package/dist/decisions.d.ts +4 -1
- package/dist/decisions.js +9 -7
- package/dist/dedupe.js +3 -2
- package/dist/delivery-recorder.js +4 -1
- package/dist/doctor.js +3 -3
- package/dist/embedding-provider.d.ts +1 -1
- package/dist/embedding-provider.js +4 -3
- package/dist/embeddings.d.ts +9 -52
- package/dist/embeddings.js +43 -297
- package/dist/env.d.ts +75 -0
- package/dist/env.js +119 -0
- package/dist/eval-suite.js +1 -1
- package/dist/eval.js +2 -2
- package/dist/extract.js +1 -1
- package/dist/gated-write.js +3 -1
- package/dist/goals.d.ts +3 -1
- package/dist/goals.js +18 -0
- package/dist/graph/read.d.ts +73 -0
- package/dist/graph/read.js +325 -0
- package/dist/graph/rows.d.ts +45 -0
- package/dist/graph/rows.js +51 -0
- package/dist/graph/types.d.ts +83 -0
- package/dist/graph/types.js +11 -0
- package/dist/graph/write.d.ts +93 -0
- package/dist/{graph.js → graph/write.js} +6 -384
- package/dist/graph-extract.js +2 -1
- package/dist/graph-recall.d.ts +1 -1
- package/dist/graph-recall.js +2 -2
- package/dist/graph-stream.js +1 -1
- package/dist/graph-view.d.ts +1 -1
- package/dist/graph-view.js +1 -1
- package/dist/half-life-migration.d.ts +1 -1
- package/dist/half-life-migration.js +2 -1
- package/dist/hooks/codex-session.d.ts +8 -0
- package/dist/hooks/codex-session.js +76 -0
- package/dist/hooks/codex-wrapper.d.ts +55 -0
- package/dist/hooks/codex-wrapper.js +288 -0
- package/dist/hooks/json-hooks.d.ts +63 -0
- package/dist/hooks/json-hooks.js +356 -0
- package/dist/hooks/opencode.d.ts +50 -0
- package/dist/hooks/opencode.js +202 -0
- package/dist/hooks/shared.d.ts +54 -0
- package/dist/hooks/shared.js +77 -0
- package/dist/http-retry.d.ts +2 -0
- package/dist/http-retry.js +4 -3
- package/dist/http-util.d.ts +3 -0
- package/dist/http-util.js +10 -0
- package/dist/{importers.d.ts → importers/core.d.ts} +13 -17
- package/dist/importers/core.js +141 -0
- package/dist/importers/markdown-parse.d.ts +41 -0
- package/dist/importers/markdown-parse.js +132 -0
- package/dist/importers/markdown.d.ts +3 -0
- package/dist/importers/markdown.js +92 -0
- package/dist/importers/sources.d.ts +7 -0
- package/dist/importers/sources.js +229 -0
- package/dist/importers/vault.d.ts +11 -0
- package/dist/importers/vault.js +352 -0
- package/dist/incidents.d.ts +3 -0
- package/dist/incidents.js +7 -5
- package/dist/index.d.ts +25 -6
- package/dist/index.js +23 -6
- package/dist/invalidation.js +2 -1
- package/dist/judgment.js +2 -1
- package/dist/keyset.d.ts +13 -0
- package/dist/keyset.js +8 -0
- package/dist/local-embedding.d.ts +13 -0
- package/dist/local-embedding.js +165 -0
- package/dist/log.d.ts +7 -0
- package/dist/log.js +19 -1
- package/dist/mcp/admin-tools.d.ts +8 -0
- package/dist/mcp/admin-tools.js +116 -0
- package/dist/mcp/format.d.ts +27 -0
- package/dist/mcp/format.js +135 -0
- package/dist/mcp/memory-tools.d.ts +5 -0
- package/dist/mcp/memory-tools.js +83 -0
- package/dist/mcp/protocol.d.ts +83 -0
- package/dist/mcp/protocol.js +55 -0
- package/dist/mcp/recall-tools.d.ts +6 -0
- package/dist/mcp/recall-tools.js +320 -0
- package/dist/mcp/request.d.ts +10 -0
- package/dist/mcp/request.js +163 -0
- package/dist/mcp/server.d.ts +4 -75
- package/dist/mcp/server.js +7 -1190
- package/dist/mcp/session-state.d.ts +11 -0
- package/dist/mcp/session-state.js +28 -0
- package/dist/mcp/stdio.d.ts +8 -0
- package/dist/mcp/stdio.js +79 -0
- package/dist/mcp/tools.d.ts +11 -0
- package/dist/mcp/tools.js +247 -0
- package/dist/memory.d.ts +3 -0
- package/dist/memory.js +27 -1
- package/dist/multihop.d.ts +1 -1
- package/dist/multihop.js +2 -1
- package/dist/owner-validation.js +2 -1
- package/dist/physics-state.js +10 -7
- package/dist/policies.d.ts +3 -0
- package/dist/policies.js +8 -6
- package/dist/postinstall.js +3 -2
- package/dist/predictions/planning-fallacy.d.ts +100 -0
- package/dist/predictions/planning-fallacy.js +190 -0
- package/dist/{predictions.d.ts → predictions/store.d.ts} +7 -102
- package/dist/predictions/store.js +434 -0
- package/dist/processes.d.ts +3 -0
- package/dist/processes.js +7 -5
- package/dist/project-briefs.d.ts +3 -0
- package/dist/project-briefs.js +7 -5
- package/dist/project-identity.js +3 -2
- package/dist/project-merge.js +3 -1
- package/dist/quarantine.d.ts +2 -1
- package/dist/quarantine.js +8 -5
- package/dist/raw-archive.js +1 -1
- package/dist/recall-history.js +3 -2
- package/dist/recall-pipeline.d.ts +2 -2
- package/dist/recall-pipeline.js +16 -8
- package/dist/recall-scope.js +1 -1
- package/dist/recall-trace.d.ts +1 -1
- package/dist/refine-llm.js +2 -1
- package/dist/reject-flow.js +6 -1
- package/dist/rerankers/clef.js +6 -5
- package/dist/rerankers/jev.d.ts +1 -1
- package/dist/rerankers/jev.js +4 -4
- package/dist/rerankers/llm.d.ts +4 -2
- package/dist/rerankers/llm.js +58 -38
- package/dist/rerankers/types.d.ts +1 -1
- package/dist/salience.js +1 -1
- package/dist/scheduler.d.ts +1 -0
- package/dist/scheduler.js +26 -4
- package/dist/scope.js +4 -3
- package/dist/search/as-of.d.ts +10 -0
- package/dist/search/as-of.js +22 -0
- package/dist/search/bm25-search.d.ts +14 -0
- package/dist/search/bm25-search.js +43 -0
- package/dist/search/bm25.d.ts +15 -0
- package/dist/search/bm25.js +54 -0
- package/dist/search/boosts.d.ts +54 -0
- package/dist/search/boosts.js +94 -0
- package/dist/search/breakdown.d.ts +7 -0
- package/dist/search/breakdown.js +20 -0
- package/dist/search/explain.d.ts +25 -0
- package/dist/search/explain.js +31 -0
- package/dist/search/finalize.d.ts +8 -0
- package/dist/search/finalize.js +52 -0
- package/dist/search/fusion.d.ts +27 -0
- package/dist/search/fusion.js +42 -0
- package/dist/search/hybrid-score.d.ts +20 -0
- package/dist/search/hybrid-score.js +73 -0
- package/dist/search/hybrid.d.ts +46 -0
- package/dist/search/hybrid.js +64 -0
- package/dist/search/physics-search.d.ts +29 -0
- package/dist/search/physics-search.js +162 -0
- package/dist/search/rerank.d.ts +10 -0
- package/dist/search/rerank.js +72 -0
- package/dist/search/temporal.d.ts +15 -0
- package/dist/search/temporal.js +45 -0
- package/dist/search/types.d.ts +91 -0
- package/dist/search/types.js +2 -0
- package/dist/search/vector.d.ts +30 -0
- package/dist/search/vector.js +71 -0
- package/dist/secret-detect.d.ts +2 -0
- package/dist/secret-detect.js +2 -1
- package/dist/server/auth.d.ts +52 -0
- package/dist/server/auth.js +221 -0
- package/dist/server/client-ip.d.ts +25 -0
- package/dist/server/client-ip.js +92 -0
- package/dist/server/cursor.d.ts +23 -0
- package/dist/server/cursor.js +58 -0
- package/dist/server/lifecycle.d.ts +7 -0
- package/dist/server/lifecycle.js +29 -0
- package/dist/server/mcp-http.d.ts +5 -0
- package/dist/server/mcp-http.js +199 -0
- package/dist/server/request.d.ts +33 -0
- package/dist/server/request.js +103 -0
- package/dist/server/routes/admin.d.ts +9 -0
- package/dist/server/routes/admin.js +158 -0
- package/dist/server/routes/customer-notes.d.ts +7 -0
- package/dist/server/routes/customer-notes.js +112 -0
- package/dist/server/routes/decisions.d.ts +7 -0
- package/dist/server/routes/decisions.js +133 -0
- package/dist/server/routes/incidents.d.ts +7 -0
- package/dist/server/routes/incidents.js +126 -0
- package/dist/server/routes/memories.d.ts +10 -0
- package/dist/server/routes/memories.js +179 -0
- package/dist/server/routes/policies.d.ts +8 -0
- package/dist/server/routes/policies.js +151 -0
- package/dist/server/routes/predictions.d.ts +7 -0
- package/dist/server/routes/predictions.js +164 -0
- package/dist/server/routes/processes.d.ts +7 -0
- package/dist/server/routes/processes.js +161 -0
- package/dist/server/routes/project-briefs.d.ts +8 -0
- package/dist/server/routes/project-briefs.js +136 -0
- package/dist/server/routes/recall.d.ts +8 -0
- package/dist/server/routes/recall.js +340 -0
- package/dist/server/routes/skills.d.ts +8 -0
- package/dist/server/routes/skills.js +150 -0
- package/dist/server/types.d.ts +56 -0
- package/dist/server/types.js +2 -0
- package/dist/server/validation.d.ts +14 -0
- package/dist/server/validation.js +91 -0
- package/dist/server.d.ts +7 -61
- package/dist/server.js +69 -2362
- package/dist/session-digest.d.ts +1 -1
- package/dist/session-digest.js +9 -2
- package/dist/shared.d.ts +3 -1
- package/dist/shared.js +26 -22
- package/dist/skills.d.ts +3 -0
- package/dist/skills.js +7 -5
- package/dist/stdin.js +2 -2
- package/dist/store/audit-event.d.ts +19 -0
- package/dist/store/audit-event.js +33 -0
- package/dist/store/candidates.d.ts +42 -0
- package/dist/store/candidates.js +152 -0
- package/dist/store/conflicts.d.ts +44 -0
- package/dist/store/conflicts.js +444 -0
- package/dist/store/delete-and-batch.d.ts +63 -0
- package/dist/store/delete-and-batch.js +310 -0
- package/dist/store/entry-reads.d.ts +92 -0
- package/dist/store/entry-reads.js +255 -0
- package/dist/store/entry-row.d.ts +67 -0
- package/dist/store/entry-row.js +208 -0
- package/dist/store/entry-writes.d.ts +46 -0
- package/dist/store/entry-writes.js +148 -0
- package/dist/store/handoffs.d.ts +26 -0
- package/dist/store/handoffs.js +184 -0
- package/dist/store/index-and-stats.d.ts +52 -0
- package/dist/store/index-and-stats.js +214 -0
- package/dist/store/markdown.d.ts +10 -0
- package/dist/store/markdown.js +108 -0
- package/dist/store/mirrors.d.ts +49 -0
- package/dist/store/mirrors.js +312 -0
- package/dist/store/open.d.ts +15 -0
- package/dist/store/open.js +223 -0
- package/dist/store/rows.d.ts +178 -0
- package/dist/store/rows.js +158 -0
- package/dist/store/search-rows.d.ts +86 -0
- package/dist/store/search-rows.js +254 -0
- package/dist/store/sessions.d.ts +83 -0
- package/dist/store/sessions.js +272 -0
- package/dist/store/summaries.d.ts +94 -0
- package/dist/store/summaries.js +377 -0
- package/dist/store/tenant-lookup.d.ts +12 -0
- package/dist/store/tenant-lookup.js +16 -0
- package/dist/store-cards.js +2 -1
- package/dist/summary-dirty.d.ts +4 -0
- package/dist/summary-dirty.js +32 -0
- package/dist/support-bundle.js +4 -3
- package/dist/tenant.js +2 -2
- package/dist/tokenize.d.ts +2 -0
- package/dist/tokenize.js +16 -0
- package/dist/transcript-tail.d.ts +7 -0
- package/dist/transcript-tail.js +48 -0
- package/dist/vector-store.d.ts +27 -0
- package/dist/vector-store.js +210 -0
- package/dist/version.d.ts +2 -2
- package/dist/version.js +2 -2
- package/dist/working-memory.js +1 -1
- package/dist/yaml.js +36 -11
- package/dist-ui/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
- package/dist-ui/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
- package/dist-ui/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
- package/dist-ui/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
- package/dist-ui/assets/ibm-plex-mono-latin-600-normal-BgSNZQsw.woff2 +0 -0
- package/dist-ui/assets/ibm-plex-mono-latin-600-normal-DWFSQ4vo.woff +0 -0
- package/dist-ui/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
- package/dist-ui/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
- package/dist-ui/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
- package/dist-ui/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
- package/dist-ui/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
- package/dist-ui/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
- package/dist-ui/assets/index-DPN7cP19.js +33 -0
- package/dist-ui/assets/index-dFloKRVr.css +1 -0
- package/dist-ui/index.html +3 -25
- 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 +1 -1
- package/dist/capture.d.ts +0 -155
- package/dist/capture.js +0 -1295
- package/dist/consolidate.d.ts +0 -58
- package/dist/consolidate.js +0 -1124
- package/dist/graph.d.ts +0 -245
- package/dist/hooks.d.ts +0 -208
- package/dist/hooks.js +0 -1076
- package/dist/importers.js +0 -900
- package/dist/predictions.js +0 -620
- package/dist/search.d.ts +0 -320
- package/dist/search.js +0 -970
- package/dist/store.d.ts +0 -776
- package/dist/store.js +0 -3473
- package/dist-ui/assets/d3-BiWEKnn4.js +0 -1
- package/dist-ui/assets/index-BhT8RvO6.js +0 -61
- package/dist-ui/assets/index-RoXXJ5dq.css +0 -1
- package/dist-ui/assets/three-BDgTxR1l.js +0 -4112
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { envForceLikePath } from '../env.js';
|
|
2
|
+
import { isFtsAvailable, closeHippoDb } from '../db.js';
|
|
3
|
+
import { tokenize } from '../tokenize.js';
|
|
4
|
+
import { RECALL_DEFAULT_DENY_SCOPES } from '../recall-scope.js';
|
|
5
|
+
import { RAREST_TERM_COUNT, rarestPromptTerms } from '../prompt-recall.js';
|
|
6
|
+
import { log } from '../log.js';
|
|
7
|
+
import { topVectorMatches } from '../vector-store.js';
|
|
8
|
+
import { MEMORY_SELECT_COLUMNS, MEMORY_SEARCH_COLUMNS, DEFAULT_SEARCH_CANDIDATE_LIMIT, rowToEntry, } from './rows.js';
|
|
9
|
+
import { openStore } from './open.js';
|
|
10
|
+
/** The recall scope rule for a table column prefix (`m.` or none); `passesScopeFilterForRecall` in recall-scope.ts is its JS twin. */
|
|
11
|
+
function recallScopeClause(col, scopeFilter) {
|
|
12
|
+
if (scopeFilter === undefined)
|
|
13
|
+
return { sql: '', params: [] };
|
|
14
|
+
if (scopeFilter.mode === 'exact')
|
|
15
|
+
return { sql: ` AND ${col}scope = ?`, params: [scopeFilter.value] };
|
|
16
|
+
// `IS NULL OR` admits NULL scopes, which NOT IN alone drops. NOT LIKE denies a superset of private scopes before the
|
|
17
|
+
// window cut so private rows cannot starve admitted ones; the anchored JS regex stays the exact post-filter.
|
|
18
|
+
const placeholders = RECALL_DEFAULT_DENY_SCOPES.map(() => '?').join(', ');
|
|
19
|
+
const admitted = `${col}scope IS NULL OR (${col}scope NOT IN (${placeholders}) AND ${col}scope NOT LIKE '%:private:%')`;
|
|
20
|
+
if (scopeFilter.mode === 'default-deny')
|
|
21
|
+
return { sql: ` AND (${admitted})`, params: [...RECALL_DEFAULT_DENY_SCOPES] };
|
|
22
|
+
// The trailing arm keeps a deliberately requested scope loadable, private or quarantined included.
|
|
23
|
+
return { sql: ` AND (${admitted} OR ${col}scope = ?)`, params: [...RECALL_DEFAULT_DENY_SCOPES, scopeFilter.value] };
|
|
24
|
+
}
|
|
25
|
+
/** Scope rule for recall: none requested is default-deny; 'exact' narrows to the request; 'additive' adds it to the default set. */
|
|
26
|
+
export function recallScopeFilter(requestedScope, mode) {
|
|
27
|
+
if (!requestedScope)
|
|
28
|
+
return { mode: 'default-deny' };
|
|
29
|
+
return mode === 'additive' ? { mode: 'default-deny-or-exact', value: requestedScope } : { mode: 'exact', value: requestedScope };
|
|
30
|
+
}
|
|
31
|
+
const FTS_QUERY_SYNTAX_RE = /fts5: syntax error|unterminated string/i;
|
|
32
|
+
function loadSearchRows(db, query, limit, tenantId, scopeFilter, includeSuperseded = true) {
|
|
33
|
+
const p = searchPredicates(tenantId, scopeFilter, includeSuperseded);
|
|
34
|
+
const terms = Array.from(new Set(tokenize(query)));
|
|
35
|
+
if (terms.length === 0) {
|
|
36
|
+
// F3 (v1.7.0) self-review: empty-query path is the second uncapped
|
|
37
|
+
// path (codex diff-pass caught the full-store fallback at the bottom;
|
|
38
|
+
// this no-terms path had the same shape). Apply LIMIT so all four
|
|
39
|
+
// candidate paths honour the caller's cap when set.
|
|
40
|
+
return selectAllCandidates(db, p, limit);
|
|
41
|
+
}
|
|
42
|
+
// v1.7.1 — test/diagnostic hook: `HIPPO_FORCE_LIKE_PATH=1` forces the
|
|
43
|
+
// LIKE-fallback path here only. Gated at the read-call site so writes
|
|
44
|
+
// (`syncFtsRow`, `deleteFtsRow`, `raw-archive.ts::archiveRaw`) keep using
|
|
45
|
+
// `isFtsAvailable` honestly and never silently skip FTS index sync.
|
|
46
|
+
// Lets tests exercise the LIKE branch deterministically without
|
|
47
|
+
// poisoning the on-disk FTS state.
|
|
48
|
+
const forceLikePath = envForceLikePath();
|
|
49
|
+
if (!forceLikePath && isFtsAvailable(db)) {
|
|
50
|
+
const rows = selectFtsCandidates(db, terms, p, limit);
|
|
51
|
+
if (rows.length > 0)
|
|
52
|
+
return rows;
|
|
53
|
+
}
|
|
54
|
+
const rows = selectLikeCandidates(db, terms, p, limit);
|
|
55
|
+
if (rows.length > 0)
|
|
56
|
+
return rows;
|
|
57
|
+
// F3 (v1.7.0) codex P1: pre-v1.7.0 the full-store fallback ignored
|
|
58
|
+
// `limit` and could return the whole tenant store. With scorerWindow
|
|
59
|
+
// now reported on RecallResult, an unbounded fallback would lie about
|
|
60
|
+
// candidate-pool size. Apply LIMIT here so all four paths honour the
|
|
61
|
+
// caller's cap.
|
|
62
|
+
return selectAllCandidates(db, p, limit);
|
|
63
|
+
}
|
|
64
|
+
function searchPredicates(tenantId, scopeFilter, includeSuperseded) {
|
|
65
|
+
// tenantId undefined = no tenant filter (legacy callers / cross-deployment
|
|
66
|
+
// helpers). tenantId set = strict tenant isolation, leveraging the composite
|
|
67
|
+
// idx_memories_tenant_created (leading column tenant_id, O(log n) lookup).
|
|
68
|
+
const tenantPredicate = tenantId !== undefined ? ` AND m.tenant_id = ?` : '';
|
|
69
|
+
const tenantPredicateNoAlias = tenantId !== undefined ? ` AND tenant_id = ?` : '';
|
|
70
|
+
const tenantOnlyPredicate = tenantId !== undefined ? ` WHERE tenant_id = ?` : '';
|
|
71
|
+
const tenantParams = tenantId !== undefined ? [tenantId] : [];
|
|
72
|
+
// v1.12.6 — belt-and-suspenders against `kind='archived'` leaking into recall.
|
|
73
|
+
// `kind='archived'` is a transient sentinel inside `archiveRawMemory`'s
|
|
74
|
+
// SAVEPOINT (src/raw-archive.ts:56): UPDATE kind = 'archived' immediately
|
|
75
|
+
// followed by DELETE, both inside one savepoint that commits or rolls back
|
|
76
|
+
// atomically. SQLite atomicity guarantees no concurrent reader sees the
|
|
77
|
+
// intermediate state. This filter is defensive-only against:
|
|
78
|
+
// (a) future bugs that drop the SAVEPOINT,
|
|
79
|
+
// (b) future bugs that introduce kind='archived' as a persisted state,
|
|
80
|
+
// (c) external direct-SQL writes that bypass archiveRawMemory.
|
|
81
|
+
// tenantOnlyPredicate starts with " WHERE tenant_id = ?" when tenant is set;
|
|
82
|
+
// when unset, we have no WHERE yet, so the archived clause needs both AND
|
|
83
|
+
// and WHERE forms. The "tenant-only" path always has WHERE (from tenant or
|
|
84
|
+
// we synthesize one).
|
|
85
|
+
const archivedClauseAlias = ` AND m.kind != 'archived'`;
|
|
86
|
+
const archivedClauseNoAlias = ` AND kind != 'archived'`;
|
|
87
|
+
// For the "tenant-only" path: if no tenant set, tenantOnlyPredicate is '',
|
|
88
|
+
// so prepend WHERE; if tenant set, append AND. handled in each call site
|
|
89
|
+
// by always joining `tenantOnlyPredicate + archivedClauseTenantOnly` where
|
|
90
|
+
// the latter switches between " AND" and " WHERE" based on caller context.
|
|
91
|
+
const archivedClauseTenantOnly = tenantId !== undefined ? ` AND kind != 'archived'` : ` WHERE kind != 'archived'`;
|
|
92
|
+
const aliasScope = recallScopeClause('m.', scopeFilter);
|
|
93
|
+
const plainScope = recallScopeClause('', scopeFilter);
|
|
94
|
+
const scopeParams = aliasScope.params;
|
|
95
|
+
const currentAlias = includeSuperseded ? '' : ' AND m.superseded_by IS NULL';
|
|
96
|
+
const currentNoAlias = includeSuperseded ? '' : ' AND superseded_by IS NULL';
|
|
97
|
+
return {
|
|
98
|
+
tenantPredicate, tenantPredicateNoAlias, tenantOnlyPredicate, tenantParams,
|
|
99
|
+
archivedClauseAlias, archivedClauseNoAlias, archivedClauseTenantOnly,
|
|
100
|
+
aliasScope, plainScope, scopeParams, currentAlias, currentNoAlias,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/** Every admitted row, oldest first; the no-terms path and the last-resort fallback share it. */
|
|
104
|
+
function selectAllCandidates(db, p, limit) {
|
|
105
|
+
const sql = `SELECT ${MEMORY_SELECT_COLUMNS} FROM memories${p.tenantOnlyPredicate}${p.archivedClauseTenantOnly}${p.plainScope.sql}${p.currentNoAlias} ORDER BY created ASC, id ASC LIMIT ?`;
|
|
106
|
+
// SAFETY: sql selects exactly MEMORY_SELECT_COLUMNS, whose column list
|
|
107
|
+
// matches MemoryRow's field set.
|
|
108
|
+
return db.prepare(sql).all(...p.tenantParams, ...p.scopeParams, limit);
|
|
109
|
+
}
|
|
110
|
+
/** FTS5 bm25 matches; empty when none match or FTS5 cannot run the query. */
|
|
111
|
+
function selectFtsCandidates(db, terms, p, limit) {
|
|
112
|
+
try {
|
|
113
|
+
const ftsQuery = terms.map((t) => `"${t.replace(/"/g, '""')}"`).join(' OR ');
|
|
114
|
+
// memories_fts virtual table has no tenant_id column; filter via the
|
|
115
|
+
// joined memories row (cheap with idx_memories_tenant_created leading
|
|
116
|
+
// on tenant_id).
|
|
117
|
+
// F1 (v1.7.0): MEMORY_SEARCH_COLUMNS adds bm25_score as the trailing
|
|
118
|
+
// result column. Every other column is m.<col> AS <col> so rowToEntry
|
|
119
|
+
// sees the same shape it always has.
|
|
120
|
+
// SAFETY: MEMORY_SEARCH_COLUMNS aliases every column to the same name
|
|
121
|
+
// MEMORY_SELECT_COLUMNS uses (plus bm25_score), matching MemoryRow.
|
|
122
|
+
return db.prepare(`
|
|
123
|
+
SELECT ${MEMORY_SEARCH_COLUMNS}
|
|
124
|
+
FROM memories m
|
|
125
|
+
JOIN memories_fts f ON f.id = m.id
|
|
126
|
+
WHERE memories_fts MATCH ?${p.tenantPredicate}${p.archivedClauseAlias}${p.aliasScope.sql}${p.currentAlias}
|
|
127
|
+
ORDER BY bm25(memories_fts), m.updated_at DESC, m.content ASC, m.id ASC
|
|
128
|
+
LIMIT ?
|
|
129
|
+
`).all(ftsQuery, ...p.tenantParams, ...p.scopeParams, limit);
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
// A query FTS5 cannot parse is expected input; anything else means the index itself is broken.
|
|
133
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
134
|
+
if (!FTS_QUERY_SYNTAX_RE.test(message))
|
|
135
|
+
log.once('fts-match-fallback', 'warn', `FTS search failed, using the slower LIKE match: ${message}`);
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function selectLikeCandidates(db, terms, p, limit) {
|
|
140
|
+
const escapeLike = (term) => term.replace(/[%_\\]/g, '\\$&');
|
|
141
|
+
const where = terms.map(() => `(LOWER(content) LIKE ? ESCAPE '\\' OR LOWER(tags_json) LIKE ? ESCAPE '\\')`).join(' OR ');
|
|
142
|
+
const params = terms.flatMap((term) => {
|
|
143
|
+
const like = `%${escapeLike(term)}%`;
|
|
144
|
+
return [like, like];
|
|
145
|
+
});
|
|
146
|
+
// SAFETY: this query selects exactly MEMORY_SELECT_COLUMNS, matching
|
|
147
|
+
// MemoryRow's field set.
|
|
148
|
+
return db.prepare(`
|
|
149
|
+
SELECT ${MEMORY_SELECT_COLUMNS}
|
|
150
|
+
FROM memories
|
|
151
|
+
WHERE (${where})${p.tenantPredicateNoAlias}${p.archivedClauseNoAlias}${p.plainScope.sql}${p.currentNoAlias}
|
|
152
|
+
ORDER BY updated_at DESC, created DESC, content ASC, id ASC
|
|
153
|
+
LIMIT ?
|
|
154
|
+
`).all(...params, ...p.tenantParams, ...p.scopeParams, limit);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Load likely search candidates directly from SQLite.
|
|
158
|
+
* Uses FTS5 when available, falls back to LIKE matching, then full-store fallback.
|
|
159
|
+
*
|
|
160
|
+
* When `tenantId` is provided, every SELECT (FTS join, LIKE, fallback) filters
|
|
161
|
+
* by tenant_id. Cross-tenant memories never surface. Omitted = no filter.
|
|
162
|
+
*/
|
|
163
|
+
export function loadSearchEntries(hippoRoot, query, limit = DEFAULT_SEARCH_CANDIDATE_LIMIT, tenantId) {
|
|
164
|
+
const db = openStore(hippoRoot);
|
|
165
|
+
try {
|
|
166
|
+
return loadSearchRows(db, query, limit, tenantId).map(rowToEntry);
|
|
167
|
+
}
|
|
168
|
+
finally {
|
|
169
|
+
closeHippoDb(db);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* v1.7.1 — recall-mode loader. Pushes the recall-side scope predicate into
|
|
174
|
+
* SQL so `unknown:legacy` cannot leak via any consumer that hasn't remembered
|
|
175
|
+
* to re-filter (root-cause-over-patches: codex flagged this on v1.6.5 review).
|
|
176
|
+
*
|
|
177
|
+
* - `requestedScope` undefined / '': default-deny on `unknown:legacy`.
|
|
178
|
+
* - `requestedScope` non-empty string: exact match on `m.scope = requestedScope`.
|
|
179
|
+
*
|
|
180
|
+
* Private-scope (`<source>:private:*`) exclusion: SQL applies a conservative
|
|
181
|
+
* pre-window approximation (`NOT LIKE '%:private:%'`, v1.25.0 — codex P2:
|
|
182
|
+
* post-window-only filtering let private rows starve admitted candidates out
|
|
183
|
+
* of the LIMIT window); the exact anchored regex
|
|
184
|
+
* (`passesScopeFilterForRecall`) remains the authoritative JS post-filter in
|
|
185
|
+
* the recall consumers.
|
|
186
|
+
*
|
|
187
|
+
* Consumers: `api.recall` (v1.7.1+), `cmdRecall`/`cmdExplain` direct CLI paths
|
|
188
|
+
* and `searchBothHybrid` recall mode (v1.25.0). Background pipelines
|
|
189
|
+
* (`consolidate`, `embeddings`, `refine-llm`, ...) keep using
|
|
190
|
+
* `loadSearchEntries` so they can see quarantined rows when needed.
|
|
191
|
+
*
|
|
192
|
+
* `tenantId` widened to optional in v1.25.0 for the searchBothHybrid recall
|
|
193
|
+
* mode (its `tenantId` option is optional); `loadSearchRows` already treats
|
|
194
|
+
* undefined as "no tenant filter" for legacy callers.
|
|
195
|
+
*/
|
|
196
|
+
export function loadRecallSearchEntries(hippoRoot, query, limit = DEFAULT_SEARCH_CANDIDATE_LIMIT, tenantId, requestedScope, explicitScopeMode = 'exact', includeSuperseded = true) {
|
|
197
|
+
const db = openStore(hippoRoot);
|
|
198
|
+
try {
|
|
199
|
+
return loadRecallSearchEntriesFromDb(db, query, limit, tenantId, requestedScope, explicitScopeMode, includeSuperseded);
|
|
200
|
+
}
|
|
201
|
+
finally {
|
|
202
|
+
closeHippoDb(db);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// Split out so callers with an already-open db (Z1 prompt-recall path) skip
|
|
206
|
+
// the initStore+open/close cycle per store per call.
|
|
207
|
+
export function loadRecallSearchEntriesFromDb(db, query, limit = DEFAULT_SEARCH_CANDIDATE_LIMIT, tenantId, requestedScope, explicitScopeMode = 'exact', includeSuperseded = true) {
|
|
208
|
+
return loadSearchRows(db, query, limit, tenantId, recallScopeFilter(requestedScope, explicitScopeMode), includeSuperseded).map(rowToEntry);
|
|
209
|
+
}
|
|
210
|
+
/** The rows nearest `queryVector` that pass `spec`, nearest first. */
|
|
211
|
+
export function loadVectorCandidateEntries(hippoRoot, queryVector, spec) {
|
|
212
|
+
const scope = recallScopeClause('m.', spec.scope);
|
|
213
|
+
const tenant = spec.tenantId !== undefined ? ' AND m.tenant_id = ?' : '';
|
|
214
|
+
const current = spec.includeSuperseded ? '' : ' AND m.superseded_by IS NULL';
|
|
215
|
+
const params = [...(spec.tenantId !== undefined ? [spec.tenantId] : []), ...scope.params];
|
|
216
|
+
const db = openStore(hippoRoot);
|
|
217
|
+
try {
|
|
218
|
+
const matches = topVectorMatches(db, queryVector, spec.limit ?? 50, `${tenant} AND m.kind != 'archived'${scope.sql}${current}`, params);
|
|
219
|
+
if (matches.length === 0)
|
|
220
|
+
return [];
|
|
221
|
+
// SAFETY: the SELECT names exactly MEMORY_SELECT_COLUMNS, matching MemoryRow's field set.
|
|
222
|
+
const rows = db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE id IN (${matches.map(() => '?').join(', ')})`)
|
|
223
|
+
.all(...matches.map((m) => m.id));
|
|
224
|
+
const byId = new Map(rows.map((r) => [r.id, rowToEntry(r)]));
|
|
225
|
+
return matches.flatMap((m) => byId.get(m.id) ?? []);
|
|
226
|
+
}
|
|
227
|
+
finally {
|
|
228
|
+
closeHippoDb(db);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/** Rarest-K prompt terms for this connection's FTS index, as a space-joined query string.
|
|
232
|
+
* Without FTS, returns the first 32 terms, as before rarest-term selection. */
|
|
233
|
+
export function pickRarestFtsQuery(db, terms, maxTerms = RAREST_TERM_COUNT) {
|
|
234
|
+
// The LIKE path has no bm25 ranking to bound, so it keeps the pre-rarest 32-term query.
|
|
235
|
+
if (!isFtsAvailable(db))
|
|
236
|
+
return terms.slice(0, 32).join(' ');
|
|
237
|
+
db.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS temp.z1_rarest_vocab USING fts5vocab(main, 'memories_fts', 'row')`);
|
|
238
|
+
// unicode61 splits `journal_mode` into two vocab terms; a term's count is its rarest part's (an upper bound).
|
|
239
|
+
const partsOf = (t) => t.split(/[^\p{L}\p{N}]+/u).filter(Boolean);
|
|
240
|
+
const vocab = Array.from(new Set(terms.flatMap(partsOf)));
|
|
241
|
+
if (vocab.length === 0)
|
|
242
|
+
return '';
|
|
243
|
+
// SAFETY: rows' shape matches the two columns named in the SELECT.
|
|
244
|
+
const rows = db
|
|
245
|
+
.prepare(`SELECT term, doc FROM temp.z1_rarest_vocab WHERE term IN (${vocab.map(() => '?').join(', ')})`)
|
|
246
|
+
.all(...vocab);
|
|
247
|
+
const counts = new Map(rows.map((r) => [r.term, r.doc]));
|
|
248
|
+
const docCount = (t) => {
|
|
249
|
+
const parts = partsOf(t);
|
|
250
|
+
return parts.length === 0 ? 0 : Math.min(...parts.map((x) => counts.get(x) ?? 0));
|
|
251
|
+
};
|
|
252
|
+
return rarestPromptTerms(terms, docCount, maxTerms).join(' ');
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=search-rows.js.map
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { type TaskSnapshot, type SessionEvent } from './rows.js';
|
|
2
|
+
export declare function saveActiveTaskSnapshot(hippoRoot: string, tenantId: string, snapshot: {
|
|
3
|
+
task: string;
|
|
4
|
+
summary: string;
|
|
5
|
+
next_step: string;
|
|
6
|
+
source?: string;
|
|
7
|
+
session_id?: string | null;
|
|
8
|
+
scope?: string | null;
|
|
9
|
+
}): TaskSnapshot;
|
|
10
|
+
export declare function loadActiveTaskSnapshot(hippoRoot: string, tenantId: string): TaskSnapshot | null;
|
|
11
|
+
/**
|
|
12
|
+
* Default freshness bound for AMBIENT active-task-snapshot reads (DF1,
|
|
13
|
+
* docs/plans/2026-08-23-df1-snapshot-lifecycle.md): 72h, chosen over 48h so
|
|
14
|
+
* a Friday-evening orphan still offers continuity on Monday morning.
|
|
15
|
+
* Exported so callers can override via `loadFreshActiveTaskSnapshot`'s
|
|
16
|
+
* `opts.maxAgeMs`; deliberately no env knob (Simplicity First).
|
|
17
|
+
*/
|
|
18
|
+
export declare const SNAPSHOT_AMBIENT_MAX_AGE_MS: number;
|
|
19
|
+
/**
|
|
20
|
+
* Bounded read for AMBIENT active-task-snapshot surfaces (UserPromptSubmit
|
|
21
|
+
* hook context, MCP recall block) — the never-expires fix for DF1. A
|
|
22
|
+
* snapshot written by `hippo pre-compact` has no death path tied to the
|
|
23
|
+
* session that owns it, so an orphaned row would otherwise inject into
|
|
24
|
+
* every prompt of every later session forever. Wraps `loadActiveTaskSnapshot`
|
|
25
|
+
* (unchanged, still the source of truth for explicit continuity surfaces),
|
|
26
|
+
* then applies, in order:
|
|
27
|
+
*
|
|
28
|
+
* 1. Owner match — ONLY when both `opts.sessionId` and the snapshot's
|
|
29
|
+
* `session_id` are non-null, non-empty strings and strictly equal
|
|
30
|
+
* (`===`). Owner reads are unbounded: the session that owns the snapshot
|
|
31
|
+
* can always see its own working state, regardless of age.
|
|
32
|
+
* 2. Age check — everything else, including absent-vs-absent ids. A
|
|
33
|
+
* null/undefined/empty id on EITHER side never counts as an owner match;
|
|
34
|
+
* it falls through here instead. (`runPreCompact` can legitimately save a
|
|
35
|
+
* snapshot with `session_id = null`; a null-equals-null "match" would
|
|
36
|
+
* reopen indefinite ambient injection for exactly those rows.) Returns
|
|
37
|
+
* the snapshot only when `age(updated_at) <= maxAgeMs` (default
|
|
38
|
+
* `SNAPSHOT_AMBIENT_MAX_AGE_MS`); otherwise null.
|
|
39
|
+
*
|
|
40
|
+
* No SQL change — age derives from the existing `updated_at` column.
|
|
41
|
+
*/
|
|
42
|
+
export declare function loadFreshActiveTaskSnapshot(hippoRoot: string, tenantId: string, opts?: {
|
|
43
|
+
maxAgeMs?: number;
|
|
44
|
+
sessionId?: string | null;
|
|
45
|
+
}): TaskSnapshot | null;
|
|
46
|
+
export declare function clearActiveTaskSnapshot(hippoRoot: string, tenantId: string, clearedStatus?: string): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Close the `active` task snapshot(s) owned by `sessionId`, for the T3
|
|
49
|
+
* session-end death path (DF1, docs/plans/2026-08-23-df1-snapshot-lifecycle.md).
|
|
50
|
+
* Only one `active` row exists per tenant in practice (supersession happens
|
|
51
|
+
* at save), but the WHERE clause scopes on `session_id` too — not just
|
|
52
|
+
* `status='active' AND tenant_id=?` — so an ending session can never close a
|
|
53
|
+
* different, newer session's active snapshot. Returns the number of rows
|
|
54
|
+
* closed (0 when no active row is owned by `sessionId`).
|
|
55
|
+
*/
|
|
56
|
+
export declare function closeTaskSnapshotsForSession(hippoRoot: string, tenantId: string, sessionId: string, status?: string): number;
|
|
57
|
+
export declare function appendSessionEvent(hippoRoot: string, tenantId: string, event: {
|
|
58
|
+
session_id: string;
|
|
59
|
+
event_type: string;
|
|
60
|
+
content: string;
|
|
61
|
+
task?: string | null;
|
|
62
|
+
source?: string;
|
|
63
|
+
scope?: string | null;
|
|
64
|
+
metadata?: Record<string, unknown>;
|
|
65
|
+
}): SessionEvent;
|
|
66
|
+
export declare function listSessionEvents(hippoRoot: string, tenantId: string, options?: {
|
|
67
|
+
session_id?: string;
|
|
68
|
+
task?: string;
|
|
69
|
+
limit?: number;
|
|
70
|
+
}): SessionEvent[];
|
|
71
|
+
/**
|
|
72
|
+
* Return session_ids with a `session_complete` event newer than `sinceMs`.
|
|
73
|
+
* Used by the sleep auto-promotion pass to bound scanning to a fixed window.
|
|
74
|
+
*/
|
|
75
|
+
export declare function findPromotableSessions(hippoRoot: string, tenantId: string, sinceMs: number): Array<{
|
|
76
|
+
session_id: string;
|
|
77
|
+
}>;
|
|
78
|
+
/**
|
|
79
|
+
* Idempotency guard — true if a trace-layer memory with this source_session_id
|
|
80
|
+
* already exists.
|
|
81
|
+
*/
|
|
82
|
+
export declare function traceExistsForSession(hippoRoot: string, tenantId: string, session_id: string): boolean;
|
|
83
|
+
//# sourceMappingURL=sessions.d.ts.map
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { closeHippoDb } from '../db.js';
|
|
2
|
+
import { assertTenantId } from '../tenant.js';
|
|
3
|
+
import { redactSecretsStrict } from '../secret-detect.js';
|
|
4
|
+
import { rowToTaskSnapshot, rowToSessionEvent, } from './rows.js';
|
|
5
|
+
import { writeActiveTaskMirror, removeActiveTaskMirror, writeRecentSessionMirror } from './mirrors.js';
|
|
6
|
+
import { openStore } from './open.js';
|
|
7
|
+
export function saveActiveTaskSnapshot(hippoRoot, tenantId, snapshot) {
|
|
8
|
+
assertTenantId('saveActiveTaskSnapshot', tenantId);
|
|
9
|
+
const db = openStore(hippoRoot);
|
|
10
|
+
const now = new Date().toISOString();
|
|
11
|
+
try {
|
|
12
|
+
db.exec('BEGIN');
|
|
13
|
+
db.prepare(`UPDATE task_snapshots SET status = 'superseded', updated_at = ? WHERE status = 'active' AND tenant_id = ?`).run(now, tenantId);
|
|
14
|
+
const result = db.prepare(`
|
|
15
|
+
INSERT INTO task_snapshots(task, summary, next_step, status, source, session_id, scope, tenant_id, created_at, updated_at)
|
|
16
|
+
VALUES (?, ?, ?, 'active', ?, ?, ?, ?, ?, ?)
|
|
17
|
+
`).run(redactSecretsStrict(snapshot.task), redactSecretsStrict(snapshot.summary), redactSecretsStrict(snapshot.next_step), snapshot.source ?? 'cli', snapshot.session_id ?? null, snapshot.scope ?? null, tenantId, now, now);
|
|
18
|
+
db.exec('COMMIT');
|
|
19
|
+
const id = Number(result.lastInsertRowid ?? 0);
|
|
20
|
+
// SAFETY: row's shape matches the ten columns named in the SELECT above.
|
|
21
|
+
const row = db.prepare(`
|
|
22
|
+
SELECT id, task, summary, next_step, status, source, session_id, scope, created_at, updated_at
|
|
23
|
+
FROM task_snapshots
|
|
24
|
+
WHERE id = ?
|
|
25
|
+
`).get(id);
|
|
26
|
+
if (!row) {
|
|
27
|
+
throw new Error('Failed to reload saved active task snapshot');
|
|
28
|
+
}
|
|
29
|
+
const loaded = rowToTaskSnapshot(row);
|
|
30
|
+
writeActiveTaskMirror(hippoRoot, tenantId, loaded);
|
|
31
|
+
return loaded;
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
try {
|
|
35
|
+
db.exec('ROLLBACK');
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Ignore nested rollback failures.
|
|
39
|
+
}
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
closeHippoDb(db);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export function loadActiveTaskSnapshot(hippoRoot, tenantId) {
|
|
47
|
+
assertTenantId('loadActiveTaskSnapshot', tenantId);
|
|
48
|
+
const db = openStore(hippoRoot);
|
|
49
|
+
try {
|
|
50
|
+
// SAFETY: row's shape matches the ten columns named in the SELECT above.
|
|
51
|
+
const row = db.prepare(`
|
|
52
|
+
SELECT id, task, summary, next_step, status, source, session_id, scope, created_at, updated_at
|
|
53
|
+
FROM task_snapshots
|
|
54
|
+
WHERE status = 'active' AND tenant_id = ?
|
|
55
|
+
ORDER BY updated_at DESC, id DESC
|
|
56
|
+
LIMIT 1
|
|
57
|
+
`).get(tenantId);
|
|
58
|
+
if (!row) {
|
|
59
|
+
removeActiveTaskMirror(hippoRoot, tenantId);
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const loaded = rowToTaskSnapshot(row);
|
|
63
|
+
writeActiveTaskMirror(hippoRoot, tenantId, loaded);
|
|
64
|
+
return loaded;
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
closeHippoDb(db);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Default freshness bound for AMBIENT active-task-snapshot reads (DF1,
|
|
72
|
+
* docs/plans/2026-08-23-df1-snapshot-lifecycle.md): 72h, chosen over 48h so
|
|
73
|
+
* a Friday-evening orphan still offers continuity on Monday morning.
|
|
74
|
+
* Exported so callers can override via `loadFreshActiveTaskSnapshot`'s
|
|
75
|
+
* `opts.maxAgeMs`; deliberately no env knob (Simplicity First).
|
|
76
|
+
*/
|
|
77
|
+
export const SNAPSHOT_AMBIENT_MAX_AGE_MS = 72 * 60 * 60 * 1000;
|
|
78
|
+
/** A usable session id: non-null, non-empty string. Named predicate (not an
|
|
79
|
+
* inline `typeof` check) so the owner-match rule in
|
|
80
|
+
* `loadFreshActiveTaskSnapshot` states its contract once. */
|
|
81
|
+
function isNonEmptySessionId(value) {
|
|
82
|
+
return typeof value === 'string' && value.length > 0;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Bounded read for AMBIENT active-task-snapshot surfaces (UserPromptSubmit
|
|
86
|
+
* hook context, MCP recall block) — the never-expires fix for DF1. A
|
|
87
|
+
* snapshot written by `hippo pre-compact` has no death path tied to the
|
|
88
|
+
* session that owns it, so an orphaned row would otherwise inject into
|
|
89
|
+
* every prompt of every later session forever. Wraps `loadActiveTaskSnapshot`
|
|
90
|
+
* (unchanged, still the source of truth for explicit continuity surfaces),
|
|
91
|
+
* then applies, in order:
|
|
92
|
+
*
|
|
93
|
+
* 1. Owner match — ONLY when both `opts.sessionId` and the snapshot's
|
|
94
|
+
* `session_id` are non-null, non-empty strings and strictly equal
|
|
95
|
+
* (`===`). Owner reads are unbounded: the session that owns the snapshot
|
|
96
|
+
* can always see its own working state, regardless of age.
|
|
97
|
+
* 2. Age check — everything else, including absent-vs-absent ids. A
|
|
98
|
+
* null/undefined/empty id on EITHER side never counts as an owner match;
|
|
99
|
+
* it falls through here instead. (`runPreCompact` can legitimately save a
|
|
100
|
+
* snapshot with `session_id = null`; a null-equals-null "match" would
|
|
101
|
+
* reopen indefinite ambient injection for exactly those rows.) Returns
|
|
102
|
+
* the snapshot only when `age(updated_at) <= maxAgeMs` (default
|
|
103
|
+
* `SNAPSHOT_AMBIENT_MAX_AGE_MS`); otherwise null.
|
|
104
|
+
*
|
|
105
|
+
* No SQL change — age derives from the existing `updated_at` column.
|
|
106
|
+
*/
|
|
107
|
+
export function loadFreshActiveTaskSnapshot(hippoRoot, tenantId, opts = {}) {
|
|
108
|
+
const snapshot = loadActiveTaskSnapshot(hippoRoot, tenantId);
|
|
109
|
+
if (!snapshot)
|
|
110
|
+
return null;
|
|
111
|
+
const callerSessionId = opts.sessionId;
|
|
112
|
+
const isOwnerMatch = isNonEmptySessionId(callerSessionId) &&
|
|
113
|
+
isNonEmptySessionId(snapshot.session_id) &&
|
|
114
|
+
callerSessionId === snapshot.session_id;
|
|
115
|
+
if (isOwnerMatch)
|
|
116
|
+
return snapshot;
|
|
117
|
+
const maxAgeMs = opts.maxAgeMs ?? SNAPSHOT_AMBIENT_MAX_AGE_MS;
|
|
118
|
+
const ageMs = Date.now() - Date.parse(snapshot.updated_at);
|
|
119
|
+
return ageMs <= maxAgeMs ? snapshot : null;
|
|
120
|
+
}
|
|
121
|
+
export function clearActiveTaskSnapshot(hippoRoot, tenantId, clearedStatus = 'cleared') {
|
|
122
|
+
assertTenantId('clearActiveTaskSnapshot', tenantId);
|
|
123
|
+
const db = openStore(hippoRoot);
|
|
124
|
+
const now = new Date().toISOString();
|
|
125
|
+
try {
|
|
126
|
+
// SAFETY: active's shape matches the single `id` column selected above.
|
|
127
|
+
const active = db.prepare(`SELECT id FROM task_snapshots WHERE status = 'active' AND tenant_id = ? ORDER BY updated_at DESC, id DESC LIMIT 1`).get(tenantId);
|
|
128
|
+
if (!active?.id) {
|
|
129
|
+
removeActiveTaskMirror(hippoRoot, tenantId);
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
db.prepare(`UPDATE task_snapshots SET status = ?, updated_at = ? WHERE id = ? AND tenant_id = ?`).run(clearedStatus, now, active.id, tenantId);
|
|
133
|
+
removeActiveTaskMirror(hippoRoot, tenantId);
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
closeHippoDb(db);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Close the `active` task snapshot(s) owned by `sessionId`, for the T3
|
|
142
|
+
* session-end death path (DF1, docs/plans/2026-08-23-df1-snapshot-lifecycle.md).
|
|
143
|
+
* Only one `active` row exists per tenant in practice (supersession happens
|
|
144
|
+
* at save), but the WHERE clause scopes on `session_id` too — not just
|
|
145
|
+
* `status='active' AND tenant_id=?` — so an ending session can never close a
|
|
146
|
+
* different, newer session's active snapshot. Returns the number of rows
|
|
147
|
+
* closed (0 when no active row is owned by `sessionId`).
|
|
148
|
+
*/
|
|
149
|
+
export function closeTaskSnapshotsForSession(hippoRoot, tenantId, sessionId, status = 'session-ended') {
|
|
150
|
+
assertTenantId('closeTaskSnapshotsForSession', tenantId);
|
|
151
|
+
const db = openStore(hippoRoot);
|
|
152
|
+
const now = new Date().toISOString();
|
|
153
|
+
try {
|
|
154
|
+
const result = db.prepare(`UPDATE task_snapshots SET status = ?, updated_at = ? WHERE status = 'active' AND tenant_id = ? AND session_id = ?`).run(status, now, tenantId, sessionId);
|
|
155
|
+
return Number(result.changes ?? 0);
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
closeHippoDb(db);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
export function appendSessionEvent(hippoRoot, tenantId, event) {
|
|
162
|
+
assertTenantId('appendSessionEvent', tenantId);
|
|
163
|
+
const db = openStore(hippoRoot);
|
|
164
|
+
const now = new Date().toISOString();
|
|
165
|
+
// v1.2: scope is wired through. Default-deny in api.recall + cmdRecall
|
|
166
|
+
// continuity reads applies to slack:private:* and 'unknown:legacy' rows.
|
|
167
|
+
try {
|
|
168
|
+
const result = db.prepare(`
|
|
169
|
+
INSERT INTO session_events(session_id, task, event_type, content, source, scope, metadata_json, tenant_id, created_at)
|
|
170
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
171
|
+
`).run(event.session_id, event.task ?? null, event.event_type, event.content, event.source ?? 'cli', event.scope ?? null, JSON.stringify(event.metadata ?? {}), tenantId, now);
|
|
172
|
+
const id = Number(result.lastInsertRowid ?? 0);
|
|
173
|
+
// SAFETY: row's shape matches the nine columns named in the SELECT
|
|
174
|
+
// above.
|
|
175
|
+
const row = db.prepare(`
|
|
176
|
+
SELECT id, session_id, task, event_type, content, source, scope, metadata_json, created_at
|
|
177
|
+
FROM session_events
|
|
178
|
+
WHERE id = ?
|
|
179
|
+
`).get(id);
|
|
180
|
+
if (!row) {
|
|
181
|
+
throw new Error('Failed to reload saved session event');
|
|
182
|
+
}
|
|
183
|
+
const loaded = rowToSessionEvent(row);
|
|
184
|
+
// SAFETY: recentRows' shape matches the nine columns named in the
|
|
185
|
+
// SELECT above.
|
|
186
|
+
const recentRows = db.prepare(`
|
|
187
|
+
SELECT id, session_id, task, event_type, content, source, scope, metadata_json, created_at
|
|
188
|
+
FROM session_events
|
|
189
|
+
WHERE session_id = ? AND tenant_id = ?
|
|
190
|
+
ORDER BY created_at DESC, id DESC
|
|
191
|
+
LIMIT ?
|
|
192
|
+
`).all(loaded.session_id, tenantId, 20);
|
|
193
|
+
const recent = recentRows.map(rowToSessionEvent).reverse();
|
|
194
|
+
writeRecentSessionMirror(hippoRoot, tenantId, recent);
|
|
195
|
+
return loaded;
|
|
196
|
+
}
|
|
197
|
+
finally {
|
|
198
|
+
closeHippoDb(db);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
export function listSessionEvents(hippoRoot, tenantId, options = {}) {
|
|
202
|
+
assertTenantId('listSessionEvents', tenantId);
|
|
203
|
+
const db = openStore(hippoRoot);
|
|
204
|
+
try {
|
|
205
|
+
const clauses = ['tenant_id = ?'];
|
|
206
|
+
const params = [tenantId];
|
|
207
|
+
if (options.session_id) {
|
|
208
|
+
clauses.push('session_id = ?');
|
|
209
|
+
params.push(options.session_id);
|
|
210
|
+
}
|
|
211
|
+
if (options.task) {
|
|
212
|
+
clauses.push('task = ?');
|
|
213
|
+
params.push(options.task);
|
|
214
|
+
}
|
|
215
|
+
const limit = Math.max(1, Math.trunc(options.limit ?? 8));
|
|
216
|
+
params.push(limit);
|
|
217
|
+
const where = `WHERE ${clauses.join(' AND ')}`;
|
|
218
|
+
// SAFETY: rows' shape matches the nine columns named in the SELECT
|
|
219
|
+
// above.
|
|
220
|
+
const rows = db.prepare(`
|
|
221
|
+
SELECT id, session_id, task, event_type, content, source, scope, metadata_json, created_at
|
|
222
|
+
FROM session_events
|
|
223
|
+
${where}
|
|
224
|
+
ORDER BY created_at DESC, id DESC
|
|
225
|
+
LIMIT ?
|
|
226
|
+
`).all(...params);
|
|
227
|
+
return rows.map(rowToSessionEvent).reverse();
|
|
228
|
+
}
|
|
229
|
+
finally {
|
|
230
|
+
closeHippoDb(db);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Return session_ids with a `session_complete` event newer than `sinceMs`.
|
|
235
|
+
* Used by the sleep auto-promotion pass to bound scanning to a fixed window.
|
|
236
|
+
*/
|
|
237
|
+
export function findPromotableSessions(hippoRoot, tenantId, sinceMs) {
|
|
238
|
+
assertTenantId('findPromotableSessions', tenantId);
|
|
239
|
+
const db = openStore(hippoRoot);
|
|
240
|
+
try {
|
|
241
|
+
// SAFETY: rows' shape matches the single `session_id` column selected
|
|
242
|
+
// above.
|
|
243
|
+
const rows = db.prepare(`
|
|
244
|
+
SELECT DISTINCT session_id FROM session_events
|
|
245
|
+
WHERE event_type = 'session_complete' AND created_at >= ? AND tenant_id = ?
|
|
246
|
+
`).all(new Date(sinceMs).toISOString(), tenantId);
|
|
247
|
+
return rows;
|
|
248
|
+
}
|
|
249
|
+
finally {
|
|
250
|
+
closeHippoDb(db);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Idempotency guard — true if a trace-layer memory with this source_session_id
|
|
255
|
+
* already exists.
|
|
256
|
+
*/
|
|
257
|
+
export function traceExistsForSession(hippoRoot, tenantId, session_id) {
|
|
258
|
+
assertTenantId('traceExistsForSession', tenantId);
|
|
259
|
+
const db = openStore(hippoRoot);
|
|
260
|
+
try {
|
|
261
|
+
const row = db.prepare(`
|
|
262
|
+
SELECT 1 FROM memories
|
|
263
|
+
WHERE source_session_id = ? AND layer = 'trace' AND tenant_id = ?
|
|
264
|
+
LIMIT 1
|
|
265
|
+
`).get(session_id, tenantId);
|
|
266
|
+
return !!row;
|
|
267
|
+
}
|
|
268
|
+
finally {
|
|
269
|
+
closeHippoDb(db);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
//# sourceMappingURL=sessions.js.map
|