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/session-digest.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type SessionTurn } from './capture.js';
|
|
1
|
+
import { type SessionTurn } from './capture/transcript.js';
|
|
2
2
|
import { type MemoryEntry } from './memory.js';
|
|
3
3
|
export declare const SESSION_DIGEST_TAG = "session-digest";
|
|
4
4
|
/** Five-word runs are stock phrases; six in a row is a copied clause. */
|
package/dist/session-digest.js
CHANGED
|
@@ -2,15 +2,22 @@
|
|
|
2
2
|
import { createHash } from 'crypto';
|
|
3
3
|
import * as fs from 'fs';
|
|
4
4
|
import * as path from 'path';
|
|
5
|
-
import { collectSessionTurns
|
|
5
|
+
import { collectSessionTurns } from './capture/transcript.js';
|
|
6
|
+
import { splitSentences } from './capture/extract.js';
|
|
6
7
|
import { isObjectLike, isStringValue } from './capture-contract.js';
|
|
7
8
|
import { PATCH_SUCCESS_LINE, patchPaths, shellPatch } from './codex-patch.js';
|
|
8
9
|
import { loadConfig } from './config.js';
|
|
10
|
+
import { errorMessage } from './log.js';
|
|
9
11
|
import { createMemory, Layer } from './memory.js';
|
|
10
12
|
import { RejectedValueError } from './rejection.js';
|
|
11
13
|
import { maskEmails, redactSecretsStrict } from './secret-detect.js';
|
|
12
|
-
import {
|
|
14
|
+
import { isInitialized } from './store/open.js';
|
|
15
|
+
import { writeEntry } from './store/entry-writes.js';
|
|
16
|
+
import { loadAllEntries } from './store/entry-reads.js';
|
|
17
|
+
import { SNAPSHOT_AMBIENT_MAX_AGE_MS } from './store/sessions.js';
|
|
18
|
+
import { loadLatestHandoff } from './store/handoffs.js';
|
|
13
19
|
import { isSyntheticMessage } from './token-ledger.js';
|
|
20
|
+
import { readTranscriptTail } from './transcript-tail.js';
|
|
14
21
|
export const SESSION_DIGEST_TAG = 'session-digest';
|
|
15
22
|
/** Five-word runs are stock phrases; six in a row is a copied clause. */
|
|
16
23
|
export const ECHO_WINDOW = 6;
|
package/dist/shared.d.ts
CHANGED
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
* Local .hippo/ stores are per-project.
|
|
6
6
|
*/
|
|
7
7
|
import { MemoryEntry } from './memory.js';
|
|
8
|
-
import { SearchResult,
|
|
8
|
+
import type { SearchResult, ResultCost } from './search/types.js';
|
|
9
|
+
import type { HybridVectorCandidates } from './search/vector.js';
|
|
10
|
+
import type { DatabaseSyncLike } from './db.js';
|
|
9
11
|
/**
|
|
10
12
|
* Returns the path to the global Hippo store.
|
|
11
13
|
* Resolution order: $HIPPO_HOME > $XDG_DATA_HOME/hippo > ~/.hippo/
|
|
@@ -23,6 +25,7 @@ export declare function initGlobal(): void;
|
|
|
23
25
|
export declare function promoteToGlobal(localRoot: string, id: string, opts?: {
|
|
24
26
|
actor?: string;
|
|
25
27
|
tenantId?: string;
|
|
28
|
+
afterWrite?: (db: DatabaseSyncLike, globalId: string) => void;
|
|
26
29
|
}): MemoryEntry;
|
|
27
30
|
export interface SearchOptions {
|
|
28
31
|
budget?: number;
|
|
@@ -91,6 +94,14 @@ export interface HybridSearchOptions extends SearchOptions {
|
|
|
91
94
|
* Async version of searchBoth that calls hybridSearch instead of search.
|
|
92
95
|
*/
|
|
93
96
|
export declare function searchBothHybrid(query: string, localRoot: string, globalRoot: string, options?: HybridSearchOptions): Promise<SearchResult[]>;
|
|
97
|
+
/** Hybrid ranking of rows already loaded from each store: the local bump, one copy per text, then the shared budget. */
|
|
98
|
+
export declare function rankBothStores(query: string, roots: {
|
|
99
|
+
local: string;
|
|
100
|
+
global: string;
|
|
101
|
+
}, entries: {
|
|
102
|
+
local: MemoryEntry[];
|
|
103
|
+
global: MemoryEntry[];
|
|
104
|
+
}, vectorCandidates: HybridVectorCandidates, options?: HybridSearchOptions): Promise<SearchResult[]>;
|
|
94
105
|
/** Tags whose rows only a hand-run share or promote may copy to the global store; derived rows inherit them. */
|
|
95
106
|
export declare const NEVER_AUTO_SHARE_TAGS: ReadonlySet<string>;
|
|
96
107
|
export declare function neverAutoShareTags(sources: readonly MemoryEntry[]): string[];
|
package/dist/shared.js
CHANGED
|
@@ -9,9 +9,16 @@ import * as fs from 'fs';
|
|
|
9
9
|
import * as path from 'path';
|
|
10
10
|
import { generateId, COMPACTION_MEMORY_TAG } from './memory.js';
|
|
11
11
|
import { AGENT_MEMORY_SOURCE_PREFIX, AGENT_MEMORY_TAGS } from './agent-memories/tools.js';
|
|
12
|
-
import { initStore
|
|
12
|
+
import { initStore } from './store/open.js';
|
|
13
|
+
import { writeEntry } from './store/entry-writes.js';
|
|
14
|
+
import { loadAllEntries, readEntry } from './store/entry-reads.js';
|
|
15
|
+
import { loadSearchEntries, loadRecallSearchEntries, recallScopeFilter } from './store/search-rows.js';
|
|
16
|
+
import { tallySources } from './store/candidates.js';
|
|
17
|
+
import { loadIndex } from './store/index-and-stats.js';
|
|
13
18
|
import { passesScopeFilterForRecall, passesCliRecallScopeFilter } from './recall-scope.js';
|
|
14
|
-
import { search
|
|
19
|
+
import { search } from './search/bm25-search.js';
|
|
20
|
+
import { hybridSearch } from './search/hybrid.js';
|
|
21
|
+
import { fitBudget } from './search/finalize.js';
|
|
15
22
|
import { evalNow } from './ablation.js';
|
|
16
23
|
import { deriveOriginProject, classifyOriginProject, resolveGlobalRootDir } from './project-identity.js';
|
|
17
24
|
import { detectSecret } from './secret-detect.js';
|
|
@@ -77,7 +84,7 @@ export function promoteToGlobal(localRoot, id, opts) {
|
|
|
77
84
|
source: `promoted:${localRoot}`,
|
|
78
85
|
origin_project: entry.origin_project ?? deriveOriginProject(path.dirname(path.resolve(localRoot))),
|
|
79
86
|
};
|
|
80
|
-
writeEntry(globalRoot, globalEntry, { actor: opts?.actor });
|
|
87
|
+
writeEntry(globalRoot, globalEntry, { actor: opts?.actor, afterWrite: opts?.afterWrite });
|
|
81
88
|
// Fire-and-forget: embedMemory gates on availability and never rejects.
|
|
82
89
|
void embedMemory(globalRoot, globalEntry);
|
|
83
90
|
return globalEntry;
|
|
@@ -141,13 +148,10 @@ export function searchBoth(query, localRoot, globalRoot, options = {}) {
|
|
|
141
148
|
* Async version of searchBoth that calls hybridSearch instead of search.
|
|
142
149
|
*/
|
|
143
150
|
export async function searchBothHybrid(query, localRoot, globalRoot, options = {}) {
|
|
144
|
-
const {
|
|
151
|
+
const { includeSuperseded, asOf, tenantId, entryFilter, recallScope } = options;
|
|
145
152
|
// When an admission filter is active, lift the per-store candidate cap
|
|
146
153
|
// (default 200): excluded rows matching the query could otherwise fill the
|
|
147
154
|
// window before any admitted row is even loaded (codex gating round 6).
|
|
148
|
-
// Only ambient-context query mode sets entryFilter, and that path is
|
|
149
|
-
// interactive - never the per-turn pinned-only hook - so ranking the full
|
|
150
|
-
// match set is acceptable.
|
|
151
155
|
// 5000 = 25x the default 200-row window: large enough that exclusion
|
|
152
156
|
// crowding is a non-issue on real stores, bounded so a common query term
|
|
153
157
|
// on a 100k-row store cannot stall an interactive call by ranking every
|
|
@@ -166,27 +170,29 @@ export async function searchBothHybrid(query, localRoot, globalRoot, options = {
|
|
|
166
170
|
? loadRecallSearchEntries(root, query, searchWindow, tenantId, recallScope.requested, recallScope.additive ? 'additive' : 'exact', Boolean(includeSuperseded) || Boolean(asOf))
|
|
167
171
|
: loadSearchEntries(root, query, searchWindow, tenantId);
|
|
168
172
|
};
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
|
|
173
|
+
const passesScope = (e) => !recallScope || (recallScope.additive
|
|
174
|
+
? passesCliRecallScopeFilter(e.scope ?? null, recallScope.requested)
|
|
175
|
+
: passesScopeFilterForRecall(e.scope ?? null, recallScope.requested));
|
|
176
|
+
const admit = (e) => passesScope(e) && (!entryFilter || entryFilter(e));
|
|
177
|
+
const localEntries = loadEntries(localRoot).filter(admit);
|
|
178
|
+
const globalEntries = loadEntries(globalRoot).filter(admit);
|
|
179
|
+
// The vector arm loads under the same SQL rules as loadEntries, then the same JS admission.
|
|
180
|
+
const vectorCandidates = {
|
|
181
|
+
tenantId,
|
|
182
|
+
scope: recallScope ? recallScopeFilter(recallScope.requested, recallScope.additive ? 'additive' : 'exact') : undefined,
|
|
183
|
+
includeSuperseded: !recallScope || Boolean(includeSuperseded) || Boolean(asOf),
|
|
184
|
+
admit,
|
|
185
|
+
};
|
|
186
|
+
return rankBothStores(query, { local: localRoot, global: globalRoot }, { local: localEntries, global: globalEntries }, vectorCandidates, options);
|
|
187
|
+
}
|
|
188
|
+
/** Hybrid ranking of rows already loaded from each store: the local bump, one copy per text, then the shared budget. */
|
|
189
|
+
export async function rankBothStores(query, roots, entries, vectorCandidates, options = {}) {
|
|
190
|
+
const { budget = 4000, now = evalNow(), embeddingWeight, explain, mmr, mmrLambda, localBump = 1.2, minResults, cost, scope, includeSuperseded, asOf, summaryDeboost, summaryFreshness } = options;
|
|
191
|
+
if (entries.local.length === 0 && entries.global.length === 0)
|
|
183
192
|
return [];
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
});
|
|
187
|
-
const globalResults = await hybridSearch(query, globalEntries, {
|
|
188
|
-
budget, now, hippoRoot: globalRoot, embeddingWeight, explain, mmr, mmrLambda, minResults, cost, scope, includeSuperseded, asOf, summaryDeboost, summaryFreshness,
|
|
189
|
-
});
|
|
193
|
+
const shared = { budget, now, embeddingWeight, explain, mmr, mmrLambda, minResults, cost, scope, includeSuperseded, asOf, summaryDeboost, summaryFreshness, vectorCandidates };
|
|
194
|
+
const localResults = await hybridSearch(query, entries.local, { ...shared, hippoRoot: roots.local });
|
|
195
|
+
const globalResults = await hybridSearch(query, entries.global, { ...shared, hippoRoot: roots.global });
|
|
190
196
|
// Tag global results. Local memories get a configurable priority bump.
|
|
191
197
|
const tagged = [
|
|
192
198
|
...localResults.map((r) => ({
|
package/dist/skills.d.ts
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* Lifecycle: active -> superseded (a newer version replaces it) or active ->
|
|
22
22
|
* closed (retired). Export renders ACTIVE skills only.
|
|
23
23
|
*/
|
|
24
|
+
import { type KeysetPosition } from './keyset.js';
|
|
24
25
|
export type SkillStatus = 'active' | 'superseded' | 'closed';
|
|
25
26
|
export declare const VALID_SKILL_STATES: ReadonlySet<SkillStatus>;
|
|
26
27
|
/** Field caps (untrusted at the HTTP/SDK boundary). instructions is a body, so a
|
|
@@ -66,6 +67,8 @@ export interface SaveSkillOpts {
|
|
|
66
67
|
export interface ListSkillsOpts {
|
|
67
68
|
status?: SkillStatus;
|
|
68
69
|
limit?: number;
|
|
70
|
+
/** Resume after this row: the position the previous page ended on. */
|
|
71
|
+
after?: KeysetPosition;
|
|
69
72
|
}
|
|
70
73
|
/**
|
|
71
74
|
* Create a skill (or a new version that supersedes an existing one). Writes the
|
package/dist/skills.js
CHANGED
|
@@ -23,11 +23,12 @@
|
|
|
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 { createMemory, Layer } from './memory.js';
|
|
29
29
|
import { appendAuditEvent } from './audit.js';
|
|
30
30
|
import { objectHalfLifeDays } from './half-life-migration.js';
|
|
31
|
+
import { keysetAfter } from './keyset.js';
|
|
31
32
|
export const VALID_SKILL_STATES = new Set([
|
|
32
33
|
'active',
|
|
33
34
|
'superseded',
|
|
@@ -110,9 +111,76 @@ function buildSkillContent(skillName, instructions, trigger) {
|
|
|
110
111
|
content += `\n\n${instructions}`;
|
|
111
112
|
return content;
|
|
112
113
|
}
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
//
|
|
114
|
+
// Preflight the supersede target BEFORE inserting the new row (so the new
|
|
115
|
+
// autoincrement id can never be its own supersede target); read the
|
|
116
|
+
// predecessor version in the same SELECT for server-derived versioning.
|
|
117
|
+
// Mirrors saveProcess / savePolicy (codex P1 2026-05-28).
|
|
118
|
+
function preflightSkillSupersede(db, tenantId, supersedesId) {
|
|
119
|
+
// SAFETY: row shape matches the `status, version` columns named in the SELECT below.
|
|
120
|
+
const pred = db.prepare(`SELECT status, version FROM skills WHERE id = ? AND tenant_id = ?`).get(supersedesId, tenantId);
|
|
121
|
+
if (!pred) {
|
|
122
|
+
throw new NotFoundError(`saveSkill: skill ${supersedesId} to supersede not found for tenant ${tenantId}`);
|
|
123
|
+
}
|
|
124
|
+
if (pred.status !== 'active') {
|
|
125
|
+
throw new ConflictError(`saveSkill: skill ${supersedesId} is not active (status='${pred.status}'); only active skills can be superseded.`);
|
|
126
|
+
}
|
|
127
|
+
return pred.version + 1;
|
|
128
|
+
}
|
|
129
|
+
function insertSkillRow(db, memoryId, w, version) {
|
|
130
|
+
const result = db.prepare(`
|
|
131
|
+
INSERT INTO skills(
|
|
132
|
+
memory_id, tenant_id, skill_name, instructions, trigger_text, version,
|
|
133
|
+
status, superseded_by, superseded_at, change_summary, closed_at, created_at
|
|
134
|
+
) VALUES (?, ?, ?, ?, ?, ?, 'active', NULL, NULL, ?, NULL, ?)
|
|
135
|
+
`).run(memoryId, w.tenantId, w.name, w.instructions, w.trigger, version, w.changeSummary, w.now);
|
|
136
|
+
return Number(result.lastInsertRowid ?? 0);
|
|
137
|
+
}
|
|
138
|
+
function supersedeSkillRow(db, w, supersedesId, skillId, version) {
|
|
139
|
+
const sup = db.prepare(`
|
|
140
|
+
UPDATE skills
|
|
141
|
+
SET status = 'superseded', superseded_by = ?, superseded_at = ?
|
|
142
|
+
WHERE id = ? AND tenant_id = ? AND status = 'active' AND id != ?
|
|
143
|
+
`).run(skillId, w.now, supersedesId, w.tenantId, skillId);
|
|
144
|
+
if (sup.changes === 0) {
|
|
145
|
+
throw new ConflictError(`saveSkill: skill ${supersedesId} could not be superseded (no longer active or self-reference).`);
|
|
146
|
+
}
|
|
147
|
+
appendAuditEvent(db, {
|
|
148
|
+
tenantId: w.tenantId,
|
|
149
|
+
actor: w.actor,
|
|
150
|
+
op: 'skill_supersede',
|
|
151
|
+
targetId: String(supersedesId),
|
|
152
|
+
metadata: {
|
|
153
|
+
skill_id: supersedesId,
|
|
154
|
+
superseded_by: skillId,
|
|
155
|
+
new_version: version,
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
/** The afterWrite body: preflight, INSERT, supersede, reload, create audit, all in one SAVEPOINT. */
|
|
160
|
+
function writeSkillRow(db, memoryId, w) {
|
|
161
|
+
const version = w.supersedesId !== undefined ? preflightSkillSupersede(db, w.tenantId, w.supersedesId) : 1;
|
|
162
|
+
const skillId = insertSkillRow(db, memoryId, w, version);
|
|
163
|
+
if (w.supersedesId !== undefined)
|
|
164
|
+
supersedeSkillRow(db, w, w.supersedesId, skillId, version);
|
|
165
|
+
// SAFETY: row's shape matches the columns named in SKILL_COLS above.
|
|
166
|
+
const row = db.prepare(`SELECT ${SKILL_COLS} FROM skills WHERE id = ?`)
|
|
167
|
+
.get(skillId);
|
|
168
|
+
if (!row)
|
|
169
|
+
throw new Error('saveSkill: failed to reload saved skill row');
|
|
170
|
+
// GDPR-light metadata: ids + flags only, no skill text.
|
|
171
|
+
appendAuditEvent(db, {
|
|
172
|
+
tenantId: w.tenantId,
|
|
173
|
+
actor: w.actor,
|
|
174
|
+
op: 'skill_create',
|
|
175
|
+
targetId: String(skillId),
|
|
176
|
+
metadata: {
|
|
177
|
+
skill_id: skillId,
|
|
178
|
+
version,
|
|
179
|
+
has_trigger: w.trigger !== null,
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
return row;
|
|
183
|
+
}
|
|
116
184
|
/**
|
|
117
185
|
* Create a skill (or a new version that supersedes an existing one). Writes the
|
|
118
186
|
* memory mirror + the skills row atomically inside writeEntry's SAVEPOINT. When
|
|
@@ -136,72 +204,21 @@ export function saveSkill(hippoRoot, tenantId, opts, actor = 'cli') {
|
|
|
136
204
|
baseHalfLifeDays: objectHalfLifeDays(hippoRoot),
|
|
137
205
|
tenantId,
|
|
138
206
|
});
|
|
207
|
+
const w = {
|
|
208
|
+
tenantId,
|
|
209
|
+
actor,
|
|
210
|
+
name,
|
|
211
|
+
instructions: opts.instructions,
|
|
212
|
+
trigger,
|
|
213
|
+
changeSummary,
|
|
214
|
+
supersedesId: opts.supersedesSkillId,
|
|
215
|
+
now,
|
|
216
|
+
};
|
|
139
217
|
let savedRow;
|
|
140
218
|
writeEntry(hippoRoot, mem, {
|
|
141
219
|
actor,
|
|
142
220
|
afterWrite: (db, memoryId) => {
|
|
143
|
-
|
|
144
|
-
// autoincrement id can never be its own supersede target); read the
|
|
145
|
-
// predecessor version in the same SELECT for server-derived versioning.
|
|
146
|
-
// Mirrors saveProcess / savePolicy (codex P1 2026-05-28).
|
|
147
|
-
let version = 1;
|
|
148
|
-
if (opts.supersedesSkillId !== undefined) {
|
|
149
|
-
// SAFETY: row shape matches the `status, version` columns named in the SELECT below.
|
|
150
|
-
const pred = db.prepare(`SELECT status, version FROM skills WHERE id = ? AND tenant_id = ?`).get(opts.supersedesSkillId, tenantId);
|
|
151
|
-
if (!pred) {
|
|
152
|
-
throw new NotFoundError(`saveSkill: skill ${opts.supersedesSkillId} to supersede not found for tenant ${tenantId}`);
|
|
153
|
-
}
|
|
154
|
-
if (pred.status !== 'active') {
|
|
155
|
-
throw new ConflictError(`saveSkill: skill ${opts.supersedesSkillId} is not active (status='${pred.status}'); only active skills can be superseded.`);
|
|
156
|
-
}
|
|
157
|
-
version = pred.version + 1;
|
|
158
|
-
}
|
|
159
|
-
const result = db.prepare(`
|
|
160
|
-
INSERT INTO skills(
|
|
161
|
-
memory_id, tenant_id, skill_name, instructions, trigger_text, version,
|
|
162
|
-
status, superseded_by, superseded_at, change_summary, closed_at, created_at
|
|
163
|
-
) VALUES (?, ?, ?, ?, ?, ?, 'active', NULL, NULL, ?, NULL, ?)
|
|
164
|
-
`).run(memoryId, tenantId, name, opts.instructions, trigger, version, changeSummary, now);
|
|
165
|
-
const skillId = Number(result.lastInsertRowid ?? 0);
|
|
166
|
-
if (opts.supersedesSkillId !== undefined) {
|
|
167
|
-
const sup = db.prepare(`
|
|
168
|
-
UPDATE skills
|
|
169
|
-
SET status = 'superseded', superseded_by = ?, superseded_at = ?
|
|
170
|
-
WHERE id = ? AND tenant_id = ? AND status = 'active' AND id != ?
|
|
171
|
-
`).run(skillId, now, opts.supersedesSkillId, tenantId, skillId);
|
|
172
|
-
if (sup.changes === 0) {
|
|
173
|
-
throw new ConflictError(`saveSkill: skill ${opts.supersedesSkillId} could not be superseded (no longer active or self-reference).`);
|
|
174
|
-
}
|
|
175
|
-
appendAuditEvent(db, {
|
|
176
|
-
tenantId,
|
|
177
|
-
actor,
|
|
178
|
-
op: 'skill_supersede',
|
|
179
|
-
targetId: String(opts.supersedesSkillId),
|
|
180
|
-
metadata: {
|
|
181
|
-
skill_id: opts.supersedesSkillId,
|
|
182
|
-
superseded_by: skillId,
|
|
183
|
-
new_version: version,
|
|
184
|
-
},
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
// SAFETY: row's shape matches the columns named in SKILL_COLS above.
|
|
188
|
-
const row = db.prepare(`SELECT ${SKILL_COLS} FROM skills WHERE id = ?`)
|
|
189
|
-
.get(skillId);
|
|
190
|
-
if (!row)
|
|
191
|
-
throw new Error('saveSkill: failed to reload saved skill row');
|
|
192
|
-
savedRow = row;
|
|
193
|
-
// GDPR-light metadata: ids + flags only, no skill text.
|
|
194
|
-
appendAuditEvent(db, {
|
|
195
|
-
tenantId,
|
|
196
|
-
actor,
|
|
197
|
-
op: 'skill_create',
|
|
198
|
-
targetId: String(skillId),
|
|
199
|
-
metadata: {
|
|
200
|
-
skill_id: skillId,
|
|
201
|
-
version,
|
|
202
|
-
has_trigger: trigger !== null,
|
|
203
|
-
},
|
|
204
|
-
});
|
|
221
|
+
savedRow = writeSkillRow(db, memoryId, w);
|
|
205
222
|
},
|
|
206
223
|
});
|
|
207
224
|
if (!savedRow) {
|
|
@@ -278,6 +295,7 @@ export function loadSkillById(hippoRoot, tenantId, id) {
|
|
|
278
295
|
export function loadSkills(hippoRoot, tenantId, opts = {}) {
|
|
279
296
|
assertTenantId('loadSkills', tenantId);
|
|
280
297
|
const limit = opts.limit ?? 100;
|
|
298
|
+
const after = keysetAfter('created_at', 'id', opts.after);
|
|
281
299
|
const db = openHippoDb(hippoRoot);
|
|
282
300
|
try {
|
|
283
301
|
let rows;
|
|
@@ -288,19 +306,19 @@ export function loadSkills(hippoRoot, tenantId, opts = {}) {
|
|
|
288
306
|
// SAFETY: rows' shape matches the columns named in SKILL_COLS above.
|
|
289
307
|
rows = db.prepare(`
|
|
290
308
|
SELECT ${SKILL_COLS} FROM skills
|
|
291
|
-
WHERE tenant_id = ? AND status =
|
|
309
|
+
WHERE tenant_id = ? AND status = ?${after.sql}
|
|
292
310
|
ORDER BY created_at DESC, id DESC
|
|
293
311
|
LIMIT ?
|
|
294
|
-
`).all(tenantId, opts.status, limit);
|
|
312
|
+
`).all(tenantId, opts.status, ...after.params, limit);
|
|
295
313
|
}
|
|
296
314
|
else {
|
|
297
315
|
// SAFETY: rows' shape matches the columns named in SKILL_COLS above.
|
|
298
316
|
rows = db.prepare(`
|
|
299
317
|
SELECT ${SKILL_COLS} FROM skills
|
|
300
|
-
WHERE tenant_id =
|
|
318
|
+
WHERE tenant_id = ?${after.sql}
|
|
301
319
|
ORDER BY created_at DESC, id DESC
|
|
302
320
|
LIMIT ?
|
|
303
|
-
`).all(tenantId, limit);
|
|
321
|
+
`).all(tenantId, ...after.params, limit);
|
|
304
322
|
}
|
|
305
323
|
return rows.map(rowToSkill);
|
|
306
324
|
}
|
package/dist/stdin.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { envStdinWaitMs } from './env.js';
|
|
1
2
|
function defaultWaitMs() {
|
|
2
|
-
|
|
3
|
-
return parsed > 0 ? parsed : 1000;
|
|
3
|
+
return envStdinWaitMs() ?? 1000;
|
|
4
4
|
}
|
|
5
5
|
/** Never blocks: a TTY resolves at once, otherwise waits up to `waitMs` of
|
|
6
6
|
* idle (default 1000ms, or `HIPPO_STDIN_WAIT_MS`), refreshed on each chunk
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { openHippoDb } from '../db.js';
|
|
2
|
+
import { type AuditOp } from '../audit.js';
|
|
3
|
+
import { RejectedValueError } from '../rejection.js';
|
|
4
|
+
import type { JsonValue } from './rows.js';
|
|
5
|
+
/**
|
|
6
|
+
* Emit an audit event for a mutation against `db`. Wrapped so a broken audit
|
|
7
|
+
* log can never crash the surrounding mutation — the SQLite store is still the
|
|
8
|
+
* source of truth and audit failures are diagnosable from the missing rows.
|
|
9
|
+
*/
|
|
10
|
+
export declare function audit(db: ReturnType<typeof openHippoDb>, op: AuditOp, targetId?: string, metadata?: Record<string, JsonValue>, actor?: string, tenantId?: string): void;
|
|
11
|
+
/**
|
|
12
|
+
* Refusal audit for the AT1 rejection guard (plan §3). Written by the
|
|
13
|
+
* transaction OWNER post-rollback — writeEntry's catch (no outer tx exists
|
|
14
|
+
* there, so this lands in a fresh implicit transaction) and api.supersede's
|
|
15
|
+
* catch (after its own ROLLBACK) — never inside a scope the caller's own
|
|
16
|
+
* rollback could claw back. Best-effort `audit()` semantics: never throws.
|
|
17
|
+
*/
|
|
18
|
+
export declare function auditRejectionRefusal(db: ReturnType<typeof openHippoDb>, err: RejectedValueError, actor: string): void;
|
|
19
|
+
//# sourceMappingURL=audit-event.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { resolveTenantId } from '../tenant.js';
|
|
2
|
+
import { appendAuditEvent, reportAuditWriteFailure } from '../audit.js';
|
|
3
|
+
/**
|
|
4
|
+
* Emit an audit event for a mutation against `db`. Wrapped so a broken audit
|
|
5
|
+
* log can never crash the surrounding mutation — the SQLite store is still the
|
|
6
|
+
* source of truth and audit failures are diagnosable from the missing rows.
|
|
7
|
+
*/
|
|
8
|
+
export function audit(db, op, targetId, metadata, actor = 'cli', tenantId) {
|
|
9
|
+
try {
|
|
10
|
+
appendAuditEvent(db, {
|
|
11
|
+
tenantId: tenantId ?? resolveTenantId({}),
|
|
12
|
+
actor,
|
|
13
|
+
op,
|
|
14
|
+
targetId,
|
|
15
|
+
metadata,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
// The mutation has already succeeded; a broken audit table must not undo it.
|
|
20
|
+
reportAuditWriteFailure(op, String(error), targetId);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Refusal audit for the AT1 rejection guard (plan §3). Written by the
|
|
25
|
+
* transaction OWNER post-rollback — writeEntry's catch (no outer tx exists
|
|
26
|
+
* there, so this lands in a fresh implicit transaction) and api.supersede's
|
|
27
|
+
* catch (after its own ROLLBACK) — never inside a scope the caller's own
|
|
28
|
+
* rollback could claw back. Best-effort `audit()` semantics: never throws.
|
|
29
|
+
*/
|
|
30
|
+
export function auditRejectionRefusal(db, err, actor) {
|
|
31
|
+
audit(db, 'reject_refusal', err.entryId, { digest: err.digest, reason: err.reason }, actor, err.tenantId);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=audit-event.js.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { MemoryEntry, StrengthInputs } from '../memory.js';
|
|
2
|
+
export interface AmbientRecallRequest {
|
|
3
|
+
terms: string[];
|
|
4
|
+
limit: number;
|
|
5
|
+
}
|
|
6
|
+
export interface AmbientLoadResult {
|
|
7
|
+
entries: MemoryEntry[];
|
|
8
|
+
recall?: MemoryEntry[];
|
|
9
|
+
}
|
|
10
|
+
/** Exported so the plan test runs the exact SQL; idx_memories_pinned (db.ts v51) serves it. */
|
|
11
|
+
export declare const AMBIENT_PINNED_WHERE = "pinned = 1 AND superseded_by IS NULL AND tenant_id = ? ORDER BY created ASC, id ASC";
|
|
12
|
+
/** Exported so the plan test runs the exact SQL; idx_memories_created_drift (db.ts v51) serves it. */
|
|
13
|
+
export declare const AMBIENT_DRIFT_SQL = "SELECT 1 FROM memories WHERE superseded_by IS NULL AND tenant_id = ? AND (length(created) <> 24 OR created NOT LIKE '%Z') LIMIT 1";
|
|
14
|
+
export declare function loadAmbientCandidates(hippoRoot: string, tenantId: string, recentNeeded: number, admit: (e: MemoryEntry) => boolean, recall?: AmbientRecallRequest): AmbientLoadResult;
|
|
15
|
+
/** The rows a non-pinned context read may admit; each predicate is one getContext's admission applies again in JS. */
|
|
16
|
+
export interface ContextCandidateFilter {
|
|
17
|
+
/** Envelope scope asked for by name; absent applies recall's default deny. */
|
|
18
|
+
exactScope?: string;
|
|
19
|
+
/** Rows of this project and user-global rows pass; absent admits every origin. */
|
|
20
|
+
project?: string;
|
|
21
|
+
/** Most rows returned; past it, the rows decay has worn least win. */
|
|
22
|
+
cap: number;
|
|
23
|
+
now: Date;
|
|
24
|
+
}
|
|
25
|
+
/** Live tenant rows passing `filter`, at most `filter.cap`, in loadAllEntries' order; below the cap, every such row. */
|
|
26
|
+
export declare function loadContextCandidates(hippoRoot: string, tenantId: string, filter: ContextCandidateFilter): MemoryEntry[];
|
|
27
|
+
/** A row's strength inputs and tags, without its text or the JSON lists a full row parses. */
|
|
28
|
+
export type StrengthRow = StrengthInputs & Pick<MemoryEntry, 'tags'>;
|
|
29
|
+
/** Every tenant row as a StrengthRow, for whole-store health numbers; defaults match rowToEntry's. */
|
|
30
|
+
export declare function loadStrengthRows(hippoRoot: string, tenantId: string): StrengthRow[];
|
|
31
|
+
/** Text and source of tenant rows holding any of `words`; a row equal to a text apart from spacing holds its every word. */
|
|
32
|
+
export declare function loadTextsHoldingWords(hippoRoot: string, tenantId: string, words: readonly string[]): Array<Pick<MemoryEntry, 'content' | 'source'>>;
|
|
33
|
+
export interface SourceTally {
|
|
34
|
+
source: string;
|
|
35
|
+
count: number;
|
|
36
|
+
latest: string;
|
|
37
|
+
/** `created`, a unit separator, then `id` of the source's first row in loadAllEntries' order. */
|
|
38
|
+
first: string;
|
|
39
|
+
}
|
|
40
|
+
/** Row count and newest `created` per source, for peer listings that need no row; all tenants when `tenantId` is absent. */
|
|
41
|
+
export declare function tallySources(hippoRoot: string, tenantId?: string): SourceTally[];
|
|
42
|
+
//# sourceMappingURL=candidates.d.ts.map
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { closeHippoDb } from '../db.js';
|
|
2
|
+
import { RECALL_DEFAULT_DENY_SCOPES } from '../recall-scope.js';
|
|
3
|
+
import { MEMORY_SELECT_COLUMNS, rowToEntry, parseJsonArray } from './rows.js';
|
|
4
|
+
import { openStore } from './open.js';
|
|
5
|
+
import { pickRarestFtsQuery, loadRecallSearchEntriesFromDb } from './search-rows.js';
|
|
6
|
+
const AMBIENT_SCOPED = 'superseded_by IS NULL AND tenant_id = ?';
|
|
7
|
+
/** Exported so the plan test runs the exact SQL; idx_memories_pinned (db.ts v51) serves it. */
|
|
8
|
+
export const AMBIENT_PINNED_WHERE = `pinned = 1 AND ${AMBIENT_SCOPED} ORDER BY created ASC, id ASC`;
|
|
9
|
+
/** Exported so the plan test runs the exact SQL; idx_memories_created_drift (db.ts v51) serves it. */
|
|
10
|
+
export const AMBIENT_DRIFT_SQL = `SELECT 1 FROM memories WHERE ${AMBIENT_SCOPED} AND (length(created) <> 24 OR created NOT LIKE '%Z') LIMIT 1`;
|
|
11
|
+
// The pins plus the `recentNeeded` newest rows that pass `admit`, for ambient
|
|
12
|
+
// injection. One connection; `recall` piggybacks the Z1 FTS query on it too.
|
|
13
|
+
export function loadAmbientCandidates(hippoRoot, tenantId, recentNeeded, admit, recall) {
|
|
14
|
+
// A SQL LIMIT takes an integer; the Array.slice this replaced truncated one,
|
|
15
|
+
// and include_recent is any non-negative finite number at the HTTP edge.
|
|
16
|
+
const needed = Math.trunc(recentNeeded);
|
|
17
|
+
const db = openStore(hippoRoot);
|
|
18
|
+
try {
|
|
19
|
+
// SAFETY: every `where` below starts from MEMORY_SELECT_COLUMNS' table.
|
|
20
|
+
const run = (where, params) => db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE ${where}`).all(...params).map(rowToEntry);
|
|
21
|
+
const byId = new Map();
|
|
22
|
+
const scoped = AMBIENT_SCOPED;
|
|
23
|
+
for (const e of run(AMBIENT_PINNED_WHERE, [tenantId])) {
|
|
24
|
+
if (admit(e))
|
|
25
|
+
byId.set(e.id, e);
|
|
26
|
+
}
|
|
27
|
+
if (needed > 0) {
|
|
28
|
+
// Text order is chronological only for canonical UTC ISO (memory.ts).
|
|
29
|
+
const drifted = db.prepare(AMBIENT_DRIFT_SQL).get(tenantId) !== undefined;
|
|
30
|
+
// `id DESC` mirrors getContext's comparator, not loadFreshRawMemories'
|
|
31
|
+
// cross-ingest-stable order: that would change what the hook injects.
|
|
32
|
+
const window = Math.max(needed * 4, 32);
|
|
33
|
+
const windowed = drifted
|
|
34
|
+
? []
|
|
35
|
+
: run(`${scoped} ORDER BY created DESC, id DESC LIMIT ?`, [tenantId, window]);
|
|
36
|
+
let kept = windowed.filter(admit);
|
|
37
|
+
if (drifted || (kept.length < needed && windowed.length === window)) {
|
|
38
|
+
kept = run(`${scoped} ORDER BY created DESC, id DESC`, [tenantId]).filter(admit);
|
|
39
|
+
}
|
|
40
|
+
for (const e of kept)
|
|
41
|
+
byId.set(e.id, e);
|
|
42
|
+
}
|
|
43
|
+
// loadAllEntries' order: rankedPinned's comparator can tie and Array.sort
|
|
44
|
+
// is stable, so input order is load-bearing downstream.
|
|
45
|
+
const entries = [...byId.values()].sort((a, b) => {
|
|
46
|
+
const byCreated = a.created.localeCompare(b.created);
|
|
47
|
+
return byCreated !== 0 ? byCreated : a.id.localeCompare(b.id);
|
|
48
|
+
});
|
|
49
|
+
if (!recall)
|
|
50
|
+
return { entries };
|
|
51
|
+
const ftsQuery = pickRarestFtsQuery(db, recall.terms);
|
|
52
|
+
const recallEntries = ftsQuery
|
|
53
|
+
? loadRecallSearchEntriesFromDb(db, ftsQuery, recall.limit, tenantId, undefined, 'exact', false)
|
|
54
|
+
: [];
|
|
55
|
+
return { entries, recall: recallEntries };
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
closeHippoDb(db);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// calculateStrength's decay exponent with its reward factor, pins first: the strongest rows, without scoring each one in JS.
|
|
62
|
+
const DECAY_RANK_SQL = `pinned DESC,
|
|
63
|
+
CASE WHEN half_life_days > 0 THEN (julianday(?) - julianday(last_retrieved))
|
|
64
|
+
/ (half_life_days * (1.0 + 0.5 * (COALESCE(outcome_positive, 0) - COALESCE(outcome_negative, 0))
|
|
65
|
+
/ (COALESCE(outcome_positive, 0) + COALESCE(outcome_negative, 0) + 1.0))) END ASC NULLS LAST,
|
|
66
|
+
id ASC`;
|
|
67
|
+
/** Live tenant rows passing `filter`, at most `filter.cap`, in loadAllEntries' order; below the cap, every such row. */
|
|
68
|
+
export function loadContextCandidates(hippoRoot, tenantId, filter) {
|
|
69
|
+
const where = ['tenant_id = ?', `COALESCE(superseded_by, '') = ''`];
|
|
70
|
+
const params = [tenantId];
|
|
71
|
+
if (filter.exactScope) {
|
|
72
|
+
where.push('scope = ?');
|
|
73
|
+
params.push(filter.exactScope);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
// isRestrictedScope's rule; NOT LIKE folds ASCII case as its /:private:/i does.
|
|
77
|
+
where.push(`(scope IS NULL OR (scope NOT IN (${RECALL_DEFAULT_DENY_SCOPES.map(() => '?').join(', ')}) AND scope NOT LIKE '%:private:%'))`);
|
|
78
|
+
params.push(...RECALL_DEFAULT_DENY_SCOPES);
|
|
79
|
+
}
|
|
80
|
+
if (filter.project !== undefined) {
|
|
81
|
+
where.push(`(origin_project = '' OR origin_project = ?)`);
|
|
82
|
+
params.push(filter.project);
|
|
83
|
+
}
|
|
84
|
+
const db = openStore(hippoRoot);
|
|
85
|
+
try {
|
|
86
|
+
// SAFETY: the outer SELECT names exactly MEMORY_SELECT_COLUMNS, matching MemoryRow's field set; the rank sort carries ids only.
|
|
87
|
+
const rows = db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE id IN (
|
|
88
|
+
SELECT id FROM memories WHERE ${where.join(' AND ')} ORDER BY ${DECAY_RANK_SQL} LIMIT ?
|
|
89
|
+
) ORDER BY created ASC, id ASC`).all(...params, filter.now.toISOString(), Math.max(0, Math.trunc(filter.cap)));
|
|
90
|
+
return rows.map(rowToEntry);
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
closeHippoDb(db);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/** Every tenant row as a StrengthRow, for whole-store health numbers; defaults match rowToEntry's. */
|
|
97
|
+
export function loadStrengthRows(hippoRoot, tenantId) {
|
|
98
|
+
const db = openStore(hippoRoot);
|
|
99
|
+
try {
|
|
100
|
+
// SAFETY: rows' shape matches the columns named in the SELECT below.
|
|
101
|
+
const rows = db.prepare(`SELECT pinned, created, last_retrieved, half_life_days, retrieval_count, emotional_valence, outcome_positive, outcome_negative, tags_json
|
|
102
|
+
FROM memories WHERE tenant_id = ?`).all(tenantId);
|
|
103
|
+
return rows.map((row) => ({
|
|
104
|
+
pinned: Boolean(row.pinned),
|
|
105
|
+
created: row.created,
|
|
106
|
+
last_retrieved: row.last_retrieved,
|
|
107
|
+
half_life_days: Number(row.half_life_days ?? 7),
|
|
108
|
+
retrieval_count: Number(row.retrieval_count ?? 0),
|
|
109
|
+
emotional_valence: row.emotional_valence ?? 'neutral',
|
|
110
|
+
outcome_positive: Number(row.outcome_positive ?? 0),
|
|
111
|
+
outcome_negative: Number(row.outcome_negative ?? 0),
|
|
112
|
+
tags: parseJsonArray(row.tags_json),
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
closeHippoDb(db);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/** Text and source of tenant rows holding any of `words`; a row equal to a text apart from spacing holds its every word. */
|
|
120
|
+
export function loadTextsHoldingWords(hippoRoot, tenantId, words) {
|
|
121
|
+
const unique = [...new Set(words)];
|
|
122
|
+
const out = [];
|
|
123
|
+
const db = openStore(hippoRoot);
|
|
124
|
+
try {
|
|
125
|
+
// Chunked so one statement stays far under SQLite's bound-parameter limit.
|
|
126
|
+
for (let i = 0; i < unique.length; i += 200) {
|
|
127
|
+
const chunk = unique.slice(i, i + 200);
|
|
128
|
+
// SAFETY: rows' shape matches the two columns named in the SELECT below.
|
|
129
|
+
const rows = db.prepare(`SELECT content, source FROM memories WHERE tenant_id = ? AND (${chunk.map(() => 'instr(content, ?) > 0').join(' OR ')})`).all(tenantId, ...chunk);
|
|
130
|
+
for (const row of rows)
|
|
131
|
+
out.push({ content: row.content, source: row.source ?? 'cli' });
|
|
132
|
+
}
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
135
|
+
finally {
|
|
136
|
+
closeHippoDb(db);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/** Row count and newest `created` per source, for peer listings that need no row; all tenants when `tenantId` is absent. */
|
|
140
|
+
export function tallySources(hippoRoot, tenantId) {
|
|
141
|
+
const db = openStore(hippoRoot);
|
|
142
|
+
try {
|
|
143
|
+
const where = tenantId !== undefined ? 'WHERE tenant_id = ?' : '';
|
|
144
|
+
// SAFETY: rows' shape matches the four aliased columns in the SELECT below.
|
|
145
|
+
return db.prepare(`SELECT COALESCE(source, 'cli') AS source, COUNT(*) AS count, MAX(created) AS latest, MIN(created || char(31) || id) AS first
|
|
146
|
+
FROM memories ${where} GROUP BY COALESCE(source, 'cli')`).all(...(tenantId !== undefined ? [tenantId] : []));
|
|
147
|
+
}
|
|
148
|
+
finally {
|
|
149
|
+
closeHippoDb(db);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=candidates.js.map
|