hippo-memory 1.59.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 -1260
- package/dist/api.js +25 -2711
- package/dist/audit.d.ts +3 -0
- package/dist/audit.js +7 -3
- package/dist/auth.d.ts +45 -4
- package/dist/auth.js +125 -48
- package/dist/autolearn.js +7 -4
- 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 +319 -13
- package/dist/cli.js +436 -8580
- package/dist/client.js +15 -8
- package/dist/compaction-record.js +12 -9
- package/dist/config.js +12 -11
- package/dist/connectors/github/backfill.js +94 -87
- package/dist/connectors/github/cli-impl.js +5 -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 +13 -12
- 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 +231 -203
- 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 -3036
- package/dist/decisions.d.ts +4 -1
- package/dist/decisions.js +9 -7
- package/dist/dedupe.js +3 -2
- package/dist/delivery-recorder.js +5 -1
- package/dist/doctor.js +4 -3
- package/dist/dormant.js +1 -0
- package/dist/embedding-provider.d.ts +1 -1
- package/dist/embedding-provider.js +7 -5
- package/dist/embeddings.d.ts +9 -52
- package/dist/embeddings.js +47 -298
- 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 +5 -4
- 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} +8 -391
- 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/handoff.js +3 -0
- 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 +8 -5
- package/dist/index.d.ts +25 -6
- package/dist/index.js +23 -6
- package/dist/invalidation.js +2 -1
- package/dist/judgment.js +7 -3
- 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 -70
- package/dist/mcp/server.js +8 -1172
- 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 +8 -3
- 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 +8 -5
- package/dist/project-briefs.d.ts +3 -0
- package/dist/project-briefs.js +7 -5
- package/dist/project-identity.d.ts +1 -1
- package/dist/project-identity.js +12 -6
- package/dist/project-merge.js +3 -1
- package/dist/quarantine.d.ts +2 -1
- package/dist/quarantine.js +8 -5
- package/dist/raw-archive-mirror-cleanup.js +2 -1
- 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 +3 -3
- package/dist/recall-trace.js +10 -14
- package/dist/refine-llm.js +20 -11
- package/dist/reject-flow.js +6 -1
- package/dist/rerankers/clef.d.ts +2 -2
- package/dist/rerankers/clef.js +72 -30
- package/dist/rerankers/cross-encoder.js +6 -4
- package/dist/rerankers/jev.d.ts +4 -2
- package/dist/rerankers/jev.js +24 -19
- package/dist/rerankers/llm.d.ts +4 -2
- package/dist/rerankers/llm.js +61 -41
- package/dist/rerankers/types.d.ts +1 -1
- package/dist/salience.js +1 -1
- package/dist/same-text.d.ts +2 -0
- package/dist/same-text.js +4 -0
- package/dist/scheduler.d.ts +1 -0
- package/dist/scheduler.js +26 -3
- 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 +3 -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 +92 -2368
- 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 +46 -40
- package/dist/skills.d.ts +3 -0
- package/dist/skills.js +7 -5
- package/dist/stdin.js +3 -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/token-ledger.js +1 -0
- 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 +2 -1
- package/dist/capture.d.ts +0 -155
- package/dist/capture.js +0 -1293
- package/dist/consolidate.d.ts +0 -58
- package/dist/consolidate.js +0 -1123
- package/dist/graph.d.ts +0 -245
- package/dist/hooks.d.ts +0 -208
- package/dist/hooks.js +0 -1073
- package/dist/importers.js +0 -897
- package/dist/predictions.js +0 -620
- package/dist/search.d.ts +0 -320
- package/dist/search.js +0 -968
- package/dist/store.d.ts +0 -744
- package/dist/store.js +0 -3372
- 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,310 @@
|
|
|
1
|
+
import { AUTO_DELETABLE_SQL } from '../memory.js';
|
|
2
|
+
import { openHippoDb, closeHippoDb } from '../db.js';
|
|
3
|
+
import { checkRejectionGuard, RejectedValueError } from '../rejection.js';
|
|
4
|
+
import { markSummaryDirtyInTx } from '../summary-dirty.js';
|
|
5
|
+
import { insertDormantRow } from '../dormant.js';
|
|
6
|
+
import { log } from '../log.js';
|
|
7
|
+
import { MEMORY_SELECT_COLUMNS, rowToEntry } from './rows.js';
|
|
8
|
+
import { audit } from './audit-event.js';
|
|
9
|
+
import { deleteFtsRow, stampOriginProject, upsertEntryRow } from './entry-row.js';
|
|
10
|
+
import { purgeMirrorBestEffort, mirrorBestEffort, writeMarkdownMirror } from './mirrors.js';
|
|
11
|
+
import { openStore } from './open.js';
|
|
12
|
+
/** Tables whose rows keep a first-class object's backing memory in `memory_id` (ON DELETE SET NULL); tests/dormant-memories.test.ts pins it to the schema. */
|
|
13
|
+
export const MEMORY_BACKED_TABLES = ['predictions', 'decisions', 'incidents', 'processes', 'policies', 'skills', 'project_briefs', 'customer_notes'];
|
|
14
|
+
/** Deleting a memory that backs an object nulls the object's link, and no restore can repair it, so no automatic pass may. */
|
|
15
|
+
const AUTOMATIC_DELETE_SQL = `${AUTO_DELETABLE_SQL}${MEMORY_BACKED_TABLES.map((t) => ` AND NOT EXISTS (SELECT 1 FROM ${t} WHERE ${t}.memory_id = memories.id)`).join('')}`;
|
|
16
|
+
/** Ids of memories that back a first-class object, for passes that plan deletes before making them. A table missing from an older schema is skipped. */
|
|
17
|
+
export function memoriesBackingObjects(hippoRoot) {
|
|
18
|
+
const ids = new Set();
|
|
19
|
+
const db = openHippoDb(hippoRoot);
|
|
20
|
+
try {
|
|
21
|
+
for (const table of MEMORY_BACKED_TABLES) {
|
|
22
|
+
try {
|
|
23
|
+
// SAFETY: SELECT of one nullable TEXT column, filtered to non-null.
|
|
24
|
+
const rows = db.prepare(`SELECT memory_id FROM ${table} WHERE memory_id IS NOT NULL`).all();
|
|
25
|
+
for (const r of rows)
|
|
26
|
+
ids.add(r.memory_id);
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
// A missing table is an older schema; any other error could hide a backing memory, so the caller stops.
|
|
30
|
+
if (!(err instanceof Error && err.message.includes('no such table')))
|
|
31
|
+
throw err;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
closeHippoDb(db);
|
|
37
|
+
}
|
|
38
|
+
return ids;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* AT1 (plan §4, round-2 fix, designed from source): db-scoped delete core.
|
|
42
|
+
* `deleteEntry` used to open+close its OWN connection, which meant it could
|
|
43
|
+
* never compose inside a caller's transaction (unlike writeEntry/
|
|
44
|
+
* writeEntryDbOnly, which already split this way). Split identically: row-
|
|
45
|
+
* meta SELECT, `DELETE FROM memories`, FTS delete, `forget` audit, DAG
|
|
46
|
+
* dirty-mark. NO filesystem I/O — the caller's own transaction may still be
|
|
47
|
+
* rolled back, and mirror writes must only happen post-commit.
|
|
48
|
+
*
|
|
49
|
+
* `opts.suppressForgetAudit` (default false, off): two AT1 callers set this
|
|
50
|
+
* so a removed non-raw row does NOT ALSO emit a `forget` row, because each
|
|
51
|
+
* already writes its own aggregate audit trail — `src/reject-flow.ts`'s
|
|
52
|
+
* `rejectValue` (single `reject_value` row covering every same-digest row
|
|
53
|
+
* removed) and `resolveConflict` (`conflict_resolve` row per resolution).
|
|
54
|
+
* Default keeps `deleteEntry` byte-identical to its pre-split behavior.
|
|
55
|
+
*
|
|
56
|
+
* Returns `{tenantId, dagParentId}` for the removed row, or `null` if no row with `id`
|
|
57
|
+
* existed or `automatic` refused it (pinned, raw, kept for good or backing an object at DELETE time, so a late pin wins).
|
|
58
|
+
*/
|
|
59
|
+
export function deleteEntryCore(db, id, opts) {
|
|
60
|
+
// SAFETY: row's shape matches the three columns named in the SELECT above.
|
|
61
|
+
const row = db
|
|
62
|
+
.prepare(`SELECT id, tenant_id, dag_parent_id FROM memories WHERE id = ?`)
|
|
63
|
+
.get(id);
|
|
64
|
+
if (!row?.id)
|
|
65
|
+
return null;
|
|
66
|
+
const guard = opts?.automatic ? ` AND ${AUTOMATIC_DELETE_SQL}` : '';
|
|
67
|
+
if (Number(db.prepare(`DELETE FROM memories WHERE id = ?${guard}`).run(id).changes ?? 0) === 0)
|
|
68
|
+
return null;
|
|
69
|
+
deleteFtsRow(db, id);
|
|
70
|
+
if (!opts?.suppressForgetAudit) {
|
|
71
|
+
audit(db, 'forget', id, opts?.reason ? { reason: opts.reason } : undefined, opts?.actor ?? 'cli', row.tenant_id);
|
|
72
|
+
}
|
|
73
|
+
// v0.30 / E2 — DAG live-coupling: forget of a child under a level-2
|
|
74
|
+
// summary marks parent dirty. Non-atomic with the DELETE (no SAVEPOINT
|
|
75
|
+
// wrapper here, same as pre-split deleteEntry); markSummaryDirtyInTx is
|
|
76
|
+
// idempotent so any future child mutation re-marks parent if this fails.
|
|
77
|
+
// Acceptable degradation, mirrors the pre-split audit best-effort posture.
|
|
78
|
+
if (row.dag_parent_id) {
|
|
79
|
+
markSummaryDirtyInTx(db, row.dag_parent_id, row.tenant_id ?? 'default', opts?.actor ?? 'cli');
|
|
80
|
+
}
|
|
81
|
+
return { tenantId: row.tenant_id ?? 'default', dagParentId: row.dag_parent_id ?? null };
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Delete an entry from SQLite and mirrors.
|
|
85
|
+
*
|
|
86
|
+
* `opts.actor` defaults to 'cli'. The api.* layer threads `ctx.actor` so HTTP
|
|
87
|
+
* callers land with `api_key:<key_id>` in the audit log without a duplicate
|
|
88
|
+
* emit from the api wrapper.
|
|
89
|
+
*
|
|
90
|
+
* Thin wrapper over `deleteEntryCore` (open → core → mirrors → close);
|
|
91
|
+
* behavior is byte-identical to the pre-split implementation for every
|
|
92
|
+
* existing caller.
|
|
93
|
+
*/
|
|
94
|
+
export function deleteEntry(hippoRoot, id, opts) {
|
|
95
|
+
const db = openStore(hippoRoot);
|
|
96
|
+
try {
|
|
97
|
+
db.exec('BEGIN IMMEDIATE');
|
|
98
|
+
let result;
|
|
99
|
+
try {
|
|
100
|
+
result = deleteEntryCore(db, id, opts);
|
|
101
|
+
db.exec('COMMIT');
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
if (db.isTransaction !== false)
|
|
105
|
+
db.exec('ROLLBACK');
|
|
106
|
+
throw err;
|
|
107
|
+
}
|
|
108
|
+
if (!result)
|
|
109
|
+
return false;
|
|
110
|
+
purgeMirrorBestEffort(hippoRoot, id, false, 'deleteEntry');
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
closeHippoDb(db);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// The child fields a level-2/3 summary is built from (loadChildrenOfSummary, generateDagSummary).
|
|
118
|
+
const SUMMARY_INPUTS = ['content', 'created', 'dag_parent_id', 'kind'];
|
|
119
|
+
function mergeOwnChanges(base, ours, live) {
|
|
120
|
+
const row = { ...live };
|
|
121
|
+
const loaded = new Map(Object.entries(base));
|
|
122
|
+
for (const [key, value] of Object.entries(ours)) {
|
|
123
|
+
if (JSON.stringify(value) !== JSON.stringify(loaded.get(key)))
|
|
124
|
+
Object.assign(row, { [key]: value });
|
|
125
|
+
}
|
|
126
|
+
return row;
|
|
127
|
+
}
|
|
128
|
+
/** Consolidation's flush, one transaction. With `snapshot` (rows as the caller loaded them), a write keeps only
|
|
129
|
+
* the fields the caller changed, takes the rest from the live row, and never resurrects a row that is gone.
|
|
130
|
+
*
|
|
131
|
+
* `dormant` (src/dormant.ts): each move's snapshot is inserted into `dormant_memories` and its `memories` row
|
|
132
|
+
* leaves exactly like a delete (FTS row, DAG parent dirty-mark, mirrors), in the same transaction, so a memory
|
|
133
|
+
* is never in both places or in neither. Deletes and moves both skip rows that are no longer auto-deletable
|
|
134
|
+
* (pinned, raw, kept for good or backing an object since the caller decided). Returns the ids that left `memories`, deleted or moved. */
|
|
135
|
+
export function batchWriteAndDelete(hippoRoot, toWrite, toDeleteIds, opts) {
|
|
136
|
+
const dormantMoves = opts?.dormant ?? [];
|
|
137
|
+
if (toWrite.length === 0 && toDeleteIds.length === 0 && dormantMoves.length === 0)
|
|
138
|
+
return [];
|
|
139
|
+
const db = openStore(hippoRoot);
|
|
140
|
+
try {
|
|
141
|
+
// BEGIN IMMEDIATE (codex delta-review P2): the AT1 tombstone probes below
|
|
142
|
+
// READ before the first write. Under a deferred BEGIN, that read pins a
|
|
143
|
+
// WAL snapshot; a concurrent writer (e.g. `hippo reject`) committing
|
|
144
|
+
// between probe and first upsert would make the later write-lock upgrade
|
|
145
|
+
// fail with SQLITE_BUSY and roll back the ENTIRE batch — the exact race
|
|
146
|
+
// the probe exists to contain. Taking the write lock up front serializes
|
|
147
|
+
// the probe and the writes on one consistent snapshot.
|
|
148
|
+
db.exec('BEGIN IMMEDIATE');
|
|
149
|
+
// v0.30 / E2 — DAG live-coupling: BEFORE deletes, snapshot dag_parent_id
|
|
150
|
+
// for every doomed row so we can mark parents dirty post-COMMIT. Done
|
|
151
|
+
// inside the same BEGIN so the SELECT sees pre-delete state.
|
|
152
|
+
// independent-review-critic R1 HIGH: consolidate.ts/sleep flushes through
|
|
153
|
+
// this path every cycle; without these hooks parents NEVER get marked
|
|
154
|
+
// dirty for the dominant mutation source (decay, merge, garbage-collect).
|
|
155
|
+
const dirty = { parents: new Set(), tenantById: new Map() };
|
|
156
|
+
const deletableIds = [];
|
|
157
|
+
if (toDeleteIds.length > 0) {
|
|
158
|
+
// A row pinned after the caller decided to delete it survives.
|
|
159
|
+
const placeholders = toDeleteIds.map(() => '?').join(',');
|
|
160
|
+
for (const row of selectAutoDeletableRows(db, placeholders, toDeleteIds, dirty))
|
|
161
|
+
deletableIds.push(row.id);
|
|
162
|
+
}
|
|
163
|
+
// v39: batch writers bypass writeEntry, so stamp store-derived origins here too (a NULL origin hides new
|
|
164
|
+
// memories from ambient context). A row queued twice keeps only its last version, the one the merge compares.
|
|
165
|
+
const stampedWrites = [...new Map(toWrite.map((e) => [e.id, stampOriginProject(hippoRoot, e)])).values()];
|
|
166
|
+
const { written, batchRejectedSkips } = applyBatchWrites(db, stampedWrites, opts?.snapshot, dirty);
|
|
167
|
+
const removedIds = moveDormantAndDelete(db, dormantMoves, deletableIds, dirty);
|
|
168
|
+
// Fire dirty-mark for every collected parent INSIDE the BEGIN, so the
|
|
169
|
+
// dirty flag commits atomically with the writes + deletes.
|
|
170
|
+
for (const parentId of dirty.parents) {
|
|
171
|
+
markSummaryDirtyInTx(db, parentId, dirty.tenantById.get(parentId) ?? 'default', 'batch');
|
|
172
|
+
}
|
|
173
|
+
db.exec('COMMIT');
|
|
174
|
+
if (batchRejectedSkips > 0) {
|
|
175
|
+
log.warn(`batchWriteAndDelete: skipped ${batchRejectedSkips} write(s) whose content matches a rejected value (tombstone hit during the batch transaction)`);
|
|
176
|
+
}
|
|
177
|
+
// Sync mirrors once after all DB writes. Entries skipped above were
|
|
178
|
+
// never inserted — writing their markdown mirror would resurrect the
|
|
179
|
+
// exact content the skip just kept out of the DB.
|
|
180
|
+
mirrorBestEffort('markdown mirrors', () => {
|
|
181
|
+
for (const entry of written)
|
|
182
|
+
writeMarkdownMirror(hippoRoot, entry);
|
|
183
|
+
});
|
|
184
|
+
for (const id of removedIds)
|
|
185
|
+
purgeMirrorBestEffort(hippoRoot, id, false, 'batchWriteAndDelete');
|
|
186
|
+
return removedIds;
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
try {
|
|
190
|
+
db.exec('ROLLBACK');
|
|
191
|
+
}
|
|
192
|
+
catch { /* ignore */ }
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
finally {
|
|
196
|
+
closeHippoDb(db);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/** Moves eligible dormant rows out, deletes them with the doomed rows, and returns every removed id. */
|
|
200
|
+
function moveDormantAndDelete(db, dormantMoves, deletableIds, dirty) {
|
|
201
|
+
// Dormant moves: same eligibility and DAG bookkeeping as deletes.
|
|
202
|
+
const movable = [];
|
|
203
|
+
if (dormantMoves.length > 0) {
|
|
204
|
+
const byId = new Map(dormantMoves.map((m) => [m.entry.id, m]));
|
|
205
|
+
const placeholders = dormantMoves.map(() => '?').join(',');
|
|
206
|
+
for (const row of selectAutoDeletableRows(db, placeholders, [...byId.keys()], dirty))
|
|
207
|
+
movable.push(byId.get(row.id));
|
|
208
|
+
}
|
|
209
|
+
for (const move of movable) {
|
|
210
|
+
insertDormantRow(db, move);
|
|
211
|
+
}
|
|
212
|
+
const removedIds = [...deletableIds, ...movable.map((m) => m.entry.id)];
|
|
213
|
+
for (const id of removedIds) {
|
|
214
|
+
db.prepare('DELETE FROM memories WHERE id = ?').run(id);
|
|
215
|
+
deleteFtsRow(db, id);
|
|
216
|
+
}
|
|
217
|
+
return removedIds;
|
|
218
|
+
}
|
|
219
|
+
/** The still auto-deletable rows among `ids`, recording each one's DAG parent as dirty. */
|
|
220
|
+
function selectAutoDeletableRows(db, placeholders, ids, dirty) {
|
|
221
|
+
// SAFETY: rows' shape matches the three columns named in the SELECT.
|
|
222
|
+
const rows = db.prepare(`SELECT id, dag_parent_id, tenant_id FROM memories WHERE id IN (${placeholders}) AND ${AUTOMATIC_DELETE_SQL}`).all(...ids);
|
|
223
|
+
for (const row of rows) {
|
|
224
|
+
if (row.dag_parent_id) {
|
|
225
|
+
dirty.parents.add(row.dag_parent_id);
|
|
226
|
+
dirty.tenantById.set(row.dag_parent_id, row.tenant_id ?? 'default');
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return rows;
|
|
230
|
+
}
|
|
231
|
+
function applyBatchWrites(db, stampedWrites, snapshot, dirty) {
|
|
232
|
+
// AT1 P1 fix (codex, batch-transaction rejection race): the producer-side
|
|
233
|
+
// check (e.g. consolidate.ts's merge pass) runs BEFORE this transaction,
|
|
234
|
+
// on a different connection. A `hippo reject X` that commits in that
|
|
235
|
+
// window is invisible to it — a queued same-id write of X already
|
|
236
|
+
// sitting in `toWrite` (decay/replay re-persist, or a merge built before
|
|
237
|
+
// the reject) would silently re-INSERT the just-rejected row via the
|
|
238
|
+
// blind bypass. Fix: one indexed point probe per batch entry, on THIS
|
|
239
|
+
// connection, INSIDE this transaction — closes the race regardless of
|
|
240
|
+
// which write class hits it. N is small per sleep, so the extra query
|
|
241
|
+
// per entry is cheap.
|
|
242
|
+
//
|
|
243
|
+
// Skip, don't throw: the batch must still complete for every OTHER
|
|
244
|
+
// entry. Skipping is correct for every write class here — a merge
|
|
245
|
+
// summary skip just means that rollup is absent this cycle (its source
|
|
246
|
+
// facts stay merely demoted, recoverable next sleep); a skipped
|
|
247
|
+
// demotion/replay re-persist of a rejected-removed row means it stays
|
|
248
|
+
// gone, which is the entire point of the tombstone.
|
|
249
|
+
let batchRejectedSkips = 0;
|
|
250
|
+
const written = [];
|
|
251
|
+
const readLiveRow = db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE id = ?`);
|
|
252
|
+
for (const entry of stampedWrites) {
|
|
253
|
+
const base = snapshot?.get(entry.id);
|
|
254
|
+
// SAFETY: MEMORY_SELECT_COLUMNS is the MemoryRow shape rowToEntry reads.
|
|
255
|
+
const liveRow = readLiveRow.get(entry.id);
|
|
256
|
+
const live = liveRow ? rowToEntry(liveRow) : undefined;
|
|
257
|
+
const row = base && live ? mergeOwnChanges(base, entry, live) : entry;
|
|
258
|
+
if (isRejectedBatchWrite(db, row)) {
|
|
259
|
+
batchRejectedSkips++;
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
if (base && !live)
|
|
263
|
+
continue;
|
|
264
|
+
written.push(row);
|
|
265
|
+
// AT1 (plan §3, corrected): bypass the rejection guard here.
|
|
266
|
+
// Consolidation merges are DETERMINISTIC CONCATENATION (mergeContents,
|
|
267
|
+
// consolidate.ts:736-751) of already-guarded leaf facts, not an LLM
|
|
268
|
+
// paraphrase — refusing mid-batch would abort the whole consolidation
|
|
269
|
+
// transaction. The bypass is safe because consolidate.ts's merge pass
|
|
270
|
+
// now checks the merged content's rejection digest against the
|
|
271
|
+
// tenant's tombstones BEFORE ever pushing a merge into pendingWrites,
|
|
272
|
+
// skipping that merge entirely on a hit, AND because the point-probe
|
|
273
|
+
// immediately above closes the race window between that producer
|
|
274
|
+
// check and this COMMIT. The guard itself still belongs on leaf
|
|
275
|
+
// inserts, which write through writeEntry / writeEntryDbOnly and stay
|
|
276
|
+
// guarded (bypassRejectionGuard defaults false).
|
|
277
|
+
upsertEntryRow(db, row, true);
|
|
278
|
+
// Hook for writes: a new child, or a change to what its summary reads, marks the parent dirty; decay alone does not.
|
|
279
|
+
if (row.dag_parent_id && (!live || SUMMARY_INPUTS.some((k) => row[k] !== live[k]))) {
|
|
280
|
+
dirty.parents.add(row.dag_parent_id);
|
|
281
|
+
dirty.tenantById.set(row.dag_parent_id, row.tenantId);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return { written, batchRejectedSkips };
|
|
285
|
+
}
|
|
286
|
+
/** True, after auditing the refusal, when the write would introduce a rejected value. */
|
|
287
|
+
function isRejectedBatchWrite(db, row) {
|
|
288
|
+
const entryTenantId = row.tenantId ?? 'default';
|
|
289
|
+
// Codex delta-review P2 fix: reuse checkRejectionGuard rather than a
|
|
290
|
+
// bare tombstone probe — the guard's content-INTRODUCTION
|
|
291
|
+
// classification must apply here too. A tombstone can legitimately
|
|
292
|
+
// coexist with a live same-content row (resolveConflict deliberately
|
|
293
|
+
// excludes keepId from its sweep; unreject-then-re-reject windows), and
|
|
294
|
+
// an unconditional skip would starve that row of decay/replay metadata
|
|
295
|
+
// updates forever. The guard throws only when the write is new-row or
|
|
296
|
+
// changes content TO the rejected value; unchanged same-id re-persists
|
|
297
|
+
// pass through, exactly as on the writeEntry path.
|
|
298
|
+
try {
|
|
299
|
+
checkRejectionGuard(db, entryTenantId, row.id, row.content);
|
|
300
|
+
}
|
|
301
|
+
catch (err) {
|
|
302
|
+
if (err instanceof RejectedValueError) {
|
|
303
|
+
audit(db, 'reject_refusal', row.id, { digest: err.digest, reason: err.reason }, 'sleep-batch', entryTenantId);
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
throw err;
|
|
307
|
+
}
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
//# sourceMappingURL=delete-and-batch.js.map
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { MemoryEntry } from '../memory.js';
|
|
2
|
+
import { type DatabaseSyncLike } from '../db.js';
|
|
3
|
+
/**
|
|
4
|
+
* Read a memory entry by ID.
|
|
5
|
+
*
|
|
6
|
+
* When `tenantId` is provided, the read is scoped to that tenant (cross-tenant
|
|
7
|
+
* lookups return null). When omitted, no tenant filter is applied — preserves
|
|
8
|
+
* legacy single-tenant callers and the writeEntry/readEntry round-trip.
|
|
9
|
+
*/
|
|
10
|
+
export declare function readEntry(hippoRoot: string, id: string, tenantId?: string): MemoryEntry | null;
|
|
11
|
+
/**
|
|
12
|
+
* Batched lookup. Caps at 500 ids per call to keep the IN(?,?,...) clause
|
|
13
|
+
* within SQLite limits. Tenant filter is enforced when `tenantId` is passed.
|
|
14
|
+
* Used by DAG-aware recall (docs/plans/2026-05-05-dag-recall.md Task 1.5)
|
|
15
|
+
* to fetch parent summaries for a set of overflowed leaves.
|
|
16
|
+
*/
|
|
17
|
+
export declare function loadEntriesByIds(hippoRoot: string, ids: readonly string[], tenantId?: string): MemoryEntry[];
|
|
18
|
+
/**
|
|
19
|
+
* All `kind='raw'` rows for a given session, tenant-scoped, returned
|
|
20
|
+
* oldest-first. Used by `api.assemble` to walk a session's chronological
|
|
21
|
+
* context. Excludes superseded rows.
|
|
22
|
+
*
|
|
23
|
+
* Cap semantics (v1.6.2 codex fix): when `cap` is provided, the NEWEST
|
|
24
|
+
* `cap` rows are loaded — `ORDER BY created DESC LIMIT cap` server-side,
|
|
25
|
+
* reversed to oldest-first client-side. Pre-v1.6.2 ordered ASC + LIMIT,
|
|
26
|
+
* which silently dropped the newest rows and broke fresh-tail in assemble.
|
|
27
|
+
*
|
|
28
|
+
* Returns `[]` for an empty sessionId. Final order: `created ASC, id ASC`.
|
|
29
|
+
*/
|
|
30
|
+
export declare function loadSessionRawMemories(hippoRoot: string, sessionId: string, tenantId?: string, cap?: number): MemoryEntry[];
|
|
31
|
+
/**
|
|
32
|
+
* Pre-cap, scope-aware row count for a session. Lets `assemble` report
|
|
33
|
+
* the full session size even when `rowCap` truncates the loaded window,
|
|
34
|
+
* WITHOUT leaking rows the caller wouldn't have been allowed to load.
|
|
35
|
+
*
|
|
36
|
+
* v1.6.3 codex P1 / senior P0: an earlier draft of this helper ran an
|
|
37
|
+
* unscoped COUNT, which let a no-scope caller infer the existence of
|
|
38
|
+
* private rows by comparing `totalRaw` against `items.length`. This
|
|
39
|
+
* version SQL-encodes the same default-deny rule `passesScopeFilterForRecall`
|
|
40
|
+
* applies in TS:
|
|
41
|
+
* - explicit scope passed: exact-match
|
|
42
|
+
* - no scope: rows where scope IS NULL, or scope is NOT a `<source>:private:*`
|
|
43
|
+
* pattern AND not the `unknown:legacy` quarantine bucket.
|
|
44
|
+
*
|
|
45
|
+
* `tenantId` is optional for back-compat. Pass `undefined` only when
|
|
46
|
+
* intentionally counting cross-tenant; `assemble()` passes `ctx.tenantId`.
|
|
47
|
+
*/
|
|
48
|
+
export declare function countSessionRawMemories(hippoRoot: string, sessionId: string, tenantId?: string, scope?: string): number;
|
|
49
|
+
/**
|
|
50
|
+
* Last N kind='raw' memories by `created` desc. Tenant scoped. When
|
|
51
|
+
* `sessionId` is supplied, also constrains to a specific session — that
|
|
52
|
+
* is the correct shape for "what did I just see in THIS session."
|
|
53
|
+
*
|
|
54
|
+
* v1.6.2 codex review fix: pre-v1.6.2 was tenant-wide only. With multiple
|
|
55
|
+
* concurrent sessions in a tenant, fresh-tail recall surfaced unrelated
|
|
56
|
+
* rows from other sessions and stamped them `isFreshTail=true`. Callers
|
|
57
|
+
* that want session-scoped fresh-tail now pass `sessionId`. The
|
|
58
|
+
* tenant-wide form (no sessionId) still exists for "anything new across
|
|
59
|
+
* the whole tenant" — pass undefined to opt in.
|
|
60
|
+
*
|
|
61
|
+
* Bounded count cap at 200 — beyond that the caller should filter via
|
|
62
|
+
* tags/scope rather than time-windowed recall.
|
|
63
|
+
*
|
|
64
|
+
* Deprecation note (v1.6.5) — the **tenant-wide call shape** (omitting
|
|
65
|
+
* `sessionId`) is rarely the right shape for "what did I just see in this
|
|
66
|
+
* conversation". `api.recall` enforces session scoping when
|
|
67
|
+
* `HIPPO_REQUIRE_SESSION_SCOPED_FRESH_TAIL=1` is set, throwing
|
|
68
|
+
* `RecallContractError` instead. Tenant-wide remains the back-compat default
|
|
69
|
+
* but is discouraged for new callers. Passing `sessionId` is fully supported
|
|
70
|
+
* and recommended; this function is NOT deprecated as a whole.
|
|
71
|
+
*/
|
|
72
|
+
export declare function loadFreshRawMemories(hippoRoot: string, count: number, tenantId?: string, sessionId?: string): MemoryEntry[];
|
|
73
|
+
/**
|
|
74
|
+
* Direct DAG children of a parent summary. Tenant scoped. Returns only rows
|
|
75
|
+
* whose `dag_parent_id` matches `parentId`; does NOT walk recursively.
|
|
76
|
+
* Used by `drillDown` (Task 3).
|
|
77
|
+
*/
|
|
78
|
+
export declare function loadChildrenOf(hippoRoot: string, parentId: string, tenantId?: string): MemoryEntry[];
|
|
79
|
+
/**
|
|
80
|
+
* Load all entries from SQLite.
|
|
81
|
+
*
|
|
82
|
+
* When `tenantId` is provided, results are scoped to that tenant. Omitting it
|
|
83
|
+
* yields all rows (legacy behavior used by consolidate/autolearn etc.). Recall
|
|
84
|
+
* paths that surface results to a user MUST pass a resolved tenant.
|
|
85
|
+
*/
|
|
86
|
+
export declare function loadAllEntries(hippoRoot: string, tenantId?: string): MemoryEntry[];
|
|
87
|
+
/** Every memory row on an open connection, so a caller can read inside its own transaction. */
|
|
88
|
+
export declare function selectAllEntries(db: DatabaseSyncLike, tenantId?: string): MemoryEntry[];
|
|
89
|
+
/** Live rows whose source starts with `prefix`, on the caller's handle; LIKE folds case, so the prefix is checked again exactly. */
|
|
90
|
+
export declare function selectLiveEntriesBySourcePrefix(db: DatabaseSyncLike, tenantId: string, prefix: string): MemoryEntry[];
|
|
91
|
+
export declare function loadContentsWithTag(hippoRoot: string, tenantId: string, tag: string): string[];
|
|
92
|
+
//# sourceMappingURL=entry-reads.d.ts.map
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { closeHippoDb } from '../db.js';
|
|
2
|
+
import { MEMORY_SELECT_COLUMNS, rowToEntry, parseJsonArray } from './rows.js';
|
|
3
|
+
import { openStore } from './open.js';
|
|
4
|
+
/**
|
|
5
|
+
* Read a memory entry by ID.
|
|
6
|
+
*
|
|
7
|
+
* When `tenantId` is provided, the read is scoped to that tenant (cross-tenant
|
|
8
|
+
* lookups return null). When omitted, no tenant filter is applied — preserves
|
|
9
|
+
* legacy single-tenant callers and the writeEntry/readEntry round-trip.
|
|
10
|
+
*/
|
|
11
|
+
export function readEntry(hippoRoot, id, tenantId) {
|
|
12
|
+
const db = openStore(hippoRoot);
|
|
13
|
+
try {
|
|
14
|
+
// SAFETY: both branches select exactly MEMORY_SELECT_COLUMNS, matching
|
|
15
|
+
// MemoryRow's field set.
|
|
16
|
+
const row = tenantId !== undefined
|
|
17
|
+
? db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE id = ? AND tenant_id = ?`).get(id, tenantId)
|
|
18
|
+
: db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE id = ?`).get(id);
|
|
19
|
+
return row ? rowToEntry(row) : null;
|
|
20
|
+
}
|
|
21
|
+
finally {
|
|
22
|
+
closeHippoDb(db);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Batched lookup. Caps at 500 ids per call to keep the IN(?,?,...) clause
|
|
27
|
+
* within SQLite limits. Tenant filter is enforced when `tenantId` is passed.
|
|
28
|
+
* Used by DAG-aware recall (docs/plans/2026-05-05-dag-recall.md Task 1.5)
|
|
29
|
+
* to fetch parent summaries for a set of overflowed leaves.
|
|
30
|
+
*/
|
|
31
|
+
export function loadEntriesByIds(hippoRoot, ids, tenantId) {
|
|
32
|
+
if (ids.length === 0)
|
|
33
|
+
return [];
|
|
34
|
+
const capped = ids.slice(0, 500);
|
|
35
|
+
const db = openStore(hippoRoot);
|
|
36
|
+
try {
|
|
37
|
+
const placeholders = capped.map(() => '?').join(',');
|
|
38
|
+
// T2: no ORDER BY meant row order followed SQLite's IN(...) scan order
|
|
39
|
+
// (undefined w.r.t. the caller's `ids` order). created ASC, id ASC
|
|
40
|
+
// makes it deterministic.
|
|
41
|
+
// SAFETY: both branches select exactly MEMORY_SELECT_COLUMNS, matching
|
|
42
|
+
// MemoryRow's field set.
|
|
43
|
+
const rows = tenantId !== undefined
|
|
44
|
+
? db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE id IN (${placeholders}) AND tenant_id = ? ORDER BY created ASC, content ASC, id ASC`).all(...capped, tenantId)
|
|
45
|
+
: db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE id IN (${placeholders}) ORDER BY created ASC, content ASC, id ASC`).all(...capped);
|
|
46
|
+
return rows.map(rowToEntry);
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
closeHippoDb(db);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* All `kind='raw'` rows for a given session, tenant-scoped, returned
|
|
54
|
+
* oldest-first. Used by `api.assemble` to walk a session's chronological
|
|
55
|
+
* context. Excludes superseded rows.
|
|
56
|
+
*
|
|
57
|
+
* Cap semantics (v1.6.2 codex fix): when `cap` is provided, the NEWEST
|
|
58
|
+
* `cap` rows are loaded — `ORDER BY created DESC LIMIT cap` server-side,
|
|
59
|
+
* reversed to oldest-first client-side. Pre-v1.6.2 ordered ASC + LIMIT,
|
|
60
|
+
* which silently dropped the newest rows and broke fresh-tail in assemble.
|
|
61
|
+
*
|
|
62
|
+
* Returns `[]` for an empty sessionId. Final order: `created ASC, id ASC`.
|
|
63
|
+
*/
|
|
64
|
+
export function loadSessionRawMemories(hippoRoot, sessionId, tenantId, cap) {
|
|
65
|
+
if (!sessionId)
|
|
66
|
+
return [];
|
|
67
|
+
const db = openStore(hippoRoot);
|
|
68
|
+
try {
|
|
69
|
+
const params = [];
|
|
70
|
+
let sql = `SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE kind = 'raw' AND source_session_id = ? AND superseded_by IS NULL`;
|
|
71
|
+
params.push(sessionId);
|
|
72
|
+
if (tenantId !== undefined) {
|
|
73
|
+
sql += ' AND tenant_id = ?';
|
|
74
|
+
params.push(tenantId);
|
|
75
|
+
}
|
|
76
|
+
if (cap !== undefined && cap > 0) {
|
|
77
|
+
sql += ' ORDER BY created DESC, id DESC LIMIT ?';
|
|
78
|
+
params.push(cap);
|
|
79
|
+
// SAFETY: sql starts from MEMORY_SELECT_COLUMNS, matching MemoryRow.
|
|
80
|
+
const rows = db.prepare(sql).all(...params);
|
|
81
|
+
return rows.reverse().map(rowToEntry);
|
|
82
|
+
}
|
|
83
|
+
sql += ' ORDER BY created ASC, id ASC';
|
|
84
|
+
// SAFETY: sql starts from MEMORY_SELECT_COLUMNS, matching MemoryRow.
|
|
85
|
+
const rows = db.prepare(sql).all(...params);
|
|
86
|
+
return rows.map(rowToEntry);
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
closeHippoDb(db);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Pre-cap, scope-aware row count for a session. Lets `assemble` report
|
|
94
|
+
* the full session size even when `rowCap` truncates the loaded window,
|
|
95
|
+
* WITHOUT leaking rows the caller wouldn't have been allowed to load.
|
|
96
|
+
*
|
|
97
|
+
* v1.6.3 codex P1 / senior P0: an earlier draft of this helper ran an
|
|
98
|
+
* unscoped COUNT, which let a no-scope caller infer the existence of
|
|
99
|
+
* private rows by comparing `totalRaw` against `items.length`. This
|
|
100
|
+
* version SQL-encodes the same default-deny rule `passesScopeFilterForRecall`
|
|
101
|
+
* applies in TS:
|
|
102
|
+
* - explicit scope passed: exact-match
|
|
103
|
+
* - no scope: rows where scope IS NULL, or scope is NOT a `<source>:private:*`
|
|
104
|
+
* pattern AND not the `unknown:legacy` quarantine bucket.
|
|
105
|
+
*
|
|
106
|
+
* `tenantId` is optional for back-compat. Pass `undefined` only when
|
|
107
|
+
* intentionally counting cross-tenant; `assemble()` passes `ctx.tenantId`.
|
|
108
|
+
*/
|
|
109
|
+
export function countSessionRawMemories(hippoRoot, sessionId, tenantId, scope) {
|
|
110
|
+
if (!sessionId)
|
|
111
|
+
return 0;
|
|
112
|
+
const db = openStore(hippoRoot);
|
|
113
|
+
try {
|
|
114
|
+
const params = [];
|
|
115
|
+
let sql = `SELECT COUNT(*) AS c FROM memories WHERE kind = 'raw' AND source_session_id = ? AND superseded_by IS NULL`;
|
|
116
|
+
params.push(sessionId);
|
|
117
|
+
if (tenantId !== undefined) {
|
|
118
|
+
sql += ' AND tenant_id = ?';
|
|
119
|
+
params.push(tenantId);
|
|
120
|
+
}
|
|
121
|
+
if (scope !== undefined && scope !== '') {
|
|
122
|
+
sql += ' AND scope = ?';
|
|
123
|
+
params.push(scope);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
// SQL-ify the TS default-deny: scope IS NULL OR (NOT LIKE '%:private:%'
|
|
127
|
+
// AND != 'unknown:legacy'). Mirrors api.passesScopeFilterForRecall.
|
|
128
|
+
sql += ` AND (scope IS NULL OR (scope NOT LIKE '%:private:%' AND scope != 'unknown:legacy'))`;
|
|
129
|
+
}
|
|
130
|
+
// SAFETY: row's shape matches the single `COUNT(*) AS c` column above.
|
|
131
|
+
const row = db.prepare(sql).get(...params);
|
|
132
|
+
return Number(row?.c ?? 0);
|
|
133
|
+
}
|
|
134
|
+
finally {
|
|
135
|
+
closeHippoDb(db);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Last N kind='raw' memories by `created` desc. Tenant scoped. When
|
|
140
|
+
* `sessionId` is supplied, also constrains to a specific session — that
|
|
141
|
+
* is the correct shape for "what did I just see in THIS session."
|
|
142
|
+
*
|
|
143
|
+
* v1.6.2 codex review fix: pre-v1.6.2 was tenant-wide only. With multiple
|
|
144
|
+
* concurrent sessions in a tenant, fresh-tail recall surfaced unrelated
|
|
145
|
+
* rows from other sessions and stamped them `isFreshTail=true`. Callers
|
|
146
|
+
* that want session-scoped fresh-tail now pass `sessionId`. The
|
|
147
|
+
* tenant-wide form (no sessionId) still exists for "anything new across
|
|
148
|
+
* the whole tenant" — pass undefined to opt in.
|
|
149
|
+
*
|
|
150
|
+
* Bounded count cap at 200 — beyond that the caller should filter via
|
|
151
|
+
* tags/scope rather than time-windowed recall.
|
|
152
|
+
*
|
|
153
|
+
* Deprecation note (v1.6.5) — the **tenant-wide call shape** (omitting
|
|
154
|
+
* `sessionId`) is rarely the right shape for "what did I just see in this
|
|
155
|
+
* conversation". `api.recall` enforces session scoping when
|
|
156
|
+
* `HIPPO_REQUIRE_SESSION_SCOPED_FRESH_TAIL=1` is set, throwing
|
|
157
|
+
* `RecallContractError` instead. Tenant-wide remains the back-compat default
|
|
158
|
+
* but is discouraged for new callers. Passing `sessionId` is fully supported
|
|
159
|
+
* and recommended; this function is NOT deprecated as a whole.
|
|
160
|
+
*/
|
|
161
|
+
export function loadFreshRawMemories(hippoRoot, count, tenantId, sessionId) {
|
|
162
|
+
if (count <= 0)
|
|
163
|
+
return [];
|
|
164
|
+
const capped = Math.min(count, 200);
|
|
165
|
+
const db = openStore(hippoRoot);
|
|
166
|
+
try {
|
|
167
|
+
const params = [];
|
|
168
|
+
let sql = `SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE kind = 'raw' AND superseded_by IS NULL`;
|
|
169
|
+
if (tenantId !== undefined) {
|
|
170
|
+
sql += ' AND tenant_id = ?';
|
|
171
|
+
params.push(tenantId);
|
|
172
|
+
}
|
|
173
|
+
if (sessionId !== undefined && sessionId !== '') {
|
|
174
|
+
sql += ' AND source_session_id = ?';
|
|
175
|
+
params.push(sessionId);
|
|
176
|
+
}
|
|
177
|
+
// T2: tie tail keeps the LIMIT window keyed on `created` while making
|
|
178
|
+
// same-`created` rows deterministic. `content` before `id` (codex
|
|
179
|
+
// review): ids are random UUIDs, so an id-only tail would pick WHICH
|
|
180
|
+
// same-created rows make the window per-instance; content is
|
|
181
|
+
// cross-ingest-stable.
|
|
182
|
+
sql += ' ORDER BY created DESC, content ASC, id ASC LIMIT ?';
|
|
183
|
+
params.push(capped);
|
|
184
|
+
// SAFETY: sql starts from MEMORY_SELECT_COLUMNS, matching MemoryRow.
|
|
185
|
+
const rows = db.prepare(sql).all(...params);
|
|
186
|
+
return rows.map(rowToEntry);
|
|
187
|
+
}
|
|
188
|
+
finally {
|
|
189
|
+
closeHippoDb(db);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Direct DAG children of a parent summary. Tenant scoped. Returns only rows
|
|
194
|
+
* whose `dag_parent_id` matches `parentId`; does NOT walk recursively.
|
|
195
|
+
* Used by `drillDown` (Task 3).
|
|
196
|
+
*/
|
|
197
|
+
export function loadChildrenOf(hippoRoot, parentId, tenantId) {
|
|
198
|
+
const db = openStore(hippoRoot);
|
|
199
|
+
try {
|
|
200
|
+
// SAFETY: both branches select exactly MEMORY_SELECT_COLUMNS, matching
|
|
201
|
+
// MemoryRow's field set.
|
|
202
|
+
const rows = tenantId !== undefined
|
|
203
|
+
? db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE dag_parent_id = ? AND tenant_id = ? ORDER BY created ASC, id ASC`).all(parentId, tenantId)
|
|
204
|
+
: db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE dag_parent_id = ? ORDER BY created ASC, id ASC`).all(parentId);
|
|
205
|
+
return rows.map(rowToEntry);
|
|
206
|
+
}
|
|
207
|
+
finally {
|
|
208
|
+
closeHippoDb(db);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Load all entries from SQLite.
|
|
213
|
+
*
|
|
214
|
+
* When `tenantId` is provided, results are scoped to that tenant. Omitting it
|
|
215
|
+
* yields all rows (legacy behavior used by consolidate/autolearn etc.). Recall
|
|
216
|
+
* paths that surface results to a user MUST pass a resolved tenant.
|
|
217
|
+
*/
|
|
218
|
+
export function loadAllEntries(hippoRoot, tenantId) {
|
|
219
|
+
const db = openStore(hippoRoot);
|
|
220
|
+
try {
|
|
221
|
+
return selectAllEntries(db, tenantId);
|
|
222
|
+
}
|
|
223
|
+
finally {
|
|
224
|
+
closeHippoDb(db);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/** Every memory row on an open connection, so a caller can read inside its own transaction. */
|
|
228
|
+
export function selectAllEntries(db, tenantId) {
|
|
229
|
+
// SAFETY: both branches select exactly MEMORY_SELECT_COLUMNS, matching
|
|
230
|
+
// MemoryRow's field set.
|
|
231
|
+
const rows = tenantId !== undefined
|
|
232
|
+
? db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE tenant_id = ? ORDER BY created ASC, id ASC`).all(tenantId)
|
|
233
|
+
: db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories ORDER BY created ASC, id ASC`).all();
|
|
234
|
+
return rows.map(rowToEntry);
|
|
235
|
+
}
|
|
236
|
+
/** Live rows whose source starts with `prefix`, on the caller's handle; LIKE folds case, so the prefix is checked again exactly. */
|
|
237
|
+
export function selectLiveEntriesBySourcePrefix(db, tenantId, prefix) {
|
|
238
|
+
// SAFETY: selects exactly MEMORY_SELECT_COLUMNS, matching MemoryRow's field set.
|
|
239
|
+
const rows = db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE tenant_id = ? AND superseded_by IS NULL AND source LIKE ? ESCAPE '\\'`).all(tenantId, `${prefix.replace(/[%_\\]/g, '\\$&')}%`);
|
|
240
|
+
return rows.map(rowToEntry).filter((entry) => entry.source.startsWith(prefix));
|
|
241
|
+
}
|
|
242
|
+
// Content of every tenant row tagged `tag`, without reading the rest of the store.
|
|
243
|
+
// `instr` is a substring prefilter over the raw JSON; `includes` below re-checks exactly.
|
|
244
|
+
export function loadContentsWithTag(hippoRoot, tenantId, tag) {
|
|
245
|
+
const db = openStore(hippoRoot);
|
|
246
|
+
try {
|
|
247
|
+
/** SAFETY: rows' shape matches the two columns named in the SELECT below. */
|
|
248
|
+
const rows = db.prepare(`SELECT content, tags_json FROM memories WHERE tenant_id = ? AND instr(tags_json, ?) > 0`).all(tenantId, JSON.stringify(tag));
|
|
249
|
+
return rows.filter((r) => parseJsonArray(r.tags_json).includes(tag)).map((r) => r.content);
|
|
250
|
+
}
|
|
251
|
+
finally {
|
|
252
|
+
closeHippoDb(db);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
//# sourceMappingURL=entry-reads.js.map
|