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,88 @@
|
|
|
1
|
+
// Git auto-learn: fix/revert/bug commit subjects become memories. `hippo learn`, init, sleep and MCP hippo_learn share it.
|
|
2
|
+
import { ForbiddenError } from '../api-errors.js';
|
|
3
|
+
import { fetchGitLog, extractLessons, partitionLessons, isGitRepo } from '../autolearn.js';
|
|
4
|
+
import { loadConfig } from '../config.js';
|
|
5
|
+
import { embedMemory } from '../embeddings.js';
|
|
6
|
+
import { extractInvalidationTarget, invalidateMatching } from '../invalidation.js';
|
|
7
|
+
import { computeSchemaFit, createMemory, Layer } from '../memory.js';
|
|
8
|
+
import { extractPathTags } from '../path-context.js';
|
|
9
|
+
import { RejectedValueError } from '../rejection.js';
|
|
10
|
+
import { duplicateKey, longestWord, storedTextKeys } from '../same-text.js';
|
|
11
|
+
import { loadTextsHoldingWords } from '../store/candidates.js';
|
|
12
|
+
import { loadAllEntries } from '../store/entry-reads.js';
|
|
13
|
+
import { writeEntry } from '../store/entry-writes.js';
|
|
14
|
+
import { updateStats } from '../store/index-and-stats.js';
|
|
15
|
+
// SHORTCUT: two profiles keep each surface's rows as they were; fold MCP onto the CLI profile once it may invalidate.
|
|
16
|
+
export const CLI_LEARN = Object.freeze({ tags: Object.freeze(['error', 'git-learned']), source: 'git-learn', full: true });
|
|
17
|
+
export const MCP_LEARN = Object.freeze({ tags: Object.freeze(['git-learned']), source: 'git', full: false });
|
|
18
|
+
/** Learn from `repoPath`'s last `days` of commits into the caller's tenant. Host admin only. */
|
|
19
|
+
export function learn(ctx, opts) {
|
|
20
|
+
// It reads the host's own git history, which belongs to the host, not to the caller's tenant.
|
|
21
|
+
if (!ctx.actor.hostAdmin)
|
|
22
|
+
throw new ForbiddenError('hippo_learn requires a host admin');
|
|
23
|
+
const none = { added: 0, skipped: 0, rejected: 0, lowInfo: 0, invalidations: [] };
|
|
24
|
+
if (!isGitRepo(opts.repoPath))
|
|
25
|
+
return { status: 'not-a-repo', ...none };
|
|
26
|
+
const gitLog = fetchGitLog(opts.repoPath, opts.days);
|
|
27
|
+
if (!gitLog.trim())
|
|
28
|
+
return { status: 'no-commits', ...none };
|
|
29
|
+
const config = loadConfig(ctx.hippoRoot);
|
|
30
|
+
const parsed = extractLessons(gitLog, config.gitLearnPatterns);
|
|
31
|
+
if (parsed.length === 0)
|
|
32
|
+
return { status: 'no-lessons', ...none };
|
|
33
|
+
// The gate filters the loop input, so a dropped lesson neither stores nor invalidates; docs/decisions git-learn-gate-placement says why.
|
|
34
|
+
const { kept, dropped } = partitionLessons(parsed);
|
|
35
|
+
return { status: 'scanned', lowInfo: dropped.length, ...writeLessons(ctx, kept, opts, config) };
|
|
36
|
+
}
|
|
37
|
+
function writeLessons(ctx, lessons, opts, config) {
|
|
38
|
+
const { hippoRoot, tenantId } = ctx;
|
|
39
|
+
const { profile } = opts;
|
|
40
|
+
// Schema fit needs every row; without it, only rows holding a lesson's longest word can be its copy.
|
|
41
|
+
const existing = profile.full ? loadAllEntries(hippoRoot, tenantId) : undefined;
|
|
42
|
+
const keys = storedTextKeys(existing ?? loadTextsHoldingWords(hippoRoot, tenantId, lessons.map(longestWord)));
|
|
43
|
+
const pathTags = profile.full ? extractPathTags(opts.repoPath) : [];
|
|
44
|
+
const counts = { added: 0, skipped: 0, rejected: 0, invalidations: [] };
|
|
45
|
+
for (const lesson of lessons) {
|
|
46
|
+
if (keys.has(duplicateKey(lesson))) {
|
|
47
|
+
counts.skipped++;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const target = profile.full ? extractInvalidationTarget(lesson) : null;
|
|
51
|
+
if (target) {
|
|
52
|
+
const { invalidated } = invalidateMatching(hippoRoot, target, tenantId);
|
|
53
|
+
if (invalidated > 0)
|
|
54
|
+
counts.invalidations.push({ from: target.from, count: invalidated });
|
|
55
|
+
}
|
|
56
|
+
const entry = createMemory(lesson, {
|
|
57
|
+
layer: Layer.Episodic,
|
|
58
|
+
tags: [...profile.tags],
|
|
59
|
+
source: profile.source,
|
|
60
|
+
confidence: 'observed',
|
|
61
|
+
schema_fit: existing && computeSchemaFit(lesson, [...profile.tags], existing),
|
|
62
|
+
tenantId,
|
|
63
|
+
baseHalfLifeDays: config.defaultHalfLifeDays,
|
|
64
|
+
});
|
|
65
|
+
for (const tag of pathTags)
|
|
66
|
+
if (!entry.tags.includes(tag))
|
|
67
|
+
entry.tags.push(tag);
|
|
68
|
+
// A refused lesson must not abort the rest of the git-log scan.
|
|
69
|
+
try {
|
|
70
|
+
writeEntry(hippoRoot, entry, { actor: ctx.actor.subject });
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
if (err instanceof RejectedValueError) {
|
|
74
|
+
counts.rejected++;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
throw err;
|
|
78
|
+
}
|
|
79
|
+
if (profile.full)
|
|
80
|
+
updateStats(hippoRoot, { remembered: 1 });
|
|
81
|
+
keys.add(duplicateKey(lesson));
|
|
82
|
+
if (profile.full)
|
|
83
|
+
void embedMemory(hippoRoot, entry);
|
|
84
|
+
counts.added++;
|
|
85
|
+
}
|
|
86
|
+
return counts;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=learn.js.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Context } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Apply a positive/negative outcome to a list of recently-recalled memory ids.
|
|
4
|
+
* Used by the MCP `hippo_outcome` tool and the HTTP `POST /v1/outcome` route.
|
|
5
|
+
* Tenant-scoped: ids that don't belong to ctx.tenantId are silently skipped
|
|
6
|
+
* (matches the prior MCP semantics — a stale id from another tenant doesn't
|
|
7
|
+
* crash the call). Each successful outcome emits one audit_log row with
|
|
8
|
+
* op='outcome' tagged with ctx.actor.subject.
|
|
9
|
+
*
|
|
10
|
+
* Returns `{applied, appliedIds}`. `appliedIds` is the tenant-filtered subset
|
|
11
|
+
* of input ids that actually had `applyOutcome` run on them (i.e. ids whose
|
|
12
|
+
* `readEntry(..., ctx.tenantId)` resolved). Callers that surface the id list
|
|
13
|
+
* over a multi-tenant boundary (HTTP /v1/outcome last-recall path, Python SDK)
|
|
14
|
+
* MUST return `appliedIds` instead of the raw input list — otherwise the
|
|
15
|
+
* non-applied (cross-tenant) ids leak to the caller. Added in v1.11.4 to
|
|
16
|
+
* close that disclosure path on POST /v1/outcome.
|
|
17
|
+
*
|
|
18
|
+
* `opts.traceId` (LC1, docs/plans/2026-08-02-lc1-recall-trace-persistence.md):
|
|
19
|
+
* OPTIONAL additive opt so a programmatic caller can link this outcome to
|
|
20
|
+
* the recall_traces row it judges. NOT applied unconditionally — an SDK
|
|
21
|
+
* caller passing explicit ids with no preceding CLI/context recall would
|
|
22
|
+
* otherwise get linked to a stale, unrelated trace. `outcomeForLastRecall`
|
|
23
|
+
* supplies this automatically from `last_trace_id`; every other caller
|
|
24
|
+
* (server.ts explicit-ids path, MCP hippo_outcome) omits it and gets no
|
|
25
|
+
* linkage, which is correct.
|
|
26
|
+
*/
|
|
27
|
+
export interface OutcomeResult {
|
|
28
|
+
applied: number;
|
|
29
|
+
appliedIds: string[];
|
|
30
|
+
}
|
|
31
|
+
export declare function outcome(ctx: Context, ids: ReadonlyArray<string>, good: boolean, opts?: {
|
|
32
|
+
traceId?: number;
|
|
33
|
+
}): OutcomeResult;
|
|
34
|
+
/**
|
|
35
|
+
* Apply an outcome to the ids most recently returned by `recall()`.
|
|
36
|
+
*
|
|
37
|
+
* Reads `loadIndex(ctx.hippoRoot).last_retrieval_ids` (per-hippoRoot local
|
|
38
|
+
* state; not tenant-scoped at the index layer) and forwards to `outcome()`,
|
|
39
|
+
* which DOES tenant-filter via `readEntry(..., ctx.tenantId)`. Cross-tenant
|
|
40
|
+
* ids in `last_retrieval_ids` are silently skipped, matching the MCP
|
|
41
|
+
* `hippo_outcome` semantics.
|
|
42
|
+
*
|
|
43
|
+
* **Tenant-safe response shape (v1.11.4 security fix):** the returned `ids`
|
|
44
|
+
* field contains ONLY the tenant-filtered subset that actually had outcomes
|
|
45
|
+
* applied (i.e. `appliedIds` from the inner `outcome()` call). Earlier
|
|
46
|
+
* versions returned the raw `last_retrieval_ids` regardless of tenant, which
|
|
47
|
+
* leaked cross-tenant memory IDs to the caller via POST /v1/outcome's
|
|
48
|
+
* no-body last-recall response. The fix is at this helper so all callers
|
|
49
|
+
* (CLI cmdOutcome, HTTP /v1/outcome, MCP `hippo_outcome` if added later)
|
|
50
|
+
* inherit the tenant-safe contract.
|
|
51
|
+
*
|
|
52
|
+
* Do NOT tighten `loadIndex` with `tenantId` inside this helper — doing so
|
|
53
|
+
* would break the (correct) cross-tenant-silent-skip behavior covered by
|
|
54
|
+
* the test in `tests/api-outcome-for-last-recall.test.ts`.
|
|
55
|
+
*/
|
|
56
|
+
export interface OutcomeForLastRecallResult {
|
|
57
|
+
applied: number;
|
|
58
|
+
ids: string[];
|
|
59
|
+
}
|
|
60
|
+
export declare function outcomeForLastRecall(ctx: Context, good: boolean): OutcomeForLastRecallResult;
|
|
61
|
+
//# sourceMappingURL=outcome.d.ts.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Outcome feedback on recalled memories.
|
|
2
|
+
import { openHippoDb, closeHippoDb } from '../db.js';
|
|
3
|
+
import { writeEntry } from '../store/entry-writes.js';
|
|
4
|
+
import { readEntry } from '../store/entry-reads.js';
|
|
5
|
+
import { loadIndex } from '../store/index-and-stats.js';
|
|
6
|
+
import { applyOutcome, CHURN_STALE_TAG } from '../memory.js';
|
|
7
|
+
import { appendAuditEvent } from '../audit.js';
|
|
8
|
+
import { recordTraceOutcome } from '../recall-trace.js';
|
|
9
|
+
export function outcome(ctx, ids, good, opts) {
|
|
10
|
+
const appliedIds = [];
|
|
11
|
+
const db = openHippoDb(ctx.hippoRoot);
|
|
12
|
+
try {
|
|
13
|
+
for (const id of ids) {
|
|
14
|
+
const entry = readEntry(ctx.hippoRoot, id, ctx.tenantId);
|
|
15
|
+
if (!entry)
|
|
16
|
+
continue;
|
|
17
|
+
let updated = applyOutcome(entry, good);
|
|
18
|
+
if (good && updated.tags.includes(CHURN_STALE_TAG)) { // FE2: a good outcome reconfirms the entry
|
|
19
|
+
updated = { ...updated, tags: updated.tags.filter((t) => t !== CHURN_STALE_TAG) };
|
|
20
|
+
}
|
|
21
|
+
writeEntry(ctx.hippoRoot, updated, { actor: ctx.actor.subject });
|
|
22
|
+
appendAuditEvent(db, {
|
|
23
|
+
tenantId: ctx.tenantId,
|
|
24
|
+
actor: ctx.actor.subject,
|
|
25
|
+
op: 'outcome',
|
|
26
|
+
targetId: id,
|
|
27
|
+
metadata: { good },
|
|
28
|
+
});
|
|
29
|
+
appliedIds.push(id);
|
|
30
|
+
}
|
|
31
|
+
// LC1: link the outcome to its trace, recording only the ids actually
|
|
32
|
+
// credited (post tenant-filtering, matches appliedIds). Lives in its own
|
|
33
|
+
// append-only table so audit_log pruning can never erase training data.
|
|
34
|
+
if (opts?.traceId !== undefined && appliedIds.length > 0) {
|
|
35
|
+
recordTraceOutcome(db, {
|
|
36
|
+
traceId: opts.traceId,
|
|
37
|
+
tenantId: ctx.tenantId,
|
|
38
|
+
outcome: good ? 'positive' : 'negative',
|
|
39
|
+
memoryIds: appliedIds,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
closeHippoDb(db);
|
|
45
|
+
}
|
|
46
|
+
return { applied: appliedIds.length, appliedIds };
|
|
47
|
+
}
|
|
48
|
+
export function outcomeForLastRecall(ctx, good) {
|
|
49
|
+
const idx = loadIndex(ctx.hippoRoot);
|
|
50
|
+
const ids = idx.last_retrieval_ids;
|
|
51
|
+
if (ids.length === 0)
|
|
52
|
+
return { applied: 0, ids: [] };
|
|
53
|
+
// LC1 F1(d) structural fix (docs/plans/2026-08-02-lc1-recall-trace-persistence.md):
|
|
54
|
+
// read the trace id from the SAME `loadIndex` snapshot already in hand
|
|
55
|
+
// (idx.last_trace_id) — a single-snapshot read, not a second DB round
|
|
56
|
+
// trip via a now-deleted readLastTraceId helper. The value is already
|
|
57
|
+
// strict-parsed by buildIndexFromDb's parseLastTraceId (store.ts): every
|
|
58
|
+
// consumer gets a clean positive-integer string or null, never a garbage
|
|
59
|
+
// value that could reach outcome() and INSERT trace_id=0/NaN. null on a
|
|
60
|
+
// fresh store / pre-v40 flow / api.recall-only usage — outcome() skips
|
|
61
|
+
// linkage silently when traceId is undefined.
|
|
62
|
+
const traceId = idx.last_trace_id !== null ? Number(idx.last_trace_id) : null;
|
|
63
|
+
const { applied, appliedIds } = outcome(ctx, ids, good, traceId !== null ? { traceId } : undefined);
|
|
64
|
+
return { applied, ids: appliedIds };
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=outcome.js.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { type DatabaseSyncLike } from '../db.js';
|
|
2
|
+
import type { Context } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Copy a local memory into the global store. Mirrors `cmdPromote` in cli.ts:
|
|
5
|
+
* the `writeEntry` inside `promoteToGlobal` emits a 'remember' on the global
|
|
6
|
+
* db; we add a 'promote' audit event on the global db so the user-facing
|
|
7
|
+
* intent stays distinct from the underlying upsert.
|
|
8
|
+
*
|
|
9
|
+
* Note: `promoteToGlobal` does not currently take a tenantId override — it
|
|
10
|
+
* reads the entry from the local root via `readEntry` (no tenant filter) and
|
|
11
|
+
* preserves the entry's existing tenantId on the global side. Task 4 may
|
|
12
|
+
* tighten this once writeEntry/readEntry thread tenant context.
|
|
13
|
+
*/
|
|
14
|
+
export interface PromoteResult {
|
|
15
|
+
ok: true;
|
|
16
|
+
sourceId: string;
|
|
17
|
+
globalId: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function promote(ctx: Context, id: string): PromoteResult;
|
|
20
|
+
/**
|
|
21
|
+
* Replace an old memory with new content, chaining old.superseded_by = new.id.
|
|
22
|
+
* Mirrors `cmdSupersede` in cli.ts (without flag-driven layer/tag/pin overrides
|
|
23
|
+
* — A1 keeps the API minimal; the CLI handler will continue to handle those
|
|
24
|
+
* flags and pass the resolved values once Task 4 lands).
|
|
25
|
+
*/
|
|
26
|
+
export interface SupersedeResult {
|
|
27
|
+
ok: true;
|
|
28
|
+
oldId: string;
|
|
29
|
+
newId: string;
|
|
30
|
+
}
|
|
31
|
+
export declare function supersede(ctx: Context, oldId: string, newContent: string): SupersedeResult;
|
|
32
|
+
/**
|
|
33
|
+
* Archive a kind='raw' memory: snapshot into raw_archive, mark archived, delete.
|
|
34
|
+
*
|
|
35
|
+
* `archiveRawMemory` audits the operation internally (op='archive_raw') using the
|
|
36
|
+
* row's own tenant_id. We DO NOT emit a second audit event here to avoid double-
|
|
37
|
+
* emitting the archive_raw op (unlike Task 1 remember/forget where the underlying
|
|
38
|
+
* helpers hardcode actor='cli'). Instead we pass `ctx.actor.subject` through as `who`,
|
|
39
|
+
* and raw-archive.ts uses that for the audit row.
|
|
40
|
+
*/
|
|
41
|
+
export interface ArchiveRawOpts {
|
|
42
|
+
/**
|
|
43
|
+
* Connector idempotency hook (v0.39 commit 3). Runs inside the same
|
|
44
|
+
* SAVEPOINT as the archive — throwing rolls the archive back. Used by the
|
|
45
|
+
* Slack deletion connector to mark the deletion event seen atomically.
|
|
46
|
+
*/
|
|
47
|
+
afterArchive?: (db: DatabaseSyncLike, archivedMemoryId: string) => void;
|
|
48
|
+
}
|
|
49
|
+
export interface ArchiveRawResult {
|
|
50
|
+
ok: true;
|
|
51
|
+
archivedAt: string;
|
|
52
|
+
}
|
|
53
|
+
export declare function archiveRaw(ctx: Context, id: string, reason: string, opts?: ArchiveRawOpts): ArchiveRawResult;
|
|
54
|
+
//# sourceMappingURL=promote.d.ts.map
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
// Promote to the global store, supersede with a successor, and archive raw memories.
|
|
2
|
+
import { openHippoDb, closeHippoDb, isSqliteBusy } from '../db.js';
|
|
3
|
+
import { ConflictError, NotFoundError } from '../api-errors.js';
|
|
4
|
+
import { auditRejectionRefusal } from '../store/audit-event.js';
|
|
5
|
+
import { stampOriginProject } from '../store/entry-row.js';
|
|
6
|
+
import { removeEntryMirrors } from '../store/mirrors.js';
|
|
7
|
+
import { writeEntryDbOnly, writeEntryMirrors } from '../store/entry-writes.js';
|
|
8
|
+
import { readEntry } from '../store/entry-reads.js';
|
|
9
|
+
import { updateStatsUnlessBusy } from '../store/index-and-stats.js';
|
|
10
|
+
import { markSummaryDirtyInTx } from '../summary-dirty.js';
|
|
11
|
+
import { RejectedValueError } from '../rejection.js';
|
|
12
|
+
import { log } from '../log.js';
|
|
13
|
+
import { createSuccessor } from '../memory.js';
|
|
14
|
+
import { appendAuditEvent } from '../audit.js';
|
|
15
|
+
import { promoteToGlobal } from '../shared.js';
|
|
16
|
+
import { archiveRawMemory } from '../raw-archive.js';
|
|
17
|
+
import { loadConfig } from '../config.js';
|
|
18
|
+
import { selectMemoryTenant } from '../store/tenant-lookup.js';
|
|
19
|
+
export function promote(ctx, id) {
|
|
20
|
+
// Tenant scope: promoteToGlobal reads the entry from the local root via
|
|
21
|
+
// readEntry without a tenant filter, so a Bearer for tenant A could
|
|
22
|
+
// promote tenant B's row by guessing or leaking the id. Pre-check the
|
|
23
|
+
// row's tenant_id and deny cross-tenant access with the same not-found
|
|
24
|
+
// wording archiveRaw uses (no info leak about whether the id exists in
|
|
25
|
+
// another tenant).
|
|
26
|
+
const ownerDb = openHippoDb(ctx.hippoRoot);
|
|
27
|
+
try {
|
|
28
|
+
if (selectMemoryTenant(ownerDb, id) !== ctx.tenantId) {
|
|
29
|
+
throw new NotFoundError(`memory not found: ${id}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
closeHippoDb(ownerDb);
|
|
34
|
+
}
|
|
35
|
+
// The 'promote' row commits with the global copy, so no write follows the commit and a busy store fails the whole promote.
|
|
36
|
+
const globalEntry = promoteToGlobal(ctx.hippoRoot, id, {
|
|
37
|
+
actor: ctx.actor.subject,
|
|
38
|
+
tenantId: ctx.tenantId,
|
|
39
|
+
afterWrite: (db, globalId) => appendAuditEvent(db, {
|
|
40
|
+
tenantId: ctx.tenantId,
|
|
41
|
+
actor: ctx.actor.subject,
|
|
42
|
+
op: 'promote',
|
|
43
|
+
targetId: globalId,
|
|
44
|
+
metadata: { sourceId: id },
|
|
45
|
+
}),
|
|
46
|
+
});
|
|
47
|
+
return { ok: true, sourceId: id, globalId: globalEntry.id };
|
|
48
|
+
}
|
|
49
|
+
export function supersede(ctx, oldId, newContent) {
|
|
50
|
+
// Read old (tenant-scoped). readEntry filters by tenantId, so a Bearer for
|
|
51
|
+
// tenant A on tenant B's id throws "Memory not found" here without any
|
|
52
|
+
// info leak.
|
|
53
|
+
const old = readEntry(ctx.hippoRoot, oldId, ctx.tenantId);
|
|
54
|
+
if (!old) {
|
|
55
|
+
throw new NotFoundError(`Memory not found: ${oldId}`);
|
|
56
|
+
}
|
|
57
|
+
// Guard: not already superseded. The CAS UPDATE below race-safely closes
|
|
58
|
+
// the window between this read and the write; this check just produces a
|
|
59
|
+
// clearer error in the common single-writer case.
|
|
60
|
+
if (old.superseded_by) {
|
|
61
|
+
throw new ConflictError(`Memory ${oldId} is already superseded by ${old.superseded_by}. Supersede that one instead.`);
|
|
62
|
+
}
|
|
63
|
+
const newEntry = createSuccessor(old, newContent, {
|
|
64
|
+
tenantId: ctx.tenantId,
|
|
65
|
+
baseHalfLifeDays: loadConfig(ctx.hippoRoot).defaultHalfLifeDays,
|
|
66
|
+
});
|
|
67
|
+
// Race-safe transition: open a fresh db handle, BEGIN IMMEDIATE, run all
|
|
68
|
+
// three steps (CAS on old + writeEntryDbOnly(new) + supersede audit row)
|
|
69
|
+
// inside the same transaction. Two concurrent supersedes: exactly one CAS
|
|
70
|
+
// wins (changes=1), the other gets changes=0 and throws CONFLICT. No
|
|
71
|
+
// dangling-pointer window: the new memory's row commits atomically with
|
|
72
|
+
// the old.superseded_by pointer.
|
|
73
|
+
const db = openHippoDb(ctx.hippoRoot);
|
|
74
|
+
try {
|
|
75
|
+
db.exec('BEGIN IMMEDIATE');
|
|
76
|
+
try {
|
|
77
|
+
// 1. CAS update: only succeed if old.superseded_by IS NULL AND the
|
|
78
|
+
// row still belongs to ctx.tenantId. Tenant filter is belt-and-
|
|
79
|
+
// braces with the readEntry above — it costs nothing and closes
|
|
80
|
+
// a hypothetical window where ownership changes between read and
|
|
81
|
+
// update.
|
|
82
|
+
const result = db.prepare(`
|
|
83
|
+
UPDATE memories
|
|
84
|
+
SET superseded_by = ?
|
|
85
|
+
WHERE id = ? AND tenant_id = ? AND superseded_by IS NULL
|
|
86
|
+
`).run(newEntry.id, oldId, ctx.tenantId);
|
|
87
|
+
if ((result.changes ?? 0) === 0) {
|
|
88
|
+
db.exec('ROLLBACK');
|
|
89
|
+
throw new ConflictError(`Memory ${oldId} already superseded by another writer`);
|
|
90
|
+
}
|
|
91
|
+
// v0.30 / E2 — DAG live-coupling: OLD entry just transitioned to
|
|
92
|
+
// superseded. Its parent (if any) needs rebuild. Lands strictly
|
|
93
|
+
// between the rollback guard above and the writeEntryDbOnly(NEW)
|
|
94
|
+
// below so a failed CAS hits throw before this hook. The NEW
|
|
95
|
+
// entry's parent (typically same parent) is auto-marked by the
|
|
96
|
+
// writeEntryDbOnly hook (same parent → idempotent, audits once).
|
|
97
|
+
if (old.dag_parent_id) {
|
|
98
|
+
markSummaryDirtyInTx(db, old.dag_parent_id, ctx.tenantId, ctx.actor.subject);
|
|
99
|
+
}
|
|
100
|
+
// 2. Write new memory inside same tx via writeEntryDbOnly (DB-only
|
|
101
|
+
// path). This emits its OWN 'remember' audit row for the new
|
|
102
|
+
// memory inside the SAVEPOINT — atomic with the row INSERT.
|
|
103
|
+
writeEntryDbOnly(db, stampOriginProject(ctx.hippoRoot, newEntry), { actor: ctx.actor.subject });
|
|
104
|
+
// 3. User-facing 'supersede' audit row inside the same tx so the
|
|
105
|
+
// chain pointer + audit trail commit atomically.
|
|
106
|
+
appendAuditEvent(db, {
|
|
107
|
+
tenantId: ctx.tenantId,
|
|
108
|
+
actor: ctx.actor.subject,
|
|
109
|
+
op: 'supersede',
|
|
110
|
+
targetId: oldId,
|
|
111
|
+
metadata: { newId: newEntry.id },
|
|
112
|
+
});
|
|
113
|
+
db.exec('COMMIT');
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
try {
|
|
117
|
+
db.exec('ROLLBACK');
|
|
118
|
+
}
|
|
119
|
+
catch { /* already rolled back */ }
|
|
120
|
+
// AT1 (plan §3): refusal audit lands post-ROLLBACK, in a fresh
|
|
121
|
+
// implicit transaction the aborted outer one cannot claw back — then
|
|
122
|
+
// rethrow so the caller sees the refusal.
|
|
123
|
+
if (err instanceof RejectedValueError) {
|
|
124
|
+
auditRejectionRefusal(db, err, ctx.actor.subject);
|
|
125
|
+
}
|
|
126
|
+
throw err;
|
|
127
|
+
}
|
|
128
|
+
// Mirrors after COMMIT, while the db handle is still open. Same
|
|
129
|
+
// invariant as the original writeEntry: a mirror failure leaves disk
|
|
130
|
+
// MISSING the markdown for the new memory (rebuildIndex rewrites every
|
|
131
|
+
// markdown mirror from the DB) but DOES NOT desync the DB or
|
|
132
|
+
// roll back the supersede. Logged + swallowed, non-fatal.
|
|
133
|
+
try {
|
|
134
|
+
writeEntryMirrors(ctx.hippoRoot, newEntry);
|
|
135
|
+
}
|
|
136
|
+
catch (mirrorErr) {
|
|
137
|
+
log.error(`supersede: mirror write failed (non-fatal, will self-heal): ${mirrorErr instanceof Error ? mirrorErr.message : String(mirrorErr)}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
finally {
|
|
141
|
+
closeHippoDb(db);
|
|
142
|
+
}
|
|
143
|
+
return { ok: true, oldId, newId: newEntry.id };
|
|
144
|
+
}
|
|
145
|
+
export function archiveRaw(ctx, id, reason, opts = {}) {
|
|
146
|
+
const db = openHippoDb(ctx.hippoRoot);
|
|
147
|
+
let mirrorOk = false;
|
|
148
|
+
try {
|
|
149
|
+
// Tenant scope: archiveRawMemory looks up the row by id alone, so a
|
|
150
|
+
// Bearer for tenant A could archive tenant B's raw row without this
|
|
151
|
+
// pre-check. Deny cross-tenant access with the same not-found message
|
|
152
|
+
// archiveRawMemory itself would throw on a missing row, so we don't
|
|
153
|
+
// leak whether the id exists in another tenant.
|
|
154
|
+
if (selectMemoryTenant(db, id) !== ctx.tenantId) {
|
|
155
|
+
throw new NotFoundError(`memory not found: ${id}`);
|
|
156
|
+
}
|
|
157
|
+
archiveRawMemory(db, id, {
|
|
158
|
+
reason,
|
|
159
|
+
who: ctx.actor.subject,
|
|
160
|
+
afterArchive: opts.afterArchive,
|
|
161
|
+
});
|
|
162
|
+
// archiveRawMemory deletes the memories row but leaves any legacy markdown
|
|
163
|
+
// mirror in <root>/{buffer,episodic,semantic}/<id>.md untouched. If we left
|
|
164
|
+
// the mirror in place, a subsequent initStore() on an empty memories table
|
|
165
|
+
// would silently re-import the row via bootstrapLegacyStore — defeating the
|
|
166
|
+
// archive (and the GDPR right-to-be-forgotten promise on raw rows). Mirror
|
|
167
|
+
// forget() at src/store.ts:1046, which uses the same removeEntryMirrors call.
|
|
168
|
+
// The DB transaction has already committed; if filesystem unlink fails here
|
|
169
|
+
// we log and continue. The mirror reaper in openHippoDb will catch it on
|
|
170
|
+
// next DB open: raw_archive.mirror_cleaned_at stays NULL until every layer
|
|
171
|
+
// mirror for this id is gone, so the reaper genuinely retries.
|
|
172
|
+
try {
|
|
173
|
+
removeEntryMirrors(ctx.hippoRoot, id);
|
|
174
|
+
mirrorOk = true;
|
|
175
|
+
}
|
|
176
|
+
catch (mirrorErr) {
|
|
177
|
+
log.error(`archiveRaw: mirror cleanup failed for ${id} (will retry via reaper on next openHippoDb): ${mirrorErr instanceof Error ? mirrorErr.message : String(mirrorErr)}`);
|
|
178
|
+
}
|
|
179
|
+
if (mirrorOk) {
|
|
180
|
+
// Stamp mirror_cleaned_at now so the next openHippoDb reaper SELECT
|
|
181
|
+
// returns empty for this row. NULL stays untouched on failure -> retry.
|
|
182
|
+
try {
|
|
183
|
+
db.prepare(`UPDATE raw_archive SET mirror_cleaned_at = ? WHERE memory_id = ?`).run(new Date().toISOString(), id);
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
if (!isSqliteBusy(err))
|
|
187
|
+
throw err;
|
|
188
|
+
log.warnThenDebug('archive-mirror-stamp-busy', `archived ${id}; the store was busy, so the mirror reaper will re-check it on the next open`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
finally {
|
|
193
|
+
closeHippoDb(db);
|
|
194
|
+
}
|
|
195
|
+
// Counted here rather than in the CLI: the HTTP archive route calls this too,
|
|
196
|
+
// so a routed archive would otherwise never reach the forgotten counter.
|
|
197
|
+
updateStatsUnlessBusy(ctx.hippoRoot, { forgotten: 1 }, `removed ${id}`);
|
|
198
|
+
// archiveRawMemory does not return the archive_at timestamp it wrote. We
|
|
199
|
+
// emit a fresh ISO timestamp here for the API response. Within a millisecond
|
|
200
|
+
// of the actual write, fine for a server response shape.
|
|
201
|
+
return { ok: true, archivedAt: new Date().toISOString() };
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=promote.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type QuarantineStatus } from '../quarantine.js';
|
|
2
|
+
import type { KeysetPosition } from '../keyset.js';
|
|
3
|
+
import type { Context } from './types.js';
|
|
4
|
+
export interface QuarantineListItem {
|
|
5
|
+
id: string;
|
|
6
|
+
originalScope: string | null;
|
|
7
|
+
reason: string;
|
|
8
|
+
status: QuarantineStatus;
|
|
9
|
+
quarantinedAt: string;
|
|
10
|
+
decidedAt: string | null;
|
|
11
|
+
decidedBy: string | null;
|
|
12
|
+
contentPreview: string;
|
|
13
|
+
}
|
|
14
|
+
/** A tenant's quarantined memories, newest first. Default `status` is 'pending' (the review queue). */
|
|
15
|
+
export declare function quarantineList(ctx: Context, opts?: {
|
|
16
|
+
status?: QuarantineStatus | 'all';
|
|
17
|
+
limit?: number;
|
|
18
|
+
after?: KeysetPosition;
|
|
19
|
+
}): QuarantineListItem[];
|
|
20
|
+
/** Release a quarantined memory to its original scope. Admin only; the scope guard refuses a row moved since (mirrors restoreDormant). */
|
|
21
|
+
export declare function quarantineApprove(ctx: Context, id: string): void;
|
|
22
|
+
/** Keep a quarantined memory hidden for good. Admin only; the raw row is untouched (append-only). */
|
|
23
|
+
export declare function quarantineReject(ctx: Context, id: string): void;
|
|
24
|
+
//# sourceMappingURL=quarantine.d.ts.map
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// Quarantine review: list, approve and reject held memories.
|
|
2
|
+
import { openHippoDb, closeHippoDb } from '../db.js';
|
|
3
|
+
import { ConflictError, ForbiddenError, NotFoundError } from '../api-errors.js';
|
|
4
|
+
import { writeEntryMirrors } from '../store/entry-writes.js';
|
|
5
|
+
import { readEntry } from '../store/entry-reads.js';
|
|
6
|
+
import { quarantineScopeFor, getQuarantineRow, listQuarantineRows, approveQuarantineRow, rejectQuarantineRow, } from '../quarantine.js';
|
|
7
|
+
import { log } from '../log.js';
|
|
8
|
+
import { appendAuditEvent } from '../audit.js';
|
|
9
|
+
const QUARANTINE_PREVIEW_CHARS = 200;
|
|
10
|
+
/** A tenant's quarantined memories, newest first. Default `status` is 'pending' (the review queue). */
|
|
11
|
+
export function quarantineList(ctx, opts = {}) {
|
|
12
|
+
const db = openHippoDb(ctx.hippoRoot);
|
|
13
|
+
try {
|
|
14
|
+
const rows = listQuarantineRows(db, ctx.tenantId, opts.status ?? 'pending', opts.limit, opts.after);
|
|
15
|
+
return rows.map((row) => {
|
|
16
|
+
const entry = readEntry(ctx.hippoRoot, row.memoryId, ctx.tenantId);
|
|
17
|
+
return {
|
|
18
|
+
id: row.memoryId,
|
|
19
|
+
originalScope: row.originalScope,
|
|
20
|
+
reason: row.reason,
|
|
21
|
+
status: row.status,
|
|
22
|
+
quarantinedAt: row.quarantinedAt,
|
|
23
|
+
decidedAt: row.decidedAt,
|
|
24
|
+
decidedBy: row.decidedBy,
|
|
25
|
+
contentPreview: entry ? entry.content.slice(0, QUARANTINE_PREVIEW_CHARS) : '',
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
closeHippoDb(db);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function loadPendingQuarantineRow(db, tenantId, id) {
|
|
34
|
+
const row = getQuarantineRow(db, tenantId, id);
|
|
35
|
+
if (!row)
|
|
36
|
+
throw new NotFoundError(`not quarantined: ${id}`);
|
|
37
|
+
if (row.status !== 'pending')
|
|
38
|
+
throw new ConflictError(`${id} is already ${row.status}`);
|
|
39
|
+
return row;
|
|
40
|
+
}
|
|
41
|
+
/** Release a quarantined memory to its original scope. Admin only; the scope guard refuses a row moved since (mirrors restoreDormant). */
|
|
42
|
+
export function quarantineApprove(ctx, id) {
|
|
43
|
+
if (ctx.actor.role !== 'admin') {
|
|
44
|
+
throw new ForbiddenError('Only an admin key can approve a quarantined memory');
|
|
45
|
+
}
|
|
46
|
+
const db = openHippoDb(ctx.hippoRoot);
|
|
47
|
+
try {
|
|
48
|
+
db.exec('BEGIN IMMEDIATE');
|
|
49
|
+
try {
|
|
50
|
+
const row = loadPendingQuarantineRow(db, ctx.tenantId, id);
|
|
51
|
+
const quarantineScope = quarantineScopeFor(row.originalScope);
|
|
52
|
+
const updated = db
|
|
53
|
+
.prepare(`UPDATE memories SET scope = ? WHERE id = ? AND tenant_id = ? AND scope = ?`)
|
|
54
|
+
.run(row.originalScope, id, ctx.tenantId, quarantineScope);
|
|
55
|
+
if (Number(updated.changes ?? 0) !== 1) {
|
|
56
|
+
throw new ConflictError(`memory ${id} scope changed since quarantine; refusing to approve`);
|
|
57
|
+
}
|
|
58
|
+
approveQuarantineRow(db, ctx.tenantId, id, ctx.actor.subject);
|
|
59
|
+
appendAuditEvent(db, {
|
|
60
|
+
tenantId: ctx.tenantId,
|
|
61
|
+
actor: ctx.actor.subject,
|
|
62
|
+
op: 'quarantine_approve',
|
|
63
|
+
targetId: id,
|
|
64
|
+
metadata: { originalScope: row.originalScope },
|
|
65
|
+
});
|
|
66
|
+
db.exec('COMMIT');
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
try {
|
|
70
|
+
db.exec('ROLLBACK');
|
|
71
|
+
}
|
|
72
|
+
catch { /* already rolled back */ }
|
|
73
|
+
throw err;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
closeHippoDb(db);
|
|
78
|
+
}
|
|
79
|
+
// Post-commit, best-effort: a failed rewrite leaves the mirror showing the quarantine scope (fail-closed).
|
|
80
|
+
try {
|
|
81
|
+
const restored = readEntry(ctx.hippoRoot, id, ctx.tenantId);
|
|
82
|
+
if (restored)
|
|
83
|
+
writeEntryMirrors(ctx.hippoRoot, restored);
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
log.error(`quarantine: mirror rewrite failed for ${id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** Keep a quarantined memory hidden for good. Admin only; the raw row is untouched (append-only). */
|
|
90
|
+
export function quarantineReject(ctx, id) {
|
|
91
|
+
if (ctx.actor.role !== 'admin') {
|
|
92
|
+
throw new ForbiddenError('Only an admin key can reject a quarantined memory');
|
|
93
|
+
}
|
|
94
|
+
const db = openHippoDb(ctx.hippoRoot);
|
|
95
|
+
try {
|
|
96
|
+
db.exec('BEGIN IMMEDIATE');
|
|
97
|
+
try {
|
|
98
|
+
loadPendingQuarantineRow(db, ctx.tenantId, id);
|
|
99
|
+
rejectQuarantineRow(db, ctx.tenantId, id, ctx.actor.subject);
|
|
100
|
+
appendAuditEvent(db, {
|
|
101
|
+
tenantId: ctx.tenantId,
|
|
102
|
+
actor: ctx.actor.subject,
|
|
103
|
+
op: 'quarantine_reject',
|
|
104
|
+
targetId: id,
|
|
105
|
+
metadata: {},
|
|
106
|
+
});
|
|
107
|
+
db.exec('COMMIT');
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
try {
|
|
111
|
+
db.exec('ROLLBACK');
|
|
112
|
+
}
|
|
113
|
+
catch { /* already rolled back */ }
|
|
114
|
+
throw err;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
closeHippoDb(db);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=quarantine.js.map
|