hippo-memory 1.60.0 → 1.62.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 +29 -54
- package/dist/ablation.js +9 -27
- package/dist/agent-memories/apply.d.ts +1 -1
- package/dist/agent-memories/apply.js +4 -1
- package/dist/agent-memories/claude-code.d.ts +3 -1
- package/dist/agent-memories/claude-code.js +54 -10
- 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.d.ts +3 -3
- package/dist/agent-memories/sync.js +22 -9
- package/dist/agent-memories/types.d.ts +0 -2
- 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 +153 -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-select.d.ts +49 -0
- package/dist/api/context-select.js +344 -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 +378 -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 +126 -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 +192 -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 +499 -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 +256 -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 +453 -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 +376 -0
- package/dist/cli/continuity.d.ts +6 -0
- package/dist/cli/continuity.js +457 -0
- package/dist/cli/curate.d.ts +19 -0
- package/dist/cli/curate.js +556 -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 +156 -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 +190 -0
- package/dist/cli/playbooks.d.ts +4 -0
- package/dist/cli/playbooks.js +573 -0
- package/dist/cli/projects.js +9 -6
- package/dist/cli/recall.d.ts +7 -0
- package/dist/cli/recall.js +582 -0
- package/dist/cli/remember.d.ts +5 -0
- package/dist/cli/remember.js +485 -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 +647 -0
- package/dist/cli/setup.d.ts +4 -0
- package/dist/cli/setup.js +397 -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 +409 -0
- package/dist/cli/transfer.d.ts +10 -0
- package/dist/cli/transfer.js +424 -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.d.ts +0 -2
- package/dist/compaction-record.js +7 -5
- 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 +83 -72
- package/dist/dag.js +225 -188
- 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 +102 -83
- package/dist/dedupe.js +3 -2
- package/dist/delivery-recorder.js +4 -1
- package/dist/doctor.js +14 -10
- 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 +117 -86
- 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 +52 -43
- 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 +93 -74
- 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 +94 -76
- package/dist/project-briefs.d.ts +3 -0
- package/dist/project-briefs.js +142 -113
- package/dist/project-identity.js +3 -2
- package/dist/project-merge.d.ts +12 -4
- package/dist/project-merge.js +132 -45
- 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 +12 -1
- package/dist/shared.js +33 -27
- package/dist/skills.d.ts +3 -0
- package/dist/skills.js +88 -70
- 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 +260 -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
package/dist/project-briefs.d.ts
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* Lifecycle: active -> superseded (a newer version replaces it) or active ->
|
|
22
22
|
* closed (retired).
|
|
23
23
|
*/
|
|
24
|
+
import { type KeysetPosition } from './keyset.js';
|
|
24
25
|
export type BriefStatus = 'active' | 'superseded' | 'closed';
|
|
25
26
|
export declare const VALID_BRIEF_STATES: ReadonlySet<BriefStatus>;
|
|
26
27
|
/** Field caps (untrusted at the HTTP/SDK boundary). summary is a body, so a larger
|
|
@@ -72,6 +73,8 @@ export interface ListProjectBriefsOpts {
|
|
|
72
73
|
/** Filter to a single repo. */
|
|
73
74
|
repo?: string;
|
|
74
75
|
limit?: number;
|
|
76
|
+
/** Resume after this row: the position the previous page ended on. */
|
|
77
|
+
after?: KeysetPosition;
|
|
75
78
|
}
|
|
76
79
|
/**
|
|
77
80
|
* Create a project_brief (or a new version that supersedes an existing one). Writes
|
package/dist/project-briefs.js
CHANGED
|
@@ -23,13 +23,14 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import { BadRequestError, ConflictError, NotFoundError } from './api-errors.js';
|
|
25
25
|
import { openHippoDb, closeHippoDb } from './db.js';
|
|
26
|
-
import { writeEntry } from './store.js';
|
|
26
|
+
import { writeEntry } from './store/entry-writes.js';
|
|
27
27
|
import { assertTenantId } from './tenant.js';
|
|
28
28
|
import { RECALL_DEFAULT_DENY_SCOPES } from './recall-scope.js';
|
|
29
|
-
import { markGraphDirty, removeGraphEntitiesForObject } from './graph.js';
|
|
29
|
+
import { markGraphDirty, removeGraphEntitiesForObject } from './graph/write.js';
|
|
30
30
|
import { createMemory, Layer } from './memory.js';
|
|
31
31
|
import { appendAuditEvent } from './audit.js';
|
|
32
32
|
import { objectHalfLifeDays } from './half-life-migration.js';
|
|
33
|
+
import { keysetAfter } from './keyset.js';
|
|
33
34
|
export const VALID_BRIEF_STATES = new Set([
|
|
34
35
|
'active',
|
|
35
36
|
'superseded',
|
|
@@ -102,9 +103,83 @@ const BRIEF_COLS = `
|
|
|
102
103
|
function buildBriefContent(repo, summary) {
|
|
103
104
|
return `${repo}\n\n${summary}`;
|
|
104
105
|
}
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
//
|
|
106
|
+
// Preflight the supersede target BEFORE inserting the new row (so the new
|
|
107
|
+
// autoincrement id can never be its own supersede target); read the
|
|
108
|
+
// predecessor version in the same SELECT for server-derived versioning.
|
|
109
|
+
// Mirrors saveSkill / saveProcess (codex P1 2026-05-28).
|
|
110
|
+
function preflightBriefSupersede(db, tenantId, supersedesId) {
|
|
111
|
+
// SAFETY: SELECT projects exactly status, version; .get() returns that
|
|
112
|
+
// shape for the matching row, or undefined when no brief/tenant pair matches.
|
|
113
|
+
const pred = db.prepare(`SELECT status, version FROM project_briefs WHERE id = ? AND tenant_id = ?`).get(supersedesId, tenantId);
|
|
114
|
+
if (!pred) {
|
|
115
|
+
throw new NotFoundError(`saveProjectBrief: brief ${supersedesId} to supersede not found for tenant ${tenantId}`);
|
|
116
|
+
}
|
|
117
|
+
if (pred.status !== 'active') {
|
|
118
|
+
throw new ConflictError(`saveProjectBrief: brief ${supersedesId} is not active (status='${pred.status}'); only active briefs can be superseded.`);
|
|
119
|
+
}
|
|
120
|
+
return pred.version + 1;
|
|
121
|
+
}
|
|
122
|
+
function insertBriefRow(db, memoryId, w, version) {
|
|
123
|
+
const result = db.prepare(`
|
|
124
|
+
INSERT INTO project_briefs(
|
|
125
|
+
memory_id, tenant_id, repo, summary, version,
|
|
126
|
+
status, superseded_by, superseded_at, change_summary, closed_at, created_at
|
|
127
|
+
) VALUES (?, ?, ?, ?, ?, 'active', NULL, NULL, ?, NULL, ?)
|
|
128
|
+
`).run(memoryId, w.tenantId, w.repo, w.summary, version, w.changeSummary, w.now);
|
|
129
|
+
return Number(result.lastInsertRowid ?? 0);
|
|
130
|
+
}
|
|
131
|
+
function supersedeBriefRow(db, w, supersedesId, briefId, version) {
|
|
132
|
+
const sup = db.prepare(`
|
|
133
|
+
UPDATE project_briefs
|
|
134
|
+
SET status = 'superseded', superseded_by = ?, superseded_at = ?
|
|
135
|
+
WHERE id = ? AND tenant_id = ? AND status = 'active' AND id != ?
|
|
136
|
+
`).run(briefId, w.now, supersedesId, w.tenantId, briefId);
|
|
137
|
+
if (sup.changes === 0) {
|
|
138
|
+
throw new ConflictError(`saveProjectBrief: brief ${supersedesId} could not be superseded (no longer active or self-reference).`);
|
|
139
|
+
}
|
|
140
|
+
appendAuditEvent(db, {
|
|
141
|
+
tenantId: w.tenantId,
|
|
142
|
+
actor: w.actor,
|
|
143
|
+
op: 'project_brief_supersede',
|
|
144
|
+
targetId: String(supersedesId),
|
|
145
|
+
metadata: {
|
|
146
|
+
brief_id: supersedesId,
|
|
147
|
+
superseded_by: briefId,
|
|
148
|
+
new_version: version,
|
|
149
|
+
refreshed: w.isRefresh,
|
|
150
|
+
...w.refreshAuditExtra,
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
/** The afterWrite body: preflight, INSERT, supersede, reload, create audit, all in one SAVEPOINT. */
|
|
155
|
+
function writeBriefRow(db, memoryId, w) {
|
|
156
|
+
const version = w.supersedesId !== undefined ? preflightBriefSupersede(db, w.tenantId, w.supersedesId) : 1;
|
|
157
|
+
const briefId = insertBriefRow(db, memoryId, w, version);
|
|
158
|
+
if (w.supersedesId !== undefined)
|
|
159
|
+
supersedeBriefRow(db, w, w.supersedesId, briefId, version);
|
|
160
|
+
// SAFETY: SELECT ${BRIEF_COLS} projects exactly the ProjectBriefRow
|
|
161
|
+
// columns; .get() returns that row, or undefined only if the
|
|
162
|
+
// just-inserted id can't be found.
|
|
163
|
+
const row = db.prepare(`SELECT ${BRIEF_COLS} FROM project_briefs WHERE id = ?`)
|
|
164
|
+
.get(briefId);
|
|
165
|
+
if (!row)
|
|
166
|
+
throw new Error('saveProjectBrief: failed to reload saved brief row');
|
|
167
|
+
// GDPR-light metadata: ids + flags only, no brief text.
|
|
168
|
+
appendAuditEvent(db, {
|
|
169
|
+
tenantId: w.tenantId,
|
|
170
|
+
actor: w.actor,
|
|
171
|
+
op: 'project_brief_create',
|
|
172
|
+
targetId: String(briefId),
|
|
173
|
+
metadata: {
|
|
174
|
+
brief_id: briefId,
|
|
175
|
+
repo: w.repo,
|
|
176
|
+
version,
|
|
177
|
+
refreshed: w.isRefresh,
|
|
178
|
+
...w.refreshAuditExtra,
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
return row;
|
|
182
|
+
}
|
|
108
183
|
/**
|
|
109
184
|
* Create a project_brief (or a new version that supersedes an existing one). Writes
|
|
110
185
|
* the memory mirror + the project_briefs row atomically inside writeEntry's
|
|
@@ -132,79 +207,22 @@ export function saveProjectBrief(hippoRoot, tenantId, opts, actor = 'cli') {
|
|
|
132
207
|
baseHalfLifeDays: objectHalfLifeDays(hippoRoot),
|
|
133
208
|
tenantId,
|
|
134
209
|
});
|
|
210
|
+
const w = {
|
|
211
|
+
tenantId,
|
|
212
|
+
actor,
|
|
213
|
+
repo,
|
|
214
|
+
summary: opts.summary,
|
|
215
|
+
changeSummary,
|
|
216
|
+
supersedesId: opts.supersedesBriefId,
|
|
217
|
+
isRefresh,
|
|
218
|
+
refreshAuditExtra,
|
|
219
|
+
now,
|
|
220
|
+
};
|
|
135
221
|
let savedRow;
|
|
136
222
|
writeEntry(hippoRoot, mem, {
|
|
137
223
|
actor,
|
|
138
224
|
afterWrite: (db, memoryId) => {
|
|
139
|
-
|
|
140
|
-
// autoincrement id can never be its own supersede target); read the
|
|
141
|
-
// predecessor version in the same SELECT for server-derived versioning.
|
|
142
|
-
// Mirrors saveSkill / saveProcess (codex P1 2026-05-28).
|
|
143
|
-
let version = 1;
|
|
144
|
-
if (opts.supersedesBriefId !== undefined) {
|
|
145
|
-
// SAFETY: SELECT projects exactly status, version; .get() returns that
|
|
146
|
-
// shape for the matching row, or undefined when no brief/tenant pair matches.
|
|
147
|
-
const pred = db.prepare(`SELECT status, version FROM project_briefs WHERE id = ? AND tenant_id = ?`).get(opts.supersedesBriefId, tenantId);
|
|
148
|
-
if (!pred) {
|
|
149
|
-
throw new NotFoundError(`saveProjectBrief: brief ${opts.supersedesBriefId} to supersede not found for tenant ${tenantId}`);
|
|
150
|
-
}
|
|
151
|
-
if (pred.status !== 'active') {
|
|
152
|
-
throw new ConflictError(`saveProjectBrief: brief ${opts.supersedesBriefId} is not active (status='${pred.status}'); only active briefs can be superseded.`);
|
|
153
|
-
}
|
|
154
|
-
version = pred.version + 1;
|
|
155
|
-
}
|
|
156
|
-
const result = db.prepare(`
|
|
157
|
-
INSERT INTO project_briefs(
|
|
158
|
-
memory_id, tenant_id, repo, summary, version,
|
|
159
|
-
status, superseded_by, superseded_at, change_summary, closed_at, created_at
|
|
160
|
-
) VALUES (?, ?, ?, ?, ?, 'active', NULL, NULL, ?, NULL, ?)
|
|
161
|
-
`).run(memoryId, tenantId, repo, opts.summary, version, changeSummary, now);
|
|
162
|
-
const briefId = Number(result.lastInsertRowid ?? 0);
|
|
163
|
-
if (opts.supersedesBriefId !== undefined) {
|
|
164
|
-
const sup = db.prepare(`
|
|
165
|
-
UPDATE project_briefs
|
|
166
|
-
SET status = 'superseded', superseded_by = ?, superseded_at = ?
|
|
167
|
-
WHERE id = ? AND tenant_id = ? AND status = 'active' AND id != ?
|
|
168
|
-
`).run(briefId, now, opts.supersedesBriefId, tenantId, briefId);
|
|
169
|
-
if (sup.changes === 0) {
|
|
170
|
-
throw new ConflictError(`saveProjectBrief: brief ${opts.supersedesBriefId} could not be superseded (no longer active or self-reference).`);
|
|
171
|
-
}
|
|
172
|
-
appendAuditEvent(db, {
|
|
173
|
-
tenantId,
|
|
174
|
-
actor,
|
|
175
|
-
op: 'project_brief_supersede',
|
|
176
|
-
targetId: String(opts.supersedesBriefId),
|
|
177
|
-
metadata: {
|
|
178
|
-
brief_id: opts.supersedesBriefId,
|
|
179
|
-
superseded_by: briefId,
|
|
180
|
-
new_version: version,
|
|
181
|
-
refreshed: isRefresh,
|
|
182
|
-
...refreshAuditExtra,
|
|
183
|
-
},
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
// SAFETY: SELECT ${BRIEF_COLS} projects exactly the ProjectBriefRow
|
|
187
|
-
// columns; .get() returns that row, or undefined only if the
|
|
188
|
-
// just-inserted id can't be found.
|
|
189
|
-
const row = db.prepare(`SELECT ${BRIEF_COLS} FROM project_briefs WHERE id = ?`)
|
|
190
|
-
.get(briefId);
|
|
191
|
-
if (!row)
|
|
192
|
-
throw new Error('saveProjectBrief: failed to reload saved brief row');
|
|
193
|
-
savedRow = row;
|
|
194
|
-
// GDPR-light metadata: ids + flags only, no brief text.
|
|
195
|
-
appendAuditEvent(db, {
|
|
196
|
-
tenantId,
|
|
197
|
-
actor,
|
|
198
|
-
op: 'project_brief_create',
|
|
199
|
-
targetId: String(briefId),
|
|
200
|
-
metadata: {
|
|
201
|
-
brief_id: briefId,
|
|
202
|
-
repo,
|
|
203
|
-
version,
|
|
204
|
-
refreshed: isRefresh,
|
|
205
|
-
...refreshAuditExtra,
|
|
206
|
-
},
|
|
207
|
-
});
|
|
225
|
+
savedRow = writeBriefRow(db, memoryId, w);
|
|
208
226
|
},
|
|
209
227
|
// afterCommit covers refreshBrief (delegates here) + brief new/supersede.
|
|
210
228
|
afterCommit: () => markGraphDirty(hippoRoot, tenantId, mem.id),
|
|
@@ -298,6 +316,7 @@ export function loadProjectBriefById(hippoRoot, tenantId, id) {
|
|
|
298
316
|
export function loadProjectBriefs(hippoRoot, tenantId, opts = {}) {
|
|
299
317
|
assertTenantId('loadProjectBriefs', tenantId);
|
|
300
318
|
const limit = opts.limit ?? 100;
|
|
319
|
+
const after = keysetAfter('created_at', 'id', opts.after);
|
|
301
320
|
if (opts.status && !VALID_BRIEF_STATES.has(opts.status)) {
|
|
302
321
|
throw new BadRequestError(`loadProjectBriefs: status must be one of ${Array.from(VALID_BRIEF_STATES).join('|')}; got ${opts.status}`);
|
|
303
322
|
}
|
|
@@ -313,13 +332,13 @@ export function loadProjectBriefs(hippoRoot, tenantId, opts = {}) {
|
|
|
313
332
|
clauses.push('repo = ?');
|
|
314
333
|
params.push(opts.repo);
|
|
315
334
|
}
|
|
316
|
-
params.push(limit);
|
|
335
|
+
params.push(...after.params, limit);
|
|
317
336
|
// SAFETY: SELECT ${BRIEF_COLS} projects exactly the ProjectBriefRow columns
|
|
318
337
|
// regardless of the dynamic WHERE clause built above; .all() returns rows
|
|
319
338
|
// in that shape.
|
|
320
339
|
const rows = db.prepare(`
|
|
321
340
|
SELECT ${BRIEF_COLS} FROM project_briefs
|
|
322
|
-
WHERE ${clauses.join(' AND ')}
|
|
341
|
+
WHERE ${clauses.join(' AND ')}${after.sql}
|
|
323
342
|
ORDER BY created_at DESC, id DESC
|
|
324
343
|
LIMIT ?
|
|
325
344
|
`).all(...params);
|
|
@@ -356,7 +375,7 @@ export function loadActiveBriefForRepo(hippoRoot, tenantId, repo) {
|
|
|
356
375
|
// ---------------------------------------------------------------------------
|
|
357
376
|
// Refresh assembler (the distinguishing deliverable)
|
|
358
377
|
// ---------------------------------------------------------------------------
|
|
359
|
-
/** Escape LIKE wildcards in operator-supplied text (mirror of store.ts
|
|
378
|
+
/** Escape LIKE wildcards in operator-supplied text (mirror of store/search-rows.ts). */
|
|
360
379
|
function escapeLike(term) {
|
|
361
380
|
return term.replace(/[%_\\]/g, '\\$&');
|
|
362
381
|
}
|
|
@@ -369,34 +388,16 @@ function receiptHeadline(content) {
|
|
|
369
388
|
? `${trimmed.slice(0, MAX_RECEIPT_HEADLINE_LEN)}...`
|
|
370
389
|
: trimmed;
|
|
371
390
|
}
|
|
372
|
-
/**
|
|
373
|
-
|
|
374
|
-
* return it WITH the receipt count (the count feeds refreshBrief's change_summary +
|
|
375
|
-
* audit metadata). NO LLM. Always returns a non-empty, valid summary (a brief
|
|
376
|
-
* `summary` is NOT NULL), including the zero-receipts case.
|
|
377
|
-
*
|
|
378
|
-
* A "receipt" = a tenant memory row carrying the repo's `path:<repo>` tag. The
|
|
379
|
-
* brief's OWN memory mirror (source='project_brief') is excluded so a brief never
|
|
380
|
-
* becomes its own receipt on the next refresh. The match is against the JSON-array
|
|
381
|
-
* serialization (each element is a double-quoted string `"path:hippo"`); the
|
|
382
|
-
* surrounding quotes are load-bearing — they stop `hip` matching `path:hippo`.
|
|
383
|
-
* `repo` is LIKE-escaped + parameterized (operator-supplied; security.md).
|
|
384
|
-
*/
|
|
385
|
-
export function assembleBriefFromReceipts(hippoRoot, tenantId, repo) {
|
|
386
|
-
assertTenantId('assembleBriefFromReceipts', tenantId);
|
|
387
|
-
const normalizedRepo = (repo ?? '').trim();
|
|
388
|
-
if (normalizedRepo.length === 0) {
|
|
389
|
-
throw new BadRequestError('assembleBriefFromReceipts: repo is required');
|
|
390
|
-
}
|
|
391
|
+
/** The repo's receipt rows, newest first, capped at MAX_BRIEF_RECEIPTS. */
|
|
392
|
+
function loadBriefReceipts(hippoRoot, tenantId, normalizedRepo) {
|
|
391
393
|
const tag = `path:${normalizedRepo.toLowerCase()}`;
|
|
392
394
|
const likeParam = `%"${escapeLike(tag)}"%`;
|
|
393
395
|
const denyPlaceholders = RECALL_DEFAULT_DENY_SCOPES.map(() => '?').join(', ');
|
|
394
396
|
const db = openHippoDb(hippoRoot);
|
|
395
|
-
let receipts;
|
|
396
397
|
try {
|
|
397
398
|
// SAFETY: SELECT projects exactly id, created, source, content (the
|
|
398
399
|
// ReceiptRow columns); .all() returns rows in that shape.
|
|
399
|
-
|
|
400
|
+
return db.prepare(`
|
|
400
401
|
SELECT id, created, source, content FROM memories
|
|
401
402
|
WHERE tenant_id = ?
|
|
402
403
|
AND source != 'project_brief'
|
|
@@ -409,17 +410,19 @@ export function assembleBriefFromReceipts(hippoRoot, tenantId, repo) {
|
|
|
409
410
|
finally {
|
|
410
411
|
closeHippoDb(db);
|
|
411
412
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
413
|
+
}
|
|
414
|
+
// NOTE on ordering: the `id DESC` tiebreak is lexical on a random-ish memory id
|
|
415
|
+
// (e.g. `sem_<hex>`), NOT chronological — within the same `created` timestamp the
|
|
416
|
+
// order is stable-but-arbitrary, not insertion order. `created DESC` is the real
|
|
417
|
+
// recency ordering. (plan-eng-critic 2026-05-30, med.)
|
|
418
|
+
//
|
|
419
|
+
// Budget-aware assembly (codex-review-critic 2026-05-30, P2): the digest is the
|
|
420
|
+
// brief `summary`, which saveProjectBrief caps at MAX_BRIEF_SUMMARY_LEN. The
|
|
421
|
+
// receipt/headline caps (50 x ~200) could otherwise build an ~11KB body that the
|
|
422
|
+
// store then REJECTS, breaking refresh for inputs within the advertised caps. So
|
|
423
|
+
// include receipt lines newest-first only while they fit under the cap (reserving
|
|
424
|
+
// slack for the header + an omission footer), and note the omitted remainder.
|
|
425
|
+
function fitReceiptLines(receipts) {
|
|
423
426
|
const buildReceiptLine = (r) => `- ${(r.created ?? '').slice(0, 10)} [${r.source}] ${receiptHeadline(r.content)}`;
|
|
424
427
|
const receiptLines = [];
|
|
425
428
|
if (receipts.length > 0) {
|
|
@@ -435,17 +438,20 @@ export function assembleBriefFromReceipts(hippoRoot, tenantId, repo) {
|
|
|
435
438
|
bodyBudget -= line.length + 1;
|
|
436
439
|
}
|
|
437
440
|
}
|
|
438
|
-
|
|
441
|
+
return receiptLines;
|
|
442
|
+
}
|
|
443
|
+
function renderBriefDigest(normalizedRepo, receiptCount, receiptLines) {
|
|
444
|
+
const omitted = receiptCount - receiptLines.length;
|
|
439
445
|
const lines = [];
|
|
440
446
|
lines.push(`# Project Brief: ${normalizedRepo}`);
|
|
441
447
|
lines.push('');
|
|
442
448
|
lines.push(omitted > 0
|
|
443
|
-
? `_Auto-assembled from ${receiptLines.length} of ${
|
|
444
|
-
: `_Auto-assembled from ${
|
|
449
|
+
? `_Auto-assembled from ${receiptLines.length} of ${receiptCount} receipt(s)._`
|
|
450
|
+
: `_Auto-assembled from ${receiptCount} receipt(s)._`);
|
|
445
451
|
lines.push('');
|
|
446
452
|
lines.push('## Recent receipts');
|
|
447
453
|
lines.push('');
|
|
448
|
-
if (
|
|
454
|
+
if (receiptCount === 0) {
|
|
449
455
|
lines.push(`_No receipts found for ${normalizedRepo}._`);
|
|
450
456
|
}
|
|
451
457
|
else {
|
|
@@ -462,6 +468,29 @@ export function assembleBriefFromReceipts(hippoRoot, tenantId, repo) {
|
|
|
462
468
|
if (markdown.length > MAX_BRIEF_SUMMARY_LEN) {
|
|
463
469
|
markdown = markdown.slice(0, MAX_BRIEF_SUMMARY_LEN);
|
|
464
470
|
}
|
|
471
|
+
return markdown;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Assemble the repo's recent receipts into a deterministic markdown digest, and
|
|
475
|
+
* return it WITH the receipt count (the count feeds refreshBrief's change_summary +
|
|
476
|
+
* audit metadata). NO LLM. Always returns a non-empty, valid summary (a brief
|
|
477
|
+
* `summary` is NOT NULL), including the zero-receipts case.
|
|
478
|
+
*
|
|
479
|
+
* A "receipt" = a tenant memory row carrying the repo's `path:<repo>` tag. The
|
|
480
|
+
* brief's OWN memory mirror (source='project_brief') is excluded so a brief never
|
|
481
|
+
* becomes its own receipt on the next refresh. The match is against the JSON-array
|
|
482
|
+
* serialization (each element is a double-quoted string `"path:hippo"`); the
|
|
483
|
+
* surrounding quotes are load-bearing — they stop `hip` matching `path:hippo`.
|
|
484
|
+
* `repo` is LIKE-escaped + parameterized (operator-supplied; security.md).
|
|
485
|
+
*/
|
|
486
|
+
export function assembleBriefFromReceipts(hippoRoot, tenantId, repo) {
|
|
487
|
+
assertTenantId('assembleBriefFromReceipts', tenantId);
|
|
488
|
+
const normalizedRepo = (repo ?? '').trim();
|
|
489
|
+
if (normalizedRepo.length === 0) {
|
|
490
|
+
throw new BadRequestError('assembleBriefFromReceipts: repo is required');
|
|
491
|
+
}
|
|
492
|
+
const receipts = loadBriefReceipts(hippoRoot, tenantId, normalizedRepo);
|
|
493
|
+
const markdown = renderBriefDigest(normalizedRepo, receipts.length, fitReceiptLines(receipts));
|
|
465
494
|
return { markdown, receiptCount: receipts.length };
|
|
466
495
|
}
|
|
467
496
|
/**
|
package/dist/project-identity.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { envHippoHome, envXdgDataHome } from './env.js';
|
|
1
2
|
import * as fs from 'fs';
|
|
2
3
|
import * as os from 'os';
|
|
3
4
|
import * as path from 'path';
|
|
@@ -170,10 +171,10 @@ export function classifyOriginProject(origin, currentName) {
|
|
|
170
171
|
* shared.getGlobalRoot delegates to this.
|
|
171
172
|
*/
|
|
172
173
|
export function resolveGlobalRootDir() {
|
|
173
|
-
const hippoHome =
|
|
174
|
+
const hippoHome = envHippoHome();
|
|
174
175
|
if (hippoHome)
|
|
175
176
|
return hippoHome;
|
|
176
|
-
const xdgData =
|
|
177
|
+
const xdgData = envXdgDataHome();
|
|
177
178
|
if (xdgData)
|
|
178
179
|
return path.join(xdgData, 'hippo');
|
|
179
180
|
return path.join(os.homedir(), '.hippo');
|
package/dist/project-merge.d.ts
CHANGED
|
@@ -18,7 +18,15 @@ export interface MergeResult {
|
|
|
18
18
|
readonly compactions: number;
|
|
19
19
|
readonly backup: string | null;
|
|
20
20
|
}
|
|
21
|
+
export interface ProjectFold {
|
|
22
|
+
readonly from: string;
|
|
23
|
+
readonly into: string;
|
|
24
|
+
}
|
|
21
25
|
export interface RepairResult {
|
|
26
|
+
/** Imported notes filed under the wrong project, or under a project name when a user-global import holds the same text: set aside. */
|
|
27
|
+
readonly copies: readonly string[];
|
|
28
|
+
/** Names whose recorded session folders all resolve to one other project today, folded as `merge` would. */
|
|
29
|
+
readonly folds: readonly ProjectFold[];
|
|
22
30
|
readonly toProject: ReadonlyArray<{
|
|
23
31
|
readonly id: string;
|
|
24
32
|
readonly origin: string;
|
|
@@ -42,10 +50,10 @@ export declare function mergeProjects(db: DatabaseSyncLike, hippoRoot: string, o
|
|
|
42
50
|
into: string;
|
|
43
51
|
dryRun: boolean;
|
|
44
52
|
}): MergeResult;
|
|
45
|
-
/** Reads only, so doctor
|
|
46
|
-
export declare function
|
|
47
|
-
/**
|
|
48
|
-
export declare function
|
|
53
|
+
/** Reads only, so doctor and a dry run take no write lock; merged rows are planned before any fold, so a few may re-tag differently once folds apply. */
|
|
54
|
+
export declare function planProjectRepair(db: DatabaseSyncLike, hippoRoot: string, tenantId: string): Omit<RepairResult, 'backup'>;
|
|
55
|
+
/** Sets aside stray imports, folds the names the resolver now maps elsewhere, then re-tags sleep's user-global merges by their parents; a dry run only plans. */
|
|
56
|
+
export declare function repairProjects(db: DatabaseSyncLike, hippoRoot: string, opts: {
|
|
49
57
|
tenantId: string;
|
|
50
58
|
dryRun: boolean;
|
|
51
59
|
}): RepairResult;
|
package/dist/project-merge.js
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
|
-
// `hippo projects`: list the project names a store holds, fold one into another, and
|
|
1
|
+
// `hippo projects`: list the project names a store holds, fold one into another, and repair old tags in one reversible pass.
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
3
|
import * as path from 'node:path';
|
|
4
4
|
import { setAsideRow } from './agent-memories/apply.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { transcriptNotesOrigin } from './agent-memories/claude-code.js';
|
|
6
|
+
import { containerId, containerPrefix } from './agent-memories/source.js';
|
|
7
|
+
import { AGENT_MEMORY_SOURCE_PREFIX, AGENT_MEMORY_TOOLS, toolSourcePrefix } from './agent-memories/tools.js';
|
|
8
|
+
import { appendAuditEvent, queryAuditEvents } from './audit.js';
|
|
7
9
|
import { insertDormantRow, listDormantSnapshots, replaceDormantEntry } from './dormant.js';
|
|
10
|
+
import { processEnv } from './env.js';
|
|
8
11
|
import { calculateStrength } from './memory.js';
|
|
9
|
-
import {
|
|
12
|
+
import { deriveOriginProject, isGlobalStoreRoot } from './project-identity.js';
|
|
13
|
+
import { duplicateKey } from './same-text.js';
|
|
14
|
+
import { removeEntryMirrors } from './store/mirrors.js';
|
|
15
|
+
import { deleteEntryRowInTx, writeEntryMirrors } from './store/entry-writes.js';
|
|
16
|
+
import { selectAllEntries, selectLiveEntriesBySourcePrefix } from './store/entry-reads.js';
|
|
10
17
|
const ACTOR = 'cli';
|
|
11
18
|
function isImport(entry) {
|
|
12
19
|
return entry.source.startsWith(AGENT_MEMORY_SOURCE_PREFIX);
|
|
@@ -23,8 +30,9 @@ export function listProjects(db, tenantId) {
|
|
|
23
30
|
for (const e of live) {
|
|
24
31
|
const origin = e.origin_project ?? null;
|
|
25
32
|
byOrigin.set(origin, [...(byOrigin.get(origin) ?? []), e]);
|
|
33
|
+
const key = duplicateKey(e.content);
|
|
26
34
|
if (isImport(e))
|
|
27
|
-
holders.set(
|
|
35
|
+
holders.set(key, (holders.get(key) ?? new Set()).add(origin));
|
|
28
36
|
}
|
|
29
37
|
return [...byOrigin].map(([origin, rows]) => {
|
|
30
38
|
const imports = rows.filter(isImport);
|
|
@@ -33,7 +41,7 @@ export function listProjects(db, tenantId) {
|
|
|
33
41
|
live: rows.length,
|
|
34
42
|
imported: imports.length,
|
|
35
43
|
newest: rows.reduce((max, e) => (e.created > max ? e.created : max), ''),
|
|
36
|
-
copiesElsewhere: imports.filter((e) => (holders.get(e.content)?.size ?? 0) > 1).length,
|
|
44
|
+
copiesElsewhere: imports.filter((e) => (holders.get(duplicateKey(e.content))?.size ?? 0) > 1).length,
|
|
37
45
|
};
|
|
38
46
|
}).sort((a, b) => b.newest.localeCompare(a.newest));
|
|
39
47
|
}
|
|
@@ -76,44 +84,111 @@ export function mergeProjects(db, hippoRoot, opts) {
|
|
|
76
84
|
const { tenantId, from, into, dryRun } = opts;
|
|
77
85
|
const backup = dryRun ? null : backupStore(db, hippoRoot, 'before-merge');
|
|
78
86
|
const result = inTransaction(db, dryRun, () => {
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const tag = toolTag(row.source);
|
|
83
|
-
if (!isImport(row) || row.superseded_by || row.kind === 'raw' || tag === null)
|
|
84
|
-
continue;
|
|
85
|
-
if (setAsideRow(db, tag, { ...row, origin_project: into }, 'project-merge').kind === 'dormant')
|
|
86
|
-
setAside.push(row.id);
|
|
87
|
-
}
|
|
88
|
-
const gone = new Set(setAside);
|
|
89
|
-
const restamped = rows.filter((e) => !gone.has(e.id)).map((e) => e.id);
|
|
90
|
-
db.prepare(`UPDATE memories SET origin_project = ?, updated_at = datetime('now') WHERE tenant_id = ? AND origin_project = ?`)
|
|
91
|
-
.run(into, tenantId, from);
|
|
92
|
-
const dormantRestamped = [];
|
|
93
|
-
for (const snap of listDormantSnapshots(db, tenantId)) {
|
|
94
|
-
if (snap.entry.origin_project !== from || gone.has(snap.entry.id))
|
|
95
|
-
continue;
|
|
96
|
-
replaceDormantEntry(db, tenantId, snap.entry.id, { ...snap.entry, origin_project: into });
|
|
97
|
-
dormantRestamped.push(snap.entry.id);
|
|
98
|
-
}
|
|
99
|
-
const compactions = Number(db.prepare(`UPDATE compactions SET origin_project = ? WHERE tenant_id = ? AND origin_project = ?`)
|
|
100
|
-
.run(into, tenantId, from).changes ?? 0);
|
|
101
|
-
appendAuditEvent(db, {
|
|
102
|
-
tenantId, actor: ACTOR, op: 'project_merge',
|
|
103
|
-
metadata: { from, into, backup, setAside, restamped, dormantRestamped, compactions },
|
|
104
|
-
});
|
|
105
|
-
return { from, into, setAside, restamped, dormantRestamped, compactions, backup };
|
|
87
|
+
const folded = foldInTx(db, tenantId, from, into);
|
|
88
|
+
appendAuditEvent(db, { tenantId, actor: ACTOR, op: 'project_merge', metadata: { from, into, backup, ...folded } });
|
|
89
|
+
return { from, into, ...folded, backup };
|
|
106
90
|
});
|
|
107
91
|
if (!dryRun)
|
|
108
92
|
refreshMirrors(db, hippoRoot, tenantId, result.restamped, result.setAside);
|
|
109
93
|
return result;
|
|
110
94
|
}
|
|
111
|
-
|
|
112
|
-
|
|
95
|
+
function foldInTx(db, tenantId, from, into) {
|
|
96
|
+
const rows = selectAllEntries(db, tenantId).filter((e) => e.origin_project === from);
|
|
97
|
+
const setAside = [];
|
|
98
|
+
for (const row of rows) {
|
|
99
|
+
const tag = toolTag(row.source);
|
|
100
|
+
if (!isImport(row) || row.superseded_by || row.kind === 'raw' || tag === null)
|
|
101
|
+
continue;
|
|
102
|
+
if (setAsideRow(db, tag, { ...row, origin_project: into }, 'project-merge').kind === 'dormant')
|
|
103
|
+
setAside.push(row.id);
|
|
104
|
+
}
|
|
105
|
+
const gone = new Set(setAside);
|
|
106
|
+
const restamped = rows.filter((e) => !gone.has(e.id)).map((e) => e.id);
|
|
107
|
+
db.prepare(`UPDATE memories SET origin_project = ?, updated_at = datetime('now') WHERE tenant_id = ? AND origin_project = ?`)
|
|
108
|
+
.run(into, tenantId, from);
|
|
109
|
+
const dormantRestamped = [];
|
|
110
|
+
for (const snap of listDormantSnapshots(db, tenantId)) {
|
|
111
|
+
if (snap.entry.origin_project !== from || gone.has(snap.entry.id))
|
|
112
|
+
continue;
|
|
113
|
+
replaceDormantEntry(db, tenantId, snap.entry.id, { ...snap.entry, origin_project: into });
|
|
114
|
+
dormantRestamped.push(snap.entry.id);
|
|
115
|
+
}
|
|
116
|
+
const compactions = Number(db.prepare(`UPDATE compactions SET origin_project = ? WHERE tenant_id = ? AND origin_project = ?`)
|
|
117
|
+
.run(into, tenantId, from).changes ?? 0);
|
|
118
|
+
return { setAside, restamped, dormantRestamped, compactions };
|
|
119
|
+
}
|
|
120
|
+
/** Live imports under a project name whose exact text a user-global import holds: a session folder's notes stamped with the folder it ended in. */
|
|
121
|
+
function importCopies(db, tenantId) {
|
|
122
|
+
const live = selectAllEntries(db, tenantId).filter((e) => !e.superseded_by && isImport(e));
|
|
123
|
+
const userGlobal = new Set(live.filter((e) => e.origin_project === '').map((e) => duplicateKey(e.content)));
|
|
124
|
+
return live.filter((e) => e.origin_project && e.kind !== 'raw' && !e.pinned && toolTag(e.source) !== null && userGlobal.has(duplicateKey(e.content)));
|
|
125
|
+
}
|
|
126
|
+
/** Imports to set aside. The global store also checks each Claude session folder a compaction recorded: its notes under any other project are misfiled, edited or not, and a text copy in the right folder is kept. */
|
|
127
|
+
function strayImports(db, hippoRoot, tenantId) {
|
|
128
|
+
const copies = importCopies(db, tenantId);
|
|
129
|
+
if (!isGlobalStoreRoot(hippoRoot))
|
|
130
|
+
return copies;
|
|
131
|
+
const machine = { platform: process.platform, env: processEnv() };
|
|
132
|
+
const { platform } = machine;
|
|
133
|
+
// SAFETY: the SELECT names the two columns of the row type.
|
|
134
|
+
const sessions = db.prepare(`SELECT DISTINCT transcript_path AS transcript, cwd FROM compactions WHERE tenant_id = ? AND transcript_path IS NOT NULL`)
|
|
135
|
+
.all(tenantId);
|
|
136
|
+
const owners = new Map();
|
|
137
|
+
for (const { transcript, cwd } of sessions) {
|
|
138
|
+
const origin = transcriptNotesOrigin(transcript, cwd, machine);
|
|
139
|
+
const dir = path.join(path.dirname(transcript), 'memory');
|
|
140
|
+
if (origin !== null)
|
|
141
|
+
owners.set(dir, (owners.get(dir) ?? new Set()).add(origin));
|
|
142
|
+
}
|
|
143
|
+
const tool = toolSourcePrefix('claude-code');
|
|
144
|
+
const live = selectLiveEntriesBySourcePrefix(db, tenantId, tool).filter((e) => e.kind !== 'raw' && !e.pinned);
|
|
145
|
+
const origins = new Set(live.map((e) => e.origin_project ?? ''));
|
|
146
|
+
const right = new Set();
|
|
147
|
+
const wrong = new Set();
|
|
148
|
+
for (const [dir, owner] of owners) {
|
|
149
|
+
if (owner.size !== 1)
|
|
150
|
+
continue;
|
|
151
|
+
for (const origin of origins)
|
|
152
|
+
(owner.has(origin) ? right : wrong).add(containerPrefix('claude-code', containerId(dir, 'project', platform, origin)));
|
|
153
|
+
}
|
|
154
|
+
const prefix = (e) => e.source.slice(0, e.source.indexOf('/', tool.length) + 1);
|
|
155
|
+
const misfiled = live.filter((e) => wrong.has(prefix(e)));
|
|
156
|
+
const seen = new Set(misfiled.map((e) => e.id));
|
|
157
|
+
return [...misfiled, ...copies.filter((e) => !seen.has(e.id) && !right.has(prefix(e)))];
|
|
158
|
+
}
|
|
159
|
+
/** Global store only, where a compaction's name came from its cwd: a name whose every cwd still on disk resolves to one other project today. */
|
|
160
|
+
function planFolds(db, tenantId) {
|
|
161
|
+
// SAFETY: the SELECT names the two columns of the row type.
|
|
162
|
+
const rows = db.prepare(`SELECT DISTINCT origin_project AS origin, cwd FROM compactions WHERE tenant_id = ? AND origin_project <> '' AND cwd IS NOT NULL`)
|
|
163
|
+
.all(tenantId);
|
|
164
|
+
const today = new Map();
|
|
165
|
+
for (const { origin, cwd } of rows) {
|
|
166
|
+
if (fs.existsSync(cwd))
|
|
167
|
+
today.set(origin, (today.get(origin) ?? new Set()).add(deriveOriginProject(cwd)));
|
|
168
|
+
}
|
|
169
|
+
// A name someone merged into by hand stays: undoing their choice would rest on the resolver alone.
|
|
170
|
+
const chosen = new Set(queryAuditEvents(db, { tenantId, op: 'project_merge', limit: 10000 }).map((e) => e.metadata.into));
|
|
171
|
+
const folds = [...today].flatMap(([from, names]) => {
|
|
172
|
+
const [into] = names;
|
|
173
|
+
return names.size === 1 && into !== '' && into !== from && !chosen.has(from) ? [{ from, into }] : [];
|
|
174
|
+
});
|
|
175
|
+
// A fold into a name that itself folds would land rows by run order; the next repair takes the rest of the chain.
|
|
176
|
+
const sources = new Set(folds.map((f) => f.from));
|
|
177
|
+
return folds.filter((f) => !sources.has(f.into));
|
|
178
|
+
}
|
|
179
|
+
/** Reads only, so doctor and a dry run take no write lock; merged rows are planned before any fold, so a few may re-tag differently once folds apply. */
|
|
180
|
+
export function planProjectRepair(db, hippoRoot, tenantId) {
|
|
181
|
+
const folds = isGlobalStoreRoot(hippoRoot) ? planFolds(db, tenantId) : [];
|
|
182
|
+
return { copies: strayImports(db, hippoRoot, tenantId).map((e) => e.id), folds, ...planUserGlobalRepair(db, tenantId, folds) };
|
|
183
|
+
}
|
|
184
|
+
/** Parents read with `folds` already applied, so a plan matches what apply does after folding. */
|
|
185
|
+
function planUserGlobalRepair(db, tenantId, folds) {
|
|
113
186
|
const all = selectAllEntries(db, tenantId);
|
|
114
|
-
const
|
|
187
|
+
const renamed = new Map(folds.map((f) => [f.from, f.into]));
|
|
188
|
+
const after = (origin) => (origin ? renamed.get(origin) ?? origin : origin ?? null);
|
|
189
|
+
const origins = new Map(listDormantSnapshots(db, tenantId).map((s) => [s.entry.id, after(s.entry.origin_project)]));
|
|
115
190
|
for (const e of all)
|
|
116
|
-
origins.set(e.id, e.origin_project
|
|
191
|
+
origins.set(e.id, after(e.origin_project));
|
|
117
192
|
const toProject = [];
|
|
118
193
|
const setAside = [];
|
|
119
194
|
const untraced = [];
|
|
@@ -133,14 +208,22 @@ export function planUserGlobalRepair(db, tenantId) {
|
|
|
133
208
|
}
|
|
134
209
|
return { toProject, setAside, untraced };
|
|
135
210
|
}
|
|
136
|
-
/**
|
|
137
|
-
export function
|
|
211
|
+
/** Sets aside stray imports, folds the names the resolver now maps elsewhere, then re-tags sleep's user-global merges by their parents; a dry run only plans. */
|
|
212
|
+
export function repairProjects(db, hippoRoot, opts) {
|
|
138
213
|
const { tenantId, dryRun } = opts;
|
|
139
214
|
if (dryRun)
|
|
140
|
-
return { ...
|
|
215
|
+
return { ...planProjectRepair(db, hippoRoot, tenantId), backup: null };
|
|
141
216
|
const backup = backupStore(db, hippoRoot, 'before-repair');
|
|
142
|
-
const result = inTransaction(db, false, () => {
|
|
143
|
-
const
|
|
217
|
+
const { result, rewrite, purge } = inTransaction(db, false, () => {
|
|
218
|
+
const copies = [];
|
|
219
|
+
for (const row of strayImports(db, hippoRoot, tenantId)) {
|
|
220
|
+
const tag = toolTag(row.source);
|
|
221
|
+
if (tag !== null && setAsideRow(db, tag, row, 'project-repair').kind === 'dormant')
|
|
222
|
+
copies.push(row.id);
|
|
223
|
+
}
|
|
224
|
+
const folds = isGlobalStoreRoot(hippoRoot) ? planFolds(db, tenantId) : [];
|
|
225
|
+
const folded = folds.map((f) => foldInTx(db, tenantId, f.from, f.into));
|
|
226
|
+
const plan = planUserGlobalRepair(db, tenantId, []);
|
|
144
227
|
const stamp = db.prepare(`UPDATE memories SET origin_project = ?, updated_at = datetime('now') WHERE tenant_id = ? AND id = ?`);
|
|
145
228
|
for (const { id, origin } of plan.toProject)
|
|
146
229
|
stamp.run(origin, tenantId, id);
|
|
@@ -150,10 +233,14 @@ export function repairUserGlobalMerges(db, hippoRoot, opts) {
|
|
|
150
233
|
insertDormantRow(db, { entry: row, strength: calculateStrength(row, now), reason: 'project-repair', dormantAt: now.toISOString() });
|
|
151
234
|
deleteEntryRowInTx(db, row, ACTOR);
|
|
152
235
|
}
|
|
153
|
-
appendAuditEvent(db, { tenantId, actor: ACTOR, op: 'project_repair', metadata: { backup, ...plan } });
|
|
154
|
-
return {
|
|
236
|
+
appendAuditEvent(db, { tenantId, actor: ACTOR, op: 'project_repair', metadata: { backup, copies, folds, folded, ...plan } });
|
|
237
|
+
return {
|
|
238
|
+
result: { copies, folds, ...plan, backup },
|
|
239
|
+
rewrite: [...plan.toProject.map((r) => r.id), ...folded.flatMap((f) => f.restamped)],
|
|
240
|
+
purge: [...copies, ...plan.setAside, ...folded.flatMap((f) => f.setAside)],
|
|
241
|
+
};
|
|
155
242
|
});
|
|
156
|
-
refreshMirrors(db, hippoRoot, tenantId,
|
|
243
|
+
refreshMirrors(db, hippoRoot, tenantId, rewrite, purge);
|
|
157
244
|
return result;
|
|
158
245
|
}
|
|
159
246
|
/** After commit, as the agent memory sync does: a stale mirror would bring the old tag back on the next rebuild. */
|