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,634 @@
|
|
|
1
|
+
// Read path: recall (sync) and retrieve (async, adds the vector arm).
|
|
2
|
+
import { envRequireSessionScopedFreshTail } from '../env.js';
|
|
3
|
+
import { openHippoDb, closeHippoDb } from '../db.js';
|
|
4
|
+
import { DEFAULT_SEARCH_CANDIDATE_LIMIT } from '../store/rows.js';
|
|
5
|
+
import { strengthenRetrieved } from '../store/entry-writes.js';
|
|
6
|
+
import { isRecallBoostAblated } from '../ablation.js';
|
|
7
|
+
import { loadEntriesByIds, loadFreshRawMemories } from '../store/entry-reads.js';
|
|
8
|
+
import { loadRecallSearchEntries, recallScopeFilter } from '../store/search-rows.js';
|
|
9
|
+
import { loadActiveTaskSnapshot, listSessionEvents } from '../store/sessions.js';
|
|
10
|
+
import { loadLatestHandoff } from '../store/handoffs.js';
|
|
11
|
+
import { estimateTokens } from '../token-ledger.js';
|
|
12
|
+
import { formatHandoffEvidenceLine } from '../handoff.js';
|
|
13
|
+
import { appendAuditEvent, auditQueryFields } from '../audit.js';
|
|
14
|
+
import { writeRecallTrace, writeRecallTraceAtRoot } from '../recall-trace.js';
|
|
15
|
+
import { applyGoalStackBoost } from '../goals.js';
|
|
16
|
+
import { hybridSearch } from '../search/hybrid.js';
|
|
17
|
+
import { physicsSearch } from '../search/physics-search.js';
|
|
18
|
+
import { churnStaleFactor } from '../search/boosts.js';
|
|
19
|
+
import { compareEntryIdentity } from '../compare.js';
|
|
20
|
+
import { dropHeldCopies, duplicateKey, storedTextKeys } from '../same-text.js';
|
|
21
|
+
import { loadConfig } from '../config.js';
|
|
22
|
+
import { computePlanningFallacyOutput } from '../predictions/planning-fallacy.js';
|
|
23
|
+
import { detectAnchoring, hashQueryText, biasHintEnabled } from '../recall-history.js';
|
|
24
|
+
import { detectAvailabilityBias } from '../availability.js';
|
|
25
|
+
import { passesScopeFilterForRecall, assertScopeRequestAllowed, isRestrictedScope } from '../recall-scope.js';
|
|
26
|
+
import { RecallContractError } from './types.js';
|
|
27
|
+
/**
|
|
28
|
+
* Shared construction helper for `RecallSuppressionSummary`. Used by
|
|
29
|
+
* `api.recall`, `cmdRecall`, and the MCP `hippo_recall` handler so all three
|
|
30
|
+
* pipelines produce the same shape without duplicating field-construction
|
|
31
|
+
* logic. Pass-through identity today; kept as a helper so future field
|
|
32
|
+
* additions (B4 interference counter wiring, etc.) land at one site.
|
|
33
|
+
*/
|
|
34
|
+
export function buildSuppressionSummary(counts) {
|
|
35
|
+
return {
|
|
36
|
+
totalCandidates: counts.totalCandidates,
|
|
37
|
+
droppedPreRank: counts.droppedPreRank,
|
|
38
|
+
droppedByBudget: counts.droppedByBudget,
|
|
39
|
+
summarySubstitutionsAdded: counts.summarySubstitutionsAdded,
|
|
40
|
+
freshTailAdded: counts.freshTailAdded,
|
|
41
|
+
suppressedByInterference: counts.suppressedByInterference,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Domain-level recall. Loads BM25-ranked candidates from SQLite scoped to
|
|
46
|
+
* `ctx.tenantId` and keeps that order whatever `mode` says; `retrieve` is the
|
|
47
|
+
* mode-aware, strengthening variant the HTTP route uses.
|
|
48
|
+
*
|
|
49
|
+
* **api.recall does NOT mutate `index.last_retrieval_ids`** (v1.11.5 contract
|
|
50
|
+
* lock). The CLI `cmdRecall` (cli.ts) writes `last_retrieval_ids` because the
|
|
51
|
+
* CLI is interactive (user is about to run `hippo outcome --good`). SDK callers
|
|
52
|
+
* are programmatic: they either pass explicit ids to `api.outcome` or call
|
|
53
|
+
* `api.getContext` first for the context-then-outcome workflow (getContext
|
|
54
|
+
* DOES write `last_retrieval_ids`). Adding the side-effect here would change
|
|
55
|
+
* `api.recall` from a pure read into a read+write, breaking SDK callers who
|
|
56
|
+
* batch recall calls in a row. Locked by
|
|
57
|
+
* `tests/api-recall-no-side-effects.test.ts`.
|
|
58
|
+
*/
|
|
59
|
+
export function recall(ctx, opts) {
|
|
60
|
+
// A member key may not unlock a private or quarantined scope by naming it.
|
|
61
|
+
assertScopeRequestAllowed(ctx.actor, opts.scope);
|
|
62
|
+
const windowSize = recallWindowSize(opts);
|
|
63
|
+
return recallFrom(ctx, opts, windowSize, loadRecallSearchEntries(ctx.hippoRoot, opts.query, windowSize, ctx.tenantId, opts.scope, 'exact', false));
|
|
64
|
+
}
|
|
65
|
+
/** Mode-aware recall that strengthens each returned row; never writes last_retrieval_ids (v1.11.5 lock). */
|
|
66
|
+
export async function retrieve(ctx, opts) {
|
|
67
|
+
assertScopeRequestAllowed(ctx.actor, opts.scope);
|
|
68
|
+
const windowSize = recallWindowSize(opts);
|
|
69
|
+
if (opts.showRanked)
|
|
70
|
+
return retrieveFromStore(ctx, opts, windowSize, opts.showRanked);
|
|
71
|
+
let candidates = loadRecallSearchEntries(ctx.hippoRoot, opts.query, windowSize, ctx.tenantId, opts.scope, 'exact', false);
|
|
72
|
+
if (opts.mode === 'hybrid' || opts.mode === 'physics') {
|
|
73
|
+
const searchOpts = { budget: Infinity, hippoRoot: ctx.hippoRoot, scope: opts.scope ?? null, vectorCandidates: recallVectorSpec(ctx, opts) };
|
|
74
|
+
const ranked = opts.mode === 'physics'
|
|
75
|
+
? await physicsSearch(opts.query, candidates, { ...searchOpts, physicsConfig: loadConfig(ctx.hippoRoot).physics })
|
|
76
|
+
: await hybridSearch(opts.query, candidates, searchOpts);
|
|
77
|
+
const rankedIds = new Set(ranked.map((r) => r.entry.id));
|
|
78
|
+
candidates = [...ranked.map((r) => r.entry), ...candidates.filter((e) => !rankedIds.has(e.id))];
|
|
79
|
+
}
|
|
80
|
+
const result = recallFrom(ctx, opts, windowSize, candidates);
|
|
81
|
+
strengthenRetrieved(ctx.hippoRoot, result.results.map((r) => r.id), { tenantId: ctx.tenantId, recallBoostAblated: isRecallBoostAblated() });
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
/** api.retrieve's vector arm: the recall load's exact-scope rule, current rows only. */
|
|
85
|
+
function recallVectorSpec(ctx, opts) {
|
|
86
|
+
return {
|
|
87
|
+
tenantId: ctx.tenantId,
|
|
88
|
+
scope: recallScopeFilter(opts.scope, 'exact'),
|
|
89
|
+
includeSuperseded: false,
|
|
90
|
+
admit: (e) => passesScopeFilterForRecall(e.scope ?? null, opts.scope),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
// Tag, pin and recency boosts can lift a row from deep in the BM25 order, so MCP ranks a wide lexical window.
|
|
94
|
+
const SHOW_RANKED_LEXICAL_WINDOW = 1000;
|
|
95
|
+
/** `retrieve` under `showRanked`: physics when `mode` says so, hybrid otherwise, over a wide lexical window plus the nearest vectors. */
|
|
96
|
+
async function retrieveFromStore(ctx, opts, windowSize, show) {
|
|
97
|
+
const loaded = loadRecallSearchEntries(ctx.hippoRoot, opts.query, Math.max(windowSize, SHOW_RANKED_LEXICAL_WINDOW), ctx.tenantId, opts.scope, 'exact', false);
|
|
98
|
+
const pool = loaded.filter((e) => passesScopeFilterForRecall(e.scope ?? null, opts.scope));
|
|
99
|
+
// No scope option: the scope boost follows HIPPO_SCOPE and the skill env, as MCP recall always ranked.
|
|
100
|
+
const searchOpts = { budget: Infinity, hippoRoot: ctx.hippoRoot, vectorCandidates: recallVectorSpec(ctx, opts) };
|
|
101
|
+
let ranked = opts.mode === 'physics'
|
|
102
|
+
? await physicsSearch(opts.query, pool, { ...searchOpts, physicsConfig: loadConfig(ctx.hippoRoot).physics })
|
|
103
|
+
: await hybridSearch(opts.query, pool, searchOpts);
|
|
104
|
+
if (opts.sessionId && !opts.goalTag) {
|
|
105
|
+
const db = openHippoDb(ctx.hippoRoot);
|
|
106
|
+
try {
|
|
107
|
+
ranked = applyGoalStackBoost(db, ranked, { sessionId: opts.sessionId, tenantId: ctx.tenantId, limit: ranked.length });
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
closeHippoDb(db);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const window = ranked.slice(0, windowSize).map((r) => r.entry);
|
|
114
|
+
const result = recallFrom(ctx, { ...opts, suppressRecallTrace: true }, windowSize, window);
|
|
115
|
+
// Rows the vector arm added count as candidates too.
|
|
116
|
+
const inPool = new Set(pool.map((e) => e.id));
|
|
117
|
+
const candidates = [...pool, ...ranked.map((r) => r.entry).filter((e) => !inPool.has(e.id))];
|
|
118
|
+
const shown = show({ ranked, pool: candidates, droppedByScope: loaded.length - pool.length }, result);
|
|
119
|
+
strengthenRetrieved(ctx.hippoRoot, shown, { tenantId: ctx.tenantId, recallBoostAblated: isRecallBoostAblated() });
|
|
120
|
+
if (!opts.suppressRecallTrace) {
|
|
121
|
+
const scores = new Map(ranked.map((r) => [r.entry.id, r.score]));
|
|
122
|
+
writeRecallTraceAtRoot(ctx.hippoRoot, {
|
|
123
|
+
tenantId: ctx.tenantId,
|
|
124
|
+
sessionId: opts.sessionId ?? null,
|
|
125
|
+
pipeline: 'mcp',
|
|
126
|
+
query: opts.query,
|
|
127
|
+
results: shown.map((id) => ({ memoryId: id, score: scores.get(id) ?? 0 })),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
/** Contract preflight: throws before any store-touching work. */
|
|
133
|
+
function recallWindowSize(opts) {
|
|
134
|
+
// F5 (v1.6.5) preflight — codex P1: original guard fired AFTER
|
|
135
|
+
// loadSearchEntries (which runs initStore, migrating legacy state on first
|
|
136
|
+
// call). For a true contract preflight we want the throw before any
|
|
137
|
+
// store-touching work. Single check here; the consumer site at
|
|
138
|
+
// `if (freshTailCount > 0)` does NOT re-validate (would be a no-op).
|
|
139
|
+
const freshTailCountPreflight = opts.freshTailCount ?? 0;
|
|
140
|
+
if (freshTailCountPreflight > 0 &&
|
|
141
|
+
!opts.freshTailSessionId &&
|
|
142
|
+
envRequireSessionScopedFreshTail()) {
|
|
143
|
+
throw new RecallContractError('fresh_tail_requires_session_id', 'fresh-tail requires a session id when HIPPO_REQUIRE_SESSION_SCOPED_FRESH_TAIL=1; ' +
|
|
144
|
+
'pass opts.freshTailSessionId or unset the env to allow tenant-wide fresh-tail.');
|
|
145
|
+
}
|
|
146
|
+
// F3 (v1.7.0): scorerWindow opt-in. When undefined (default),
|
|
147
|
+
// loadSearchEntries uses its own store-internal default — this
|
|
148
|
+
// preserves every pre-v1.7.0 caller's behaviour bit-for-bit (codex
|
|
149
|
+
// mk2-pass P0-1: defaulting to `limit` would have shrunk the
|
|
150
|
+
// candidate pool and killed overflow summaries).
|
|
151
|
+
// DEFAULT_SEARCH_CANDIDATE_LIMIT is imported from store.ts so the two
|
|
152
|
+
// values cannot drift (codex diff-pass P1 #3).
|
|
153
|
+
// Validate the input — codex diff-pass P1 #1 caught that scorerWindow=0
|
|
154
|
+
// would route through FTS/LIKE LIMIT 0 and then fall through to an
|
|
155
|
+
// uncapped full-store fallback. Reject non-positive / non-finite values.
|
|
156
|
+
if (opts.scorerWindow !== undefined) {
|
|
157
|
+
if (!Number.isFinite(opts.scorerWindow) ||
|
|
158
|
+
!Number.isInteger(opts.scorerWindow) ||
|
|
159
|
+
opts.scorerWindow < 1) {
|
|
160
|
+
throw new RecallContractError('invalid_scorer_window', `scorerWindow must be a positive integer; got ${opts.scorerWindow}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return opts.scorerWindow ?? DEFAULT_SEARCH_CANDIDATE_LIMIT;
|
|
164
|
+
}
|
|
165
|
+
function recallFrom(ctx, opts, windowSize, all) {
|
|
166
|
+
const limit = opts.limit ?? 10;
|
|
167
|
+
// v1.7.1 — root-cause fix for the `unknown:legacy` leak. Scope predicate
|
|
168
|
+
// is now pushed into `loadSearchRows` SQL via `loadRecallSearchEntries`.
|
|
169
|
+
// - opts.scope undefined / '': SQL excludes `unknown:legacy`.
|
|
170
|
+
// - opts.scope non-empty: SQL exact-matches m.scope = opts.scope.
|
|
171
|
+
// Tenant predicate still runs first, so a tenant-mismatched scope cannot
|
|
172
|
+
// surface another tenant's row even when both share the same scope string.
|
|
173
|
+
//
|
|
174
|
+
// **CALLER CONTRACT:** any future recall-mode loader MUST go through
|
|
175
|
+
// `loadRecallSearchEntries` (or invoke the SQL scope predicate equivalently).
|
|
176
|
+
// Calling `loadSearchEntries` from this code path re-introduces the v1.6.5
|
|
177
|
+
// codex-flagged leak. See `passesScopeFilterForRecall` in this file for
|
|
178
|
+
// the canonical recall-side scope rule (kept in sync with the SQL clause
|
|
179
|
+
// in loadSearchRows).
|
|
180
|
+
//
|
|
181
|
+
// Also fixes a latent code smell: pre-v1.7.1 passed `opts.scorerWindow`
|
|
182
|
+
// (raw, possibly undefined) where `windowSize` was intended.
|
|
183
|
+
// v1.12.13 / C5 — WYSIATI counters. Declared BEFORE the load step so the
|
|
184
|
+
// assignments at the existing filter sites (load / scope-filter / limit-
|
|
185
|
+
// slice / substitution / fresh-tail) are after declaration. The return at
|
|
186
|
+
// end-of-function reads them via buildSuppressionSummary.
|
|
187
|
+
let totalCandidatesCount = 0;
|
|
188
|
+
let droppedPreRankCount = 0;
|
|
189
|
+
let droppedByBudgetCount = 0;
|
|
190
|
+
let summarySubstitutionsCount = 0;
|
|
191
|
+
let freshTailAddedCount = 0;
|
|
192
|
+
// v1.12.13 / C5 — WYSIATI totalCandidates counter (post tenant + SQL scope
|
|
193
|
+
// predicate, pre JS scope filter).
|
|
194
|
+
totalCandidatesCount = all.length;
|
|
195
|
+
const current = all.filter((e) => !e.superseded_by);
|
|
196
|
+
let entries;
|
|
197
|
+
if (opts.scope !== undefined && opts.scope !== '') {
|
|
198
|
+
// SQL already exact-matched in loadRecallSearchEntries; keep the JS
|
|
199
|
+
// filter as defense-in-depth so a future SQL-clause regression cannot
|
|
200
|
+
// silently surface cross-scope rows.
|
|
201
|
+
entries = current.filter((e) => e.scope === opts.scope);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
// SQL already excluded `unknown:legacy` AND (v1.25.0) pre-filtered
|
|
205
|
+
// ':private:' scopes with a conservative LIKE before the candidate
|
|
206
|
+
// window, so private rows can no longer starve admitted rows out of the
|
|
207
|
+
// LIMIT (codex review-stage P2). This JS filter stays as the exact
|
|
208
|
+
// anchored `<source>:private:*` rule (v1.2.1 generalization) and
|
|
209
|
+
// defense-in-depth: connector authors cannot silently surface private
|
|
210
|
+
// rows to no-scope callers even if the SQL clause regresses.
|
|
211
|
+
entries = current.filter((e) => !isRestrictedScope(e.scope ?? null));
|
|
212
|
+
}
|
|
213
|
+
// v1.12.13 / C5 — WYSIATI dropped_pre_rank counter (JS scope filter drops
|
|
214
|
+
// for api.recall; cmdRecall pipeline rolls --outcome/--layer/--as-of/etc.
|
|
215
|
+
// into the same field per the plan's Task 3 mapping table).
|
|
216
|
+
droppedPreRankCount = all.length - entries.length;
|
|
217
|
+
entries = entries
|
|
218
|
+
.map((e, i) => ({ e, s: (1 - i / entries.length) * churnStaleFactor(e) }))
|
|
219
|
+
.sort((a, b) => b.s - a.s)
|
|
220
|
+
.map((r) => r.e);
|
|
221
|
+
// BM25 ordering already comes from loadRecallSearchEntries; cap to `limit`.
|
|
222
|
+
// Score is a placeholder — the physics/hybrid scorers in src/search.ts
|
|
223
|
+
// produce richer breakdowns and will replace this when wired up.
|
|
224
|
+
let baseSlice = entries.slice(0, limit);
|
|
225
|
+
// v1.12.13 / C5 — WYSIATI dropped_by_budget counter (candidates loaded but
|
|
226
|
+
// excluded by the final limit slice).
|
|
227
|
+
droppedByBudgetCount = entries.length - baseSlice.length;
|
|
228
|
+
// v1.7.4 -- single db handle for the goal-stack boost AND the audit-event
|
|
229
|
+
// emit below (codex P1: do not open a second short-lived handle for the
|
|
230
|
+
// appendAuditEvent call). The handle is closed in the matching `finally`
|
|
231
|
+
// immediately above the continuity block.
|
|
232
|
+
const db = openHippoDb(ctx.hippoRoot);
|
|
233
|
+
// v1.7.4 -- declared outside the try so the return statement (which lives
|
|
234
|
+
// outside, after the continuity block) can read the final values.
|
|
235
|
+
let rankedOut = [];
|
|
236
|
+
let tokensOut = 0;
|
|
237
|
+
let totalOut = 0;
|
|
238
|
+
// v1.7.4 -- dlPFC goal-stack boost on the PRIMARY band only. Appendix paths
|
|
239
|
+
// (fresh-tail, summary substitutions) are appended AFTER and keep their
|
|
240
|
+
// semantically-special placement.
|
|
241
|
+
let baseScored = baseSlice.map((entry, idx) => ({
|
|
242
|
+
entry,
|
|
243
|
+
score: Math.max(0, 1 - idx / Math.max(1, limit)),
|
|
244
|
+
}));
|
|
245
|
+
// A7 recall-trace: separate side-channel accumulator, allocated ONLY under
|
|
246
|
+
// explain. applyGoalStackBoost writes goal-boost steps here keyed by entry
|
|
247
|
+
// id; the baseRanked map reads it. When !explain it stays undefined and is
|
|
248
|
+
// never passed → the helper's default-path math is byte-identical.
|
|
249
|
+
const explainTrace = opts.explain ? new Map() : undefined;
|
|
250
|
+
try {
|
|
251
|
+
if (opts.sessionId && !opts.goalTag) {
|
|
252
|
+
baseScored = applyGoalStackBoost(db, baseScored, {
|
|
253
|
+
sessionId: opts.sessionId,
|
|
254
|
+
tenantId: ctx.tenantId,
|
|
255
|
+
limit,
|
|
256
|
+
// trace is optional on applyGoalStackBoost; explicitly passing
|
|
257
|
+
// undefined when !explain is identical to omitting the key.
|
|
258
|
+
trace: explainTrace,
|
|
259
|
+
});
|
|
260
|
+
baseSlice = baseScored.map((r) => r.entry);
|
|
261
|
+
}
|
|
262
|
+
// v1.5.0 DAG-aware substitution (Phase 1, Task 2). When entries overflow the
|
|
263
|
+
// limit and ≥2 of them share a level-2 parent summary, append the parent
|
|
264
|
+
// summary so the user sees a compact pointer to the dropped detail. Capped
|
|
265
|
+
// at ceil(limit * 0.3) substitutions so a runaway DAG can't expand results.
|
|
266
|
+
// Each substituted summary is tenant-scoped via loadEntriesByIds and
|
|
267
|
+
// re-checked against the active scope filter (default-deny on private).
|
|
268
|
+
// Drill-down (Task 3) reverses substitution: caller passes substitutedFor[]
|
|
269
|
+
// ids back through `drillDown` to recover the children.
|
|
270
|
+
const summarizeOverflow = opts.summarizeOverflow ?? true;
|
|
271
|
+
let substituted = [];
|
|
272
|
+
if (summarizeOverflow && entries.length > limit) {
|
|
273
|
+
const overflow = entries.slice(limit);
|
|
274
|
+
const baseIds = new Set(baseSlice.map((e) => e.id));
|
|
275
|
+
const overflowByParent = new Map();
|
|
276
|
+
for (const e of overflow) {
|
|
277
|
+
const parentId = e.dag_parent_id;
|
|
278
|
+
if (!parentId)
|
|
279
|
+
continue;
|
|
280
|
+
if ((e.dag_level ?? 0) > 1)
|
|
281
|
+
continue;
|
|
282
|
+
const list = overflowByParent.get(parentId) ?? [];
|
|
283
|
+
list.push(e);
|
|
284
|
+
overflowByParent.set(parentId, list);
|
|
285
|
+
}
|
|
286
|
+
const eligibleParentIds = Array.from(overflowByParent.keys()).filter((pid) => (overflowByParent.get(pid)?.length ?? 0) >= 2 && !baseIds.has(pid));
|
|
287
|
+
if (eligibleParentIds.length > 0) {
|
|
288
|
+
const parents = loadEntriesByIds(ctx.hippoRoot, eligibleParentIds, ctx.tenantId);
|
|
289
|
+
const eligibleParents = parents.filter((p) => (p.dag_level ?? 0) === 2 && !p.superseded_by && passesScopeFilterForRecall(p.scope ?? null, opts.scope));
|
|
290
|
+
const maxSub = Math.max(1, Math.ceil(limit * 0.3));
|
|
291
|
+
// Order parents by overflow count descending so the most
|
|
292
|
+
// information-dense substitutions come first. Overflow count is the
|
|
293
|
+
// true primary key (unchanged); compareEntryIdentity is only a TAIL
|
|
294
|
+
// for the case two parents overflow the same number of children —
|
|
295
|
+
// without it that tie fell to SQLite scan order / loadEntriesByIds
|
|
296
|
+
// batch order (T2, deterministic tie keys).
|
|
297
|
+
eligibleParents.sort((a, b) => {
|
|
298
|
+
const ac = overflowByParent.get(a.id)?.length ?? 0;
|
|
299
|
+
const bc = overflowByParent.get(b.id)?.length ?? 0;
|
|
300
|
+
return bc !== ac ? bc - ac : compareEntryIdentity(a, b);
|
|
301
|
+
});
|
|
302
|
+
substituted = eligibleParents.slice(0, maxSub).map((p) => ({
|
|
303
|
+
entry: p,
|
|
304
|
+
childIds: (overflowByParent.get(p.id) ?? []).map((e) => e.id),
|
|
305
|
+
}));
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (!opts.keepHeldCopies) {
|
|
309
|
+
const shownIds = new Set(dropHeldCopies([...baseScored.map((r) => r.entry), ...substituted.map((s) => s.entry)], (e) => e).map((e) => e.id));
|
|
310
|
+
droppedPreRankCount += baseScored.filter((r) => !shownIds.has(r.entry.id)).length;
|
|
311
|
+
baseScored = baseScored.filter((r) => shownIds.has(r.entry.id));
|
|
312
|
+
baseSlice = baseScored.map((r) => r.entry);
|
|
313
|
+
substituted = substituted.filter((s) => shownIds.has(s.entry.id));
|
|
314
|
+
}
|
|
315
|
+
// v1.12.13 / C5 — WYSIATI summary_substitutions_added counter.
|
|
316
|
+
summarySubstitutionsCount = substituted.length;
|
|
317
|
+
// v1.7.4 -- baseScored carries the (possibly boosted) per-row scores. When
|
|
318
|
+
// the goal-stack boost did not run, scores are identical to the original
|
|
319
|
+
// positional placeholder; when it did run, scores reflect the boost AND the
|
|
320
|
+
// rows are in the boosted order (helper sort()).
|
|
321
|
+
const baseRanked = baseScored.map((r) => {
|
|
322
|
+
const item = {
|
|
323
|
+
id: r.entry.id,
|
|
324
|
+
content: r.entry.content,
|
|
325
|
+
score: r.score,
|
|
326
|
+
layer: r.entry.layer,
|
|
327
|
+
strength: r.entry.strength,
|
|
328
|
+
};
|
|
329
|
+
// A7 recall-trace: under explain, every api band carries rerankPipeline:'api';
|
|
330
|
+
// only baseRanked passes through the goal-boost helper, so only it can carry
|
|
331
|
+
// a step (and only for rows that actually matched an active goal).
|
|
332
|
+
if (opts.explain) {
|
|
333
|
+
item.rerankPipeline = 'api';
|
|
334
|
+
const step = explainTrace?.get(r.entry.id);
|
|
335
|
+
if (step)
|
|
336
|
+
item.rerankTrace = [step];
|
|
337
|
+
}
|
|
338
|
+
return item;
|
|
339
|
+
});
|
|
340
|
+
// Substituted summaries land at the end with score = 0.5 (mid-rank), so
|
|
341
|
+
// they don't outrank top-N strong matches but stay above lowest-rank
|
|
342
|
+
// leaves on the consumer side. Caller sorts/filters as it sees fit.
|
|
343
|
+
const summaryRanked = substituted.map((s) => {
|
|
344
|
+
const item = {
|
|
345
|
+
id: s.entry.id,
|
|
346
|
+
content: s.entry.content,
|
|
347
|
+
score: 0.5,
|
|
348
|
+
layer: s.entry.layer,
|
|
349
|
+
strength: s.entry.strength,
|
|
350
|
+
isSummary: true,
|
|
351
|
+
substitutedFor: s.childIds,
|
|
352
|
+
descendantCount: s.entry.descendant_count ?? s.childIds.length,
|
|
353
|
+
};
|
|
354
|
+
// A7 recall-trace: summary band runs no re-ranking, but under explain it
|
|
355
|
+
// still carries the pipeline marker (no steps). Absent when !explain.
|
|
356
|
+
if (opts.explain)
|
|
357
|
+
item.rerankPipeline = 'api';
|
|
358
|
+
return item;
|
|
359
|
+
});
|
|
360
|
+
// v1.5.2 fresh-tail. Surface the last N kind='raw' rows so an agent's
|
|
361
|
+
// "what did I just see" recall path always covers the recent window even
|
|
362
|
+
// when the query terms don't match. Tenant + scope filtered.
|
|
363
|
+
//
|
|
364
|
+
// Dual-membership semantics: `loadSearchEntries` returns all tenant-scoped
|
|
365
|
+
// rows scored by BM25 (even rows with no token overlap can surface at
|
|
366
|
+
// score≈0), so a row in the recent window often ALSO appears as a BM25
|
|
367
|
+
// hit. We don't duplicate. Instead:
|
|
368
|
+
// 1. Mark any baseRanked entry that's in the recent set with isFreshTail.
|
|
369
|
+
// 2. Prepend genuinely-new recent rows (not in BM25 hits or summaries).
|
|
370
|
+
// Net: every recent row carries `isFreshTail=true`, exactly once.
|
|
371
|
+
const freshTailCount = opts.freshTailCount ?? 0;
|
|
372
|
+
const freshRanked = [];
|
|
373
|
+
if (freshTailCount > 0) {
|
|
374
|
+
// F5 contract guard fires at recall() preflight (top of function).
|
|
375
|
+
// No re-check needed here — by the time we reach this block the
|
|
376
|
+
// env/session policy has already been validated.
|
|
377
|
+
const recent = loadFreshRawMemories(ctx.hippoRoot, freshTailCount, ctx.tenantId, opts.freshTailSessionId);
|
|
378
|
+
const recentScoped = recent.filter((m) => passesScopeFilterForRecall(m.scope ?? null, opts.scope));
|
|
379
|
+
const recentIdSet = new Set(recentScoped.map((m) => m.id));
|
|
380
|
+
for (const r of baseRanked) {
|
|
381
|
+
if (recentIdSet.has(r.id))
|
|
382
|
+
r.isFreshTail = true;
|
|
383
|
+
}
|
|
384
|
+
const seenIds = new Set([
|
|
385
|
+
...baseRanked.map((r) => r.id),
|
|
386
|
+
...summaryRanked.map((r) => r.id),
|
|
387
|
+
]);
|
|
388
|
+
const shownKeys = storedTextKeys(opts.keepHeldCopies ? [] : [...baseSlice, ...substituted.map((s) => s.entry)]);
|
|
389
|
+
for (const m of recentScoped) {
|
|
390
|
+
if (seenIds.has(m.id) || shownKeys.has(duplicateKey(m.content)))
|
|
391
|
+
continue;
|
|
392
|
+
shownKeys.add(duplicateKey(m.content));
|
|
393
|
+
const item = {
|
|
394
|
+
id: m.id,
|
|
395
|
+
content: m.content,
|
|
396
|
+
score: 1.0,
|
|
397
|
+
layer: m.layer,
|
|
398
|
+
strength: m.strength,
|
|
399
|
+
isFreshTail: true,
|
|
400
|
+
};
|
|
401
|
+
// A7 recall-trace: fresh-tail band runs no re-ranking; under explain
|
|
402
|
+
// it carries the pipeline marker (no steps). Absent when !explain.
|
|
403
|
+
if (opts.explain)
|
|
404
|
+
item.rerankPipeline = 'api';
|
|
405
|
+
freshRanked.push(item);
|
|
406
|
+
seenIds.add(m.id);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
// v1.12.13 / C5 — WYSIATI fresh_tail_added counter. Captures the new rows
|
|
410
|
+
// prepended (NOT rows already in baseRanked that got tagged isFreshTail).
|
|
411
|
+
freshTailAddedCount = freshRanked.length;
|
|
412
|
+
rankedOut = [...freshRanked, ...baseRanked, ...summaryRanked];
|
|
413
|
+
tokensOut = rankedOut.reduce((acc, r) => acc + estimateTokens(r.content), 0);
|
|
414
|
+
totalOut = entries.length;
|
|
415
|
+
// TODO(a1-task-4): emit via the shared audit hook in store.ts so we don't
|
|
416
|
+
// double-emit. Recall does not currently write through writeEntry, so no
|
|
417
|
+
// duplicate exists today, but we keep the same shape for symmetry.
|
|
418
|
+
// v1.7.4: reuse the `db` handle opened above for the goal-stack boost --
|
|
419
|
+
// single open/close spans both side effects.
|
|
420
|
+
// GDPR Path A: store a sha256 hash (16 hex chars) of the query text
|
|
421
|
+
// instead of the truncated query itself. If a caller queries with content
|
|
422
|
+
// that matches an archived (RTBF) memory, the original text must not
|
|
423
|
+
// persist in audit_log. query_length is preserved for debugging
|
|
424
|
+
// long-prompt patterns and compliance metrics.
|
|
425
|
+
appendAuditEvent(db, {
|
|
426
|
+
tenantId: ctx.tenantId,
|
|
427
|
+
actor: ctx.actor.subject,
|
|
428
|
+
op: 'recall',
|
|
429
|
+
metadata: {
|
|
430
|
+
...auditQueryFields(opts.query),
|
|
431
|
+
results: rankedOut.length,
|
|
432
|
+
},
|
|
433
|
+
});
|
|
434
|
+
// LC1 (docs/plans/2026-08-02-lc1-recall-trace-persistence.md): trace the
|
|
435
|
+
// returned ids+ranks+scores next to the audit emit, on the SAME open
|
|
436
|
+
// handle. v1.11.5 contract lock holds — api.recall does NOT write
|
|
437
|
+
// last_trace_id (tests/api-recall-no-side-effects.test.ts); a trace INSERT
|
|
438
|
+
// is the same observability class as the audit row it sits beside, not
|
|
439
|
+
// retrieval state. F2 fix: suppressed when the caller traces its own,
|
|
440
|
+
// different result set (retrieve under showRanked traces the shown list as
|
|
441
|
+
// 'mcp'). Fail-soft internally; never throws.
|
|
442
|
+
if (!opts.suppressRecallTrace) {
|
|
443
|
+
writeRecallTrace(db, {
|
|
444
|
+
tenantId: ctx.tenantId,
|
|
445
|
+
sessionId: opts.sessionId ?? null,
|
|
446
|
+
pipeline: 'api',
|
|
447
|
+
query: opts.query,
|
|
448
|
+
explainMode: opts.explain === true,
|
|
449
|
+
results: rankedOut.map((r) => ({
|
|
450
|
+
memoryId: r.id,
|
|
451
|
+
score: r.score,
|
|
452
|
+
rerankSteps: r.rerankTrace,
|
|
453
|
+
})),
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
finally {
|
|
458
|
+
closeHippoDb(db);
|
|
459
|
+
}
|
|
460
|
+
let continuity;
|
|
461
|
+
let continuityTokens;
|
|
462
|
+
if (opts.includeContinuity) {
|
|
463
|
+
const snapshot = loadActiveTaskSnapshot(ctx.hippoRoot, ctx.tenantId);
|
|
464
|
+
// No active snapshot = no anchor = no handoff/events. Avoids resurrecting
|
|
465
|
+
// a stale handoff from a deleted/completed session.
|
|
466
|
+
const sessionId = snapshot?.session_id ?? undefined;
|
|
467
|
+
const sessionHandoff = sessionId
|
|
468
|
+
? loadLatestHandoff(ctx.hippoRoot, ctx.tenantId, sessionId)
|
|
469
|
+
: null;
|
|
470
|
+
const recentSessionEvents = sessionId
|
|
471
|
+
? listSessionEvents(ctx.hippoRoot, ctx.tenantId, { session_id: sessionId, limit: 5 })
|
|
472
|
+
: [];
|
|
473
|
+
// Scope filtering on continuity. Mirrors the memory-recall path:
|
|
474
|
+
// - opts.scope set: EXACT match required (no cross-scope leakage)
|
|
475
|
+
// - opts.scope unset: default-deny on ANY `<source>:private:*` AND on
|
|
476
|
+
// legacy 'unknown:legacy' rows quarantined by the v23 migration.
|
|
477
|
+
// Public and null scopes pass through.
|
|
478
|
+
// v1.1.0 wrongly wrote this as `opts.scope || isPublic`, which allowed
|
|
479
|
+
// ANY explicit scope to see ALL continuity rows. v1.2 closed the latent
|
|
480
|
+
// leak. v1.2.1 generalizes the private check from slack-only to any
|
|
481
|
+
// source so v1.3 GitHub (and future Jira/Linear/etc.) cannot leak.
|
|
482
|
+
const rowScope = (r) => r?.scope ?? null;
|
|
483
|
+
// v1.2: TaskSnapshot / SessionHandoff / SessionEvent now carry scope; the
|
|
484
|
+
// wrapper just normalizes null vs undefined. W1: was its own copy of
|
|
485
|
+
// passesScopeFilterForRecall (cloned 3x); calls the shared helper now.
|
|
486
|
+
const filteredSnapshot = snapshot && passesScopeFilterForRecall(rowScope(snapshot), opts.scope) ? snapshot : null;
|
|
487
|
+
const filteredHandoff = sessionHandoff && passesScopeFilterForRecall(rowScope(sessionHandoff), opts.scope) ? sessionHandoff : null;
|
|
488
|
+
const filteredEvents = recentSessionEvents.filter((e) => passesScopeFilterForRecall(rowScope(e), opts.scope));
|
|
489
|
+
continuity = {
|
|
490
|
+
activeSnapshot: filteredSnapshot,
|
|
491
|
+
sessionHandoff: filteredHandoff,
|
|
492
|
+
recentSessionEvents: filteredEvents,
|
|
493
|
+
};
|
|
494
|
+
const tokenize = (s) => s ? estimateTokens(s) : 0;
|
|
495
|
+
continuityTokens =
|
|
496
|
+
tokenize(filteredSnapshot?.task) +
|
|
497
|
+
tokenize(filteredSnapshot?.summary) +
|
|
498
|
+
tokenize(filteredSnapshot?.next_step) +
|
|
499
|
+
tokenize(filteredHandoff?.summary) +
|
|
500
|
+
tokenize(filteredHandoff?.nextAction) +
|
|
501
|
+
(filteredHandoff?.artifacts ?? []).reduce((acc, a) => acc + tokenize(a), 0) +
|
|
502
|
+
(filteredHandoff?.constraints ?? []).reduce((acc, c) => acc + tokenize(c), 0) +
|
|
503
|
+
tokenize(filteredHandoff?.evidence ? formatHandoffEvidenceLine(filteredHandoff.evidence) : null) +
|
|
504
|
+
tokenize(filteredHandoff?.outcome) +
|
|
505
|
+
tokenize(filteredHandoff?.targetRuntime) +
|
|
506
|
+
tokenize(filteredHandoff?.cardId) +
|
|
507
|
+
filteredEvents.reduce((acc, e) => acc + tokenize(e.content), 0);
|
|
508
|
+
}
|
|
509
|
+
// v0.32 / J3.2 — auto-injection of reference-class baserate when the
|
|
510
|
+
// query carries a forward-prediction phrase AND the closest matching
|
|
511
|
+
// class has closed historical data. Pipeline-invariant (queryText-
|
|
512
|
+
// derived), so MCP and CLI both read this as the single source of
|
|
513
|
+
// truth instead of recomputing (unlike suppressionSummary which IS
|
|
514
|
+
// per-pipeline). opts.actor threads through to the inner
|
|
515
|
+
// computePredictionBaserate call so MCP/HTTP-originated hints attribute
|
|
516
|
+
// correctly instead of defaulting to 'cli'. Disabled by HIPPO_AUTODEBIAS=off.
|
|
517
|
+
// The hint and the no-class-match / tiebreak watching variant are mutually exclusive; both go out as optional fields.
|
|
518
|
+
const planningFallacyOutput = computePlanningFallacyOutput(ctx.hippoRoot, ctx.tenantId, opts.query, { actor: ctx.actor.subject });
|
|
519
|
+
const planningFallacyHint = planningFallacyOutput.hint ?? null;
|
|
520
|
+
const planningFallacyWatching = planningFallacyOutput.watching ?? null;
|
|
521
|
+
// v0.33 / J1 (v1.13.2) — recall-recurrence anchoring detection.
|
|
522
|
+
// Uses opts.recallHistory (caller-supplied snapshot) + this pipeline's
|
|
523
|
+
// own top-1 from rankedOut[0]. PURE read — does NOT mutate the snapshot
|
|
524
|
+
// or any caller-side Map. Disabled by HIPPO_ANCHORING=off (which gates
|
|
525
|
+
// even the detectAnchoring call so disabled tenants pay zero work on
|
|
526
|
+
// this surface). On CLI-routed call paths opts.recallHistory is
|
|
527
|
+
// undefined because cmdRecall computes its own hint separately; the
|
|
528
|
+
// detect call returns null and api.recall's anchoringHint stays absent.
|
|
529
|
+
let anchoringHint = null;
|
|
530
|
+
let suppressedByInterferenceCount = 0;
|
|
531
|
+
if (biasHintEnabled('anchoring') && opts.recallHistory) {
|
|
532
|
+
const queryHash = hashQueryText(opts.query);
|
|
533
|
+
const topMemoryId = rankedOut[0]?.id ?? null;
|
|
534
|
+
anchoringHint = detectAnchoring(opts.recallHistory, queryHash, topMemoryId);
|
|
535
|
+
if (anchoringHint?.reason === 'memory_dominance') {
|
|
536
|
+
suppressedByInterferenceCount = 1;
|
|
537
|
+
// Emit audit op for the memory-dominance detection.
|
|
538
|
+
const db = openHippoDb(ctx.hippoRoot);
|
|
539
|
+
try {
|
|
540
|
+
appendAuditEvent(db, {
|
|
541
|
+
tenantId: ctx.tenantId,
|
|
542
|
+
actor: ctx.actor.subject,
|
|
543
|
+
op: 'recall_anchor_detected_memory_dominance',
|
|
544
|
+
targetId: anchoringHint.memoryId,
|
|
545
|
+
metadata: {
|
|
546
|
+
memory_id: anchoringHint.memoryId,
|
|
547
|
+
query_count: anchoringHint.queryCount ?? null,
|
|
548
|
+
},
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
finally {
|
|
552
|
+
closeHippoDb(db);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
else if (anchoringHint?.reason === 'query_repeat') {
|
|
556
|
+
const db = openHippoDb(ctx.hippoRoot);
|
|
557
|
+
try {
|
|
558
|
+
appendAuditEvent(db, {
|
|
559
|
+
tenantId: ctx.tenantId,
|
|
560
|
+
actor: ctx.actor.subject,
|
|
561
|
+
op: 'recall_anchor_detected_query_repeat',
|
|
562
|
+
targetId: anchoringHint.memoryId,
|
|
563
|
+
metadata: { memory_id: anchoringHint.memoryId },
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
finally {
|
|
567
|
+
closeHippoDb(db);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
// v1.13.x / J2 — availability/recency-bias detection. PURE read: compares
|
|
572
|
+
// the age distribution of the returned top-K (baseSlice, the post-goal-boost
|
|
573
|
+
// slice) against the matched candidate pool it was drawn from (entries, the
|
|
574
|
+
// scope/private-FILTERED candidate set baseSlice is sliced from — NOT `all`,
|
|
575
|
+
// which still holds private/cross-scope rows the caller is not eligible to see
|
|
576
|
+
// and that could never enter the top-K; counting them would leak hidden pool
|
|
577
|
+
// shape and inflate the signal). Soft warning only — does NOT filter, reorder,
|
|
578
|
+
// or suppress. Disabled by HIPPO_AVAILABILITY=off (gates even the detect call
|
|
579
|
+
// so disabled tenants pay zero work). Suppressed via opts.suppressAvailabilityHint
|
|
580
|
+
// when the caller computes its own per-pipeline hint (MCP), mirroring the J1
|
|
581
|
+
// opts.recallHistory gate above so we never double-emit the audit op. Audit
|
|
582
|
+
// emission is pipeline-local, mirroring the J1 block above.
|
|
583
|
+
let availabilityHint = null;
|
|
584
|
+
if (biasHintEnabled('availability') && !opts.suppressAvailabilityHint) {
|
|
585
|
+
availabilityHint = detectAvailabilityBias({
|
|
586
|
+
topK: baseSlice.map((e) => ({ id: e.id, created: e.created })),
|
|
587
|
+
pool: entries.map((e) => ({ id: e.id, created: e.created })),
|
|
588
|
+
});
|
|
589
|
+
if (availabilityHint) {
|
|
590
|
+
const db = openHippoDb(ctx.hippoRoot);
|
|
591
|
+
try {
|
|
592
|
+
appendAuditEvent(db, {
|
|
593
|
+
tenantId: ctx.tenantId,
|
|
594
|
+
actor: ctx.actor.subject,
|
|
595
|
+
op: 'recall_availability_detected',
|
|
596
|
+
metadata: {
|
|
597
|
+
recent_fraction: availabilityHint.recentFraction,
|
|
598
|
+
older_passed_over: availabilityHint.olderCandidatesPassedOver,
|
|
599
|
+
returned_count: availabilityHint.returnedCount,
|
|
600
|
+
},
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
finally {
|
|
604
|
+
closeHippoDb(db);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
const result = {
|
|
609
|
+
results: rankedOut,
|
|
610
|
+
total: totalOut,
|
|
611
|
+
tokens: tokensOut,
|
|
612
|
+
continuity,
|
|
613
|
+
continuityTokens,
|
|
614
|
+
windowSize,
|
|
615
|
+
suppressionSummary: buildSuppressionSummary({
|
|
616
|
+
totalCandidates: totalCandidatesCount,
|
|
617
|
+
droppedPreRank: droppedPreRankCount,
|
|
618
|
+
droppedByBudget: droppedByBudgetCount,
|
|
619
|
+
summarySubstitutionsAdded: summarySubstitutionsCount,
|
|
620
|
+
freshTailAdded: freshTailAddedCount,
|
|
621
|
+
suppressedByInterference: suppressedByInterferenceCount,
|
|
622
|
+
}),
|
|
623
|
+
};
|
|
624
|
+
if (planningFallacyHint)
|
|
625
|
+
result.planningFallacyHint = planningFallacyHint;
|
|
626
|
+
if (planningFallacyWatching)
|
|
627
|
+
result.planningFallacyWatching = planningFallacyWatching;
|
|
628
|
+
if (anchoringHint)
|
|
629
|
+
result.anchoringHint = anchoringHint;
|
|
630
|
+
if (availabilityHint)
|
|
631
|
+
result.availabilityHint = availabilityHint;
|
|
632
|
+
return result;
|
|
633
|
+
}
|
|
634
|
+
//# sourceMappingURL=recall.js.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { DatabaseSyncLike } from '../db.js';
|
|
2
|
+
import { type MemoryKind } from '../memory.js';
|
|
3
|
+
import type { Context } from './types.js';
|
|
4
|
+
export interface RememberOpts {
|
|
5
|
+
content: string;
|
|
6
|
+
kind?: MemoryKind;
|
|
7
|
+
scope?: string;
|
|
8
|
+
owner?: string;
|
|
9
|
+
artifactRef?: string;
|
|
10
|
+
tags?: string[];
|
|
11
|
+
/**
|
|
12
|
+
* Optional hook invoked inside the same transaction as the underlying
|
|
13
|
+
* memories INSERT. Used by ingestion connectors (E1.3+) to stamp
|
|
14
|
+
* idempotency / cursor rows atomically with the memory row, so a crash
|
|
15
|
+
* mid-write cannot produce a memory without its corresponding side-effect
|
|
16
|
+
* log row (or vice versa). If the callback throws, the INSERT is rolled
|
|
17
|
+
* back and the error is rethrown.
|
|
18
|
+
*/
|
|
19
|
+
afterWrite?: (db: DatabaseSyncLike, memoryId: string) => void;
|
|
20
|
+
/** CD5: connector-ingested content an agent doesn't control; gates detectInstruction. CLI/HTTP/MCP never set this. */
|
|
21
|
+
untrusted?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface RememberResult {
|
|
24
|
+
id: string;
|
|
25
|
+
kind: MemoryKind;
|
|
26
|
+
tenantId: string;
|
|
27
|
+
/** Set only when untrusted content was flagged and quarantined instead of stored under its requested scope. */
|
|
28
|
+
quarantined?: {
|
|
29
|
+
reason: string;
|
|
30
|
+
};
|
|
31
|
+
/** Set only when the content held secret material: untrusted text had it redacted, typed text was stored as sent. */
|
|
32
|
+
warnings?: string[];
|
|
33
|
+
}
|
|
34
|
+
export declare function remember(ctx: Context, opts: RememberOpts): RememberResult;
|
|
35
|
+
//# sourceMappingURL=remember.d.ts.map
|