hippo-memory 1.60.0 → 1.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/ablation.js +9 -27
- package/dist/agent-memories/apply.js +4 -1
- package/dist/agent-memories/claude-code.js +1 -1
- package/dist/agent-memories/codex.js +1 -1
- package/dist/agent-memories/gemini.js +1 -1
- package/dist/agent-memories/legacy.js +1 -1
- package/dist/agent-memories/source.js +1 -1
- package/dist/agent-memories/sync.js +8 -3
- package/dist/ambient-store.d.ts +14 -0
- package/dist/ambient-store.js +90 -0
- package/dist/ambient.d.ts +23 -0
- package/dist/ambient.js +72 -50
- package/dist/api/assemble.d.ts +93 -0
- package/dist/api/assemble.js +152 -0
- package/dist/api/audit.d.ts +17 -0
- package/dist/api/audit.js +23 -0
- package/dist/api/auth.d.ts +79 -0
- package/dist/api/auth.js +178 -0
- package/dist/api/context-types.d.ts +105 -0
- package/dist/api/context-types.js +3 -0
- package/dist/api/context.d.ts +31 -0
- package/dist/api/context.js +705 -0
- package/dist/api/dormant.d.ts +30 -0
- package/dist/api/dormant.js +140 -0
- package/dist/api/drill-down.d.ts +84 -0
- package/dist/api/drill-down.js +123 -0
- package/dist/api/forget.d.ts +57 -0
- package/dist/api/forget.js +87 -0
- package/dist/api/goals.d.ts +18 -0
- package/dist/api/goals.js +33 -0
- package/dist/api/learn.d.ts +31 -0
- package/dist/api/learn.js +88 -0
- package/dist/api/outcome.d.ts +61 -0
- package/dist/api/outcome.js +66 -0
- package/dist/api/promote.d.ts +54 -0
- package/dist/api/promote.js +203 -0
- package/dist/api/quarantine.d.ts +24 -0
- package/dist/api/quarantine.js +121 -0
- package/dist/api/recall-types.d.ts +390 -0
- package/dist/api/recall-types.js +3 -0
- package/dist/api/recall.d.ts +36 -0
- package/dist/api/recall.js +634 -0
- package/dist/api/remember.d.ts +35 -0
- package/dist/api/remember.js +43 -0
- package/dist/api/sleep.d.ts +136 -0
- package/dist/api/sleep.js +271 -0
- package/dist/api/tokens.d.ts +26 -0
- package/dist/api/tokens.js +60 -0
- package/dist/api/types.d.ts +56 -0
- package/dist/api/types.js +38 -0
- package/dist/api.d.ts +20 -1262
- package/dist/api.js +25 -2727
- package/dist/audit.d.ts +3 -0
- package/dist/audit.js +6 -3
- package/dist/auth.d.ts +45 -4
- package/dist/auth.js +125 -48
- package/dist/autolearn.js +2 -2
- package/dist/capture/command.d.ts +33 -0
- package/dist/capture/command.js +264 -0
- package/dist/capture/compact.d.ts +44 -0
- package/dist/capture/compact.js +354 -0
- package/dist/capture/extract.d.ts +21 -0
- package/dist/capture/extract.js +464 -0
- package/dist/capture/transcript.d.ts +40 -0
- package/dist/capture/transcript.js +193 -0
- package/dist/capture-error.js +2 -1
- package/dist/churn-git.js +4 -2
- package/dist/cli/audit.d.ts +3 -0
- package/dist/cli/audit.js +159 -0
- package/dist/cli/auth.d.ts +2 -0
- package/dist/cli/auth.js +171 -0
- package/dist/cli/briefs.d.ts +4 -0
- package/dist/cli/briefs.js +435 -0
- package/dist/cli/card.d.ts +3 -0
- package/dist/cli/card.js +333 -0
- package/dist/cli/context.d.ts +15 -0
- package/dist/cli/context.js +366 -0
- package/dist/cli/continuity.d.ts +6 -0
- package/dist/cli/continuity.js +445 -0
- package/dist/cli/curate.d.ts +19 -0
- package/dist/cli/curate.js +555 -0
- package/dist/cli/dag.d.ts +5 -0
- package/dist/cli/dag.js +177 -0
- package/dist/cli/decisions.d.ts +4 -0
- package/dist/cli/decisions.js +528 -0
- package/dist/cli/eval.d.ts +5 -0
- package/dist/cli/eval.js +213 -0
- package/dist/cli/explain.d.ts +4 -0
- package/dist/cli/explain.js +150 -0
- package/dist/cli/goals.d.ts +2 -0
- package/dist/cli/goals.js +196 -0
- package/dist/cli/hook-blocks.d.ts +18 -0
- package/dist/cli/hook-blocks.js +233 -0
- package/dist/cli/init.d.ts +2 -0
- package/dist/cli/init.js +305 -0
- package/dist/cli/maintenance.d.ts +4 -0
- package/dist/cli/maintenance.js +179 -0
- package/dist/cli/playbooks.d.ts +4 -0
- package/dist/cli/playbooks.js +556 -0
- package/dist/cli/projects.js +1 -1
- package/dist/cli/recall.d.ts +7 -0
- package/dist/cli/recall.js +597 -0
- package/dist/cli/remember.d.ts +5 -0
- package/dist/cli/remember.js +442 -0
- package/dist/cli/serve.d.ts +5 -0
- package/dist/cli/serve.js +40 -0
- package/dist/cli/session-hooks.d.ts +29 -0
- package/dist/cli/session-hooks.js +637 -0
- package/dist/cli/setup.d.ts +4 -0
- package/dist/cli/setup.js +376 -0
- package/dist/cli/shared.d.ts +15 -19
- package/dist/cli/shared.js +50 -341
- package/dist/cli/slack.d.ts +2 -0
- package/dist/cli/slack.js +171 -0
- package/dist/cli/status.d.ts +18 -0
- package/dist/cli/status.js +400 -0
- package/dist/cli/transfer.d.ts +10 -0
- package/dist/cli/transfer.js +438 -0
- package/dist/cli/usage.d.ts +85 -0
- package/dist/cli/usage.js +741 -0
- package/dist/cli.d.ts +71 -120
- package/dist/cli.js +170 -8469
- package/dist/client.js +15 -8
- package/dist/compaction-record.js +6 -4
- package/dist/connectors/github/backfill.js +94 -87
- package/dist/connectors/github/cli-impl.js +4 -3
- package/dist/connectors/github/dlq.js +67 -54
- package/dist/connectors/github/ingest.js +34 -35
- package/dist/connectors/github/tenant-routing.js +3 -2
- package/dist/connectors/github/webhook.js +135 -216
- package/dist/connectors/slack/dlq.js +49 -61
- package/dist/connectors/slack/ingest.js +55 -48
- package/dist/connectors/slack/tenant-routing.js +4 -3
- package/dist/connectors/slack/webhook.js +72 -74
- package/dist/consolidate/conflicts.d.ts +10 -0
- package/dist/consolidate/conflicts.js +178 -0
- package/dist/consolidate/decay.d.ts +12 -0
- package/dist/consolidate/decay.js +145 -0
- package/dist/consolidate/llm-passes.d.ts +3 -0
- package/dist/consolidate/llm-passes.js +141 -0
- package/dist/consolidate/merge.d.ts +7 -0
- package/dist/consolidate/merge.js +251 -0
- package/dist/consolidate/physics-pass.d.ts +3 -0
- package/dist/consolidate/physics-pass.js +60 -0
- package/dist/consolidate/run.d.ts +69 -0
- package/dist/consolidate/run.js +76 -0
- package/dist/consolidate/sleep.d.ts +18 -0
- package/dist/consolidate/sleep.js +209 -0
- package/dist/consolidate/traces.d.ts +4 -0
- package/dist/consolidate/traces.js +178 -0
- package/dist/context-auto.js +7 -11
- package/dist/context-render.d.ts +1 -1
- package/dist/context-render.js +1 -1
- package/dist/customer-notes.d.ts +3 -0
- package/dist/customer-notes.js +6 -4
- package/dist/dag.js +6 -5
- package/dist/dashboard-actions.d.ts +20 -0
- package/dist/dashboard-actions.js +88 -0
- package/dist/dashboard-params.d.ts +45 -0
- package/dist/dashboard-params.js +127 -0
- package/dist/dashboard-queries.d.ts +15 -0
- package/dist/dashboard-queries.js +355 -0
- package/dist/dashboard-snapshot.d.ts +138 -0
- package/dist/dashboard-snapshot.js +308 -0
- package/dist/dashboard-types.d.ts +163 -0
- package/dist/dashboard-types.js +3 -0
- package/dist/dashboard.d.ts +6 -7
- package/dist/dashboard.js +228 -202
- package/dist/db/busy.d.ts +5 -0
- package/dist/db/busy.js +24 -0
- package/dist/db/continuity.d.ts +5 -0
- package/dist/db/continuity.js +145 -0
- package/dist/db/meta.d.ts +8 -0
- package/dist/db/meta.js +35 -0
- package/dist/db/migrate.d.ts +9 -0
- package/dist/db/migrate.js +138 -0
- package/dist/db/migrations/index.d.ts +5 -0
- package/dist/db/migrations/index.js +109 -0
- package/dist/db/migrations/types.d.ts +14 -0
- package/dist/db/migrations/types.js +2 -0
- package/dist/db/migrations/v01.d.ts +3 -0
- package/dist/db/migrations/v01.js +38 -0
- package/dist/db/migrations/v02.d.ts +3 -0
- package/dist/db/migrations/v02.js +21 -0
- package/dist/db/migrations/v03.d.ts +3 -0
- package/dist/db/migrations/v03.js +22 -0
- package/dist/db/migrations/v04.d.ts +3 -0
- package/dist/db/migrations/v04.js +28 -0
- package/dist/db/migrations/v05.d.ts +3 -0
- package/dist/db/migrations/v05.js +21 -0
- package/dist/db/migrations/v06.d.ts +3 -0
- package/dist/db/migrations/v06.js +25 -0
- package/dist/db/migrations/v07.d.ts +3 -0
- package/dist/db/migrations/v07.js +13 -0
- package/dist/db/migrations/v08.d.ts +3 -0
- package/dist/db/migrations/v08.js +8 -0
- package/dist/db/migrations/v09.d.ts +3 -0
- package/dist/db/migrations/v09.js +13 -0
- package/dist/db/migrations/v10.d.ts +3 -0
- package/dist/db/migrations/v10.js +17 -0
- package/dist/db/migrations/v11.d.ts +3 -0
- package/dist/db/migrations/v11.js +15 -0
- package/dist/db/migrations/v12.d.ts +3 -0
- package/dist/db/migrations/v12.js +11 -0
- package/dist/db/migrations/v13.d.ts +3 -0
- package/dist/db/migrations/v13.js +15 -0
- package/dist/db/migrations/v14.d.ts +3 -0
- package/dist/db/migrations/v14.js +66 -0
- package/dist/db/migrations/v15.d.ts +3 -0
- package/dist/db/migrations/v15.js +42 -0
- package/dist/db/migrations/v16.d.ts +3 -0
- package/dist/db/migrations/v16.js +61 -0
- package/dist/db/migrations/v17.d.ts +3 -0
- package/dist/db/migrations/v17.js +46 -0
- package/dist/db/migrations/v18.d.ts +3 -0
- package/dist/db/migrations/v18.js +60 -0
- package/dist/db/migrations/v19.d.ts +3 -0
- package/dist/db/migrations/v19.js +28 -0
- package/dist/db/migrations/v20.d.ts +3 -0
- package/dist/db/migrations/v20.js +42 -0
- package/dist/db/migrations/v21.d.ts +3 -0
- package/dist/db/migrations/v21.js +16 -0
- package/dist/db/migrations/v22.d.ts +3 -0
- package/dist/db/migrations/v22.js +82 -0
- package/dist/db/migrations/v23.d.ts +3 -0
- package/dist/db/migrations/v23.js +48 -0
- package/dist/db/migrations/v24.d.ts +3 -0
- package/dist/db/migrations/v24.js +72 -0
- package/dist/db/migrations/v25.d.ts +3 -0
- package/dist/db/migrations/v25.js +46 -0
- package/dist/db/migrations/v26.d.ts +3 -0
- package/dist/db/migrations/v26.js +23 -0
- package/dist/db/migrations/v27.d.ts +3 -0
- package/dist/db/migrations/v27.js +57 -0
- package/dist/db/migrations/v28.d.ts +3 -0
- package/dist/db/migrations/v28.js +38 -0
- package/dist/db/migrations/v29.d.ts +3 -0
- package/dist/db/migrations/v29.js +78 -0
- package/dist/db/migrations/v30.d.ts +3 -0
- package/dist/db/migrations/v30.js +92 -0
- package/dist/db/migrations/v31.d.ts +3 -0
- package/dist/db/migrations/v31.js +74 -0
- package/dist/db/migrations/v32.d.ts +3 -0
- package/dist/db/migrations/v32.js +102 -0
- package/dist/db/migrations/v33.d.ts +3 -0
- package/dist/db/migrations/v33.js +104 -0
- package/dist/db/migrations/v34.d.ts +3 -0
- package/dist/db/migrations/v34.js +93 -0
- package/dist/db/migrations/v35.d.ts +3 -0
- package/dist/db/migrations/v35.js +98 -0
- package/dist/db/migrations/v36.d.ts +3 -0
- package/dist/db/migrations/v36.js +98 -0
- package/dist/db/migrations/v37.d.ts +3 -0
- package/dist/db/migrations/v37.js +219 -0
- package/dist/db/migrations/v38.d.ts +3 -0
- package/dist/db/migrations/v38.js +277 -0
- package/dist/db/migrations/v39.d.ts +3 -0
- package/dist/db/migrations/v39.js +59 -0
- package/dist/db/migrations/v40.d.ts +3 -0
- package/dist/db/migrations/v40.js +74 -0
- package/dist/db/migrations/v41.d.ts +3 -0
- package/dist/db/migrations/v41.js +43 -0
- package/dist/db/migrations/v42.d.ts +3 -0
- package/dist/db/migrations/v42.js +41 -0
- package/dist/db/migrations/v43.d.ts +3 -0
- package/dist/db/migrations/v43.js +67 -0
- package/dist/db/migrations/v44.d.ts +3 -0
- package/dist/db/migrations/v44.js +28 -0
- package/dist/db/migrations/v45.d.ts +3 -0
- package/dist/db/migrations/v45.js +30 -0
- package/dist/db/migrations/v46.d.ts +3 -0
- package/dist/db/migrations/v46.js +25 -0
- package/dist/db/migrations/v47.d.ts +3 -0
- package/dist/db/migrations/v47.js +17 -0
- package/dist/db/migrations/v48.d.ts +3 -0
- package/dist/db/migrations/v48.js +10 -0
- package/dist/db/migrations/v49.d.ts +3 -0
- package/dist/db/migrations/v49.js +31 -0
- package/dist/db/migrations/v50.d.ts +3 -0
- package/dist/db/migrations/v50.js +67 -0
- package/dist/db/migrations/v51.d.ts +3 -0
- package/dist/db/migrations/v51.js +14 -0
- package/dist/db/migrations/v52.d.ts +3 -0
- package/dist/db/migrations/v52.js +7 -0
- package/dist/db/open.d.ts +23 -0
- package/dist/db/open.js +146 -0
- package/dist/db/sqlite.d.ts +21 -0
- package/dist/db/sqlite.js +8 -0
- package/dist/db/tables.d.ts +7 -0
- package/dist/db/tables.js +38 -0
- package/dist/db.d.ts +6 -46
- package/dist/db.js +5 -3049
- package/dist/decisions.d.ts +4 -1
- package/dist/decisions.js +9 -7
- package/dist/dedupe.js +3 -2
- package/dist/delivery-recorder.js +4 -1
- package/dist/doctor.js +3 -3
- package/dist/embedding-provider.d.ts +1 -1
- package/dist/embedding-provider.js +4 -3
- package/dist/embeddings.d.ts +9 -52
- package/dist/embeddings.js +43 -297
- package/dist/env.d.ts +75 -0
- package/dist/env.js +119 -0
- package/dist/eval-suite.js +1 -1
- package/dist/eval.js +2 -2
- package/dist/extract.js +1 -1
- package/dist/gated-write.js +3 -1
- package/dist/goals.d.ts +3 -1
- package/dist/goals.js +18 -0
- package/dist/graph/read.d.ts +73 -0
- package/dist/graph/read.js +325 -0
- package/dist/graph/rows.d.ts +45 -0
- package/dist/graph/rows.js +51 -0
- package/dist/graph/types.d.ts +83 -0
- package/dist/graph/types.js +11 -0
- package/dist/graph/write.d.ts +93 -0
- package/dist/{graph.js → graph/write.js} +6 -384
- package/dist/graph-extract.js +2 -1
- package/dist/graph-recall.d.ts +1 -1
- package/dist/graph-recall.js +2 -2
- package/dist/graph-stream.js +1 -1
- package/dist/graph-view.d.ts +1 -1
- package/dist/graph-view.js +1 -1
- package/dist/half-life-migration.d.ts +1 -1
- package/dist/half-life-migration.js +2 -1
- package/dist/hooks/codex-session.d.ts +8 -0
- package/dist/hooks/codex-session.js +76 -0
- package/dist/hooks/codex-wrapper.d.ts +55 -0
- package/dist/hooks/codex-wrapper.js +288 -0
- package/dist/hooks/json-hooks.d.ts +63 -0
- package/dist/hooks/json-hooks.js +356 -0
- package/dist/hooks/opencode.d.ts +50 -0
- package/dist/hooks/opencode.js +202 -0
- package/dist/hooks/shared.d.ts +54 -0
- package/dist/hooks/shared.js +77 -0
- package/dist/http-retry.d.ts +2 -0
- package/dist/http-retry.js +4 -3
- package/dist/http-util.d.ts +3 -0
- package/dist/http-util.js +10 -0
- package/dist/{importers.d.ts → importers/core.d.ts} +13 -17
- package/dist/importers/core.js +141 -0
- package/dist/importers/markdown-parse.d.ts +41 -0
- package/dist/importers/markdown-parse.js +132 -0
- package/dist/importers/markdown.d.ts +3 -0
- package/dist/importers/markdown.js +92 -0
- package/dist/importers/sources.d.ts +7 -0
- package/dist/importers/sources.js +229 -0
- package/dist/importers/vault.d.ts +11 -0
- package/dist/importers/vault.js +352 -0
- package/dist/incidents.d.ts +3 -0
- package/dist/incidents.js +7 -5
- package/dist/index.d.ts +25 -6
- package/dist/index.js +23 -6
- package/dist/invalidation.js +2 -1
- package/dist/judgment.js +2 -1
- package/dist/keyset.d.ts +13 -0
- package/dist/keyset.js +8 -0
- package/dist/local-embedding.d.ts +13 -0
- package/dist/local-embedding.js +165 -0
- package/dist/log.d.ts +7 -0
- package/dist/log.js +19 -1
- package/dist/mcp/admin-tools.d.ts +8 -0
- package/dist/mcp/admin-tools.js +116 -0
- package/dist/mcp/format.d.ts +27 -0
- package/dist/mcp/format.js +135 -0
- package/dist/mcp/memory-tools.d.ts +5 -0
- package/dist/mcp/memory-tools.js +83 -0
- package/dist/mcp/protocol.d.ts +83 -0
- package/dist/mcp/protocol.js +55 -0
- package/dist/mcp/recall-tools.d.ts +6 -0
- package/dist/mcp/recall-tools.js +320 -0
- package/dist/mcp/request.d.ts +10 -0
- package/dist/mcp/request.js +163 -0
- package/dist/mcp/server.d.ts +4 -75
- package/dist/mcp/server.js +7 -1190
- package/dist/mcp/session-state.d.ts +11 -0
- package/dist/mcp/session-state.js +28 -0
- package/dist/mcp/stdio.d.ts +8 -0
- package/dist/mcp/stdio.js +79 -0
- package/dist/mcp/tools.d.ts +11 -0
- package/dist/mcp/tools.js +247 -0
- package/dist/memory.d.ts +3 -0
- package/dist/memory.js +27 -1
- package/dist/multihop.d.ts +1 -1
- package/dist/multihop.js +2 -1
- package/dist/owner-validation.js +2 -1
- package/dist/physics-state.js +10 -7
- package/dist/policies.d.ts +3 -0
- package/dist/policies.js +8 -6
- package/dist/postinstall.js +3 -2
- package/dist/predictions/planning-fallacy.d.ts +100 -0
- package/dist/predictions/planning-fallacy.js +190 -0
- package/dist/{predictions.d.ts → predictions/store.d.ts} +7 -102
- package/dist/predictions/store.js +434 -0
- package/dist/processes.d.ts +3 -0
- package/dist/processes.js +7 -5
- package/dist/project-briefs.d.ts +3 -0
- package/dist/project-briefs.js +7 -5
- package/dist/project-identity.js +3 -2
- package/dist/project-merge.js +3 -1
- package/dist/quarantine.d.ts +2 -1
- package/dist/quarantine.js +8 -5
- package/dist/raw-archive.js +1 -1
- package/dist/recall-history.js +3 -2
- package/dist/recall-pipeline.d.ts +2 -2
- package/dist/recall-pipeline.js +16 -8
- package/dist/recall-scope.js +1 -1
- package/dist/recall-trace.d.ts +1 -1
- package/dist/refine-llm.js +2 -1
- package/dist/reject-flow.js +6 -1
- package/dist/rerankers/clef.js +6 -5
- package/dist/rerankers/jev.d.ts +1 -1
- package/dist/rerankers/jev.js +4 -4
- package/dist/rerankers/llm.d.ts +4 -2
- package/dist/rerankers/llm.js +58 -38
- package/dist/rerankers/types.d.ts +1 -1
- package/dist/salience.js +1 -1
- package/dist/scheduler.d.ts +1 -0
- package/dist/scheduler.js +26 -4
- package/dist/scope.js +4 -3
- package/dist/search/as-of.d.ts +10 -0
- package/dist/search/as-of.js +22 -0
- package/dist/search/bm25-search.d.ts +14 -0
- package/dist/search/bm25-search.js +43 -0
- package/dist/search/bm25.d.ts +15 -0
- package/dist/search/bm25.js +54 -0
- package/dist/search/boosts.d.ts +54 -0
- package/dist/search/boosts.js +94 -0
- package/dist/search/breakdown.d.ts +7 -0
- package/dist/search/breakdown.js +20 -0
- package/dist/search/explain.d.ts +25 -0
- package/dist/search/explain.js +31 -0
- package/dist/search/finalize.d.ts +8 -0
- package/dist/search/finalize.js +52 -0
- package/dist/search/fusion.d.ts +27 -0
- package/dist/search/fusion.js +42 -0
- package/dist/search/hybrid-score.d.ts +20 -0
- package/dist/search/hybrid-score.js +73 -0
- package/dist/search/hybrid.d.ts +46 -0
- package/dist/search/hybrid.js +64 -0
- package/dist/search/physics-search.d.ts +29 -0
- package/dist/search/physics-search.js +162 -0
- package/dist/search/rerank.d.ts +10 -0
- package/dist/search/rerank.js +72 -0
- package/dist/search/temporal.d.ts +15 -0
- package/dist/search/temporal.js +45 -0
- package/dist/search/types.d.ts +91 -0
- package/dist/search/types.js +2 -0
- package/dist/search/vector.d.ts +30 -0
- package/dist/search/vector.js +71 -0
- package/dist/secret-detect.d.ts +2 -0
- package/dist/secret-detect.js +2 -1
- package/dist/server/auth.d.ts +52 -0
- package/dist/server/auth.js +221 -0
- package/dist/server/client-ip.d.ts +25 -0
- package/dist/server/client-ip.js +92 -0
- package/dist/server/cursor.d.ts +23 -0
- package/dist/server/cursor.js +58 -0
- package/dist/server/lifecycle.d.ts +7 -0
- package/dist/server/lifecycle.js +29 -0
- package/dist/server/mcp-http.d.ts +5 -0
- package/dist/server/mcp-http.js +199 -0
- package/dist/server/request.d.ts +33 -0
- package/dist/server/request.js +103 -0
- package/dist/server/routes/admin.d.ts +9 -0
- package/dist/server/routes/admin.js +158 -0
- package/dist/server/routes/customer-notes.d.ts +7 -0
- package/dist/server/routes/customer-notes.js +112 -0
- package/dist/server/routes/decisions.d.ts +7 -0
- package/dist/server/routes/decisions.js +133 -0
- package/dist/server/routes/incidents.d.ts +7 -0
- package/dist/server/routes/incidents.js +126 -0
- package/dist/server/routes/memories.d.ts +10 -0
- package/dist/server/routes/memories.js +179 -0
- package/dist/server/routes/policies.d.ts +8 -0
- package/dist/server/routes/policies.js +151 -0
- package/dist/server/routes/predictions.d.ts +7 -0
- package/dist/server/routes/predictions.js +164 -0
- package/dist/server/routes/processes.d.ts +7 -0
- package/dist/server/routes/processes.js +161 -0
- package/dist/server/routes/project-briefs.d.ts +8 -0
- package/dist/server/routes/project-briefs.js +136 -0
- package/dist/server/routes/recall.d.ts +8 -0
- package/dist/server/routes/recall.js +340 -0
- package/dist/server/routes/skills.d.ts +8 -0
- package/dist/server/routes/skills.js +150 -0
- package/dist/server/types.d.ts +56 -0
- package/dist/server/types.js +2 -0
- package/dist/server/validation.d.ts +14 -0
- package/dist/server/validation.js +91 -0
- package/dist/server.d.ts +7 -61
- package/dist/server.js +69 -2362
- package/dist/session-digest.d.ts +1 -1
- package/dist/session-digest.js +9 -2
- package/dist/shared.d.ts +3 -1
- package/dist/shared.js +26 -22
- package/dist/skills.d.ts +3 -0
- package/dist/skills.js +7 -5
- package/dist/stdin.js +2 -2
- package/dist/store/audit-event.d.ts +19 -0
- package/dist/store/audit-event.js +33 -0
- package/dist/store/candidates.d.ts +42 -0
- package/dist/store/candidates.js +152 -0
- package/dist/store/conflicts.d.ts +44 -0
- package/dist/store/conflicts.js +444 -0
- package/dist/store/delete-and-batch.d.ts +63 -0
- package/dist/store/delete-and-batch.js +310 -0
- package/dist/store/entry-reads.d.ts +92 -0
- package/dist/store/entry-reads.js +255 -0
- package/dist/store/entry-row.d.ts +67 -0
- package/dist/store/entry-row.js +208 -0
- package/dist/store/entry-writes.d.ts +46 -0
- package/dist/store/entry-writes.js +148 -0
- package/dist/store/handoffs.d.ts +26 -0
- package/dist/store/handoffs.js +184 -0
- package/dist/store/index-and-stats.d.ts +52 -0
- package/dist/store/index-and-stats.js +214 -0
- package/dist/store/markdown.d.ts +10 -0
- package/dist/store/markdown.js +108 -0
- package/dist/store/mirrors.d.ts +49 -0
- package/dist/store/mirrors.js +312 -0
- package/dist/store/open.d.ts +15 -0
- package/dist/store/open.js +223 -0
- package/dist/store/rows.d.ts +178 -0
- package/dist/store/rows.js +158 -0
- package/dist/store/search-rows.d.ts +86 -0
- package/dist/store/search-rows.js +254 -0
- package/dist/store/sessions.d.ts +83 -0
- package/dist/store/sessions.js +272 -0
- package/dist/store/summaries.d.ts +94 -0
- package/dist/store/summaries.js +377 -0
- package/dist/store/tenant-lookup.d.ts +12 -0
- package/dist/store/tenant-lookup.js +16 -0
- package/dist/store-cards.js +2 -1
- package/dist/summary-dirty.d.ts +4 -0
- package/dist/summary-dirty.js +32 -0
- package/dist/support-bundle.js +4 -3
- package/dist/tenant.js +2 -2
- package/dist/tokenize.d.ts +2 -0
- package/dist/tokenize.js +16 -0
- package/dist/transcript-tail.d.ts +7 -0
- package/dist/transcript-tail.js +48 -0
- package/dist/vector-store.d.ts +27 -0
- package/dist/vector-store.js +210 -0
- package/dist/version.d.ts +2 -2
- package/dist/version.js +2 -2
- package/dist/working-memory.js +1 -1
- package/dist/yaml.js +36 -11
- package/dist-ui/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
- package/dist-ui/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
- package/dist-ui/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
- package/dist-ui/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
- package/dist-ui/assets/ibm-plex-mono-latin-600-normal-BgSNZQsw.woff2 +0 -0
- package/dist-ui/assets/ibm-plex-mono-latin-600-normal-DWFSQ4vo.woff +0 -0
- package/dist-ui/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
- package/dist-ui/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
- package/dist-ui/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
- package/dist-ui/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
- package/dist-ui/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
- package/dist-ui/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
- package/dist-ui/assets/index-DPN7cP19.js +33 -0
- package/dist-ui/assets/index-dFloKRVr.css +1 -0
- package/dist-ui/index.html +3 -25
- package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
- package/extensions/openclaw-plugin/package.json +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/dist/capture.d.ts +0 -155
- package/dist/capture.js +0 -1295
- package/dist/consolidate.d.ts +0 -58
- package/dist/consolidate.js +0 -1124
- package/dist/graph.d.ts +0 -245
- package/dist/hooks.d.ts +0 -208
- package/dist/hooks.js +0 -1076
- package/dist/importers.js +0 -900
- package/dist/predictions.js +0 -620
- package/dist/search.d.ts +0 -320
- package/dist/search.js +0 -970
- package/dist/store.d.ts +0 -776
- package/dist/store.js +0 -3473
- package/dist-ui/assets/d3-BiWEKnn4.js +0 -1
- package/dist-ui/assets/index-BhT8RvO6.js +0 -61
- package/dist-ui/assets/index-RoXXJ5dq.css +0 -1
- package/dist-ui/assets/three-BDgTxR1l.js +0 -4112
package/dist/dashboard.js
CHANGED
|
@@ -1,124 +1,19 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Hippo Dashboard — local web UI for memory health visualization.
|
|
3
|
-
* Serves a single-page app with memory stats, decay curves, and conflict status.
|
|
4
|
-
*
|
|
5
|
-
* Usage: hippo dashboard [--port 3333]
|
|
6
|
-
*/
|
|
1
|
+
/** Hippo dashboard server: the React bundle plus the JSON API behind the Health view and the Board (`hippo dashboard --port`). */
|
|
7
2
|
import * as http from 'http';
|
|
8
|
-
import * as os from 'os';
|
|
9
3
|
import * as path from 'path';
|
|
10
4
|
import * as fs from 'fs';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
5
|
+
import { randomBytes, timingSafeEqual } from 'crypto';
|
|
6
|
+
import { evalNow } from './ablation.js';
|
|
7
|
+
import { readEntry } from './store/entry-reads.js';
|
|
13
8
|
import { listCards } from './store-cards.js';
|
|
14
|
-
import { calculateStrength, confidenceFacets } from './memory.js';
|
|
15
|
-
import { loadConfig } from './config.js';
|
|
16
|
-
import { listPeers } from './shared.js';
|
|
17
|
-
import { loadEmbeddingIndex } from './embeddings.js';
|
|
18
9
|
import { resolveTenantId } from './tenant.js';
|
|
19
10
|
import { loadCardDetail } from './card-detail.js';
|
|
20
|
-
import { isCrossSite, LOOPBACK_HOST_HEADER } from './
|
|
11
|
+
import { isCrossSite, LOOPBACK_HOST_HEADER } from './http-util.js';
|
|
21
12
|
import { log } from './log.js';
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const entries = loadAllEntries(hippoRoot, tenantId);
|
|
27
|
-
const now = new Date();
|
|
28
|
-
const config = loadConfig(hippoRoot);
|
|
29
|
-
// v0.28 — fetch ALL conflict statuses (open + resolved) so the UI can
|
|
30
|
-
// render resolved conflicts as faded historical context, not just open
|
|
31
|
-
// conflicts. The open_conflicts stat below still counts only 'open' rows
|
|
32
|
-
// to preserve the existing badge meaning.
|
|
33
|
-
const conflicts = listMemoryConflicts(hippoRoot, '*', tenantId);
|
|
34
|
-
// D4 v1.12.10: tenant-scope peer discovery in the dashboard (matches the
|
|
35
|
-
// tenantId already used for loadAllEntries on line 72).
|
|
36
|
-
const peers = listPeers(undefined, tenantId);
|
|
37
|
-
const embeddingIndex = loadEmbeddingIndex(hippoRoot);
|
|
38
|
-
const embeddedCount = Object.keys(embeddingIndex).length;
|
|
39
|
-
let totalStrength = 0;
|
|
40
|
-
let totalHalfLife = 0;
|
|
41
|
-
let atRisk = 0;
|
|
42
|
-
let agedOut = 0;
|
|
43
|
-
const byLayer = {};
|
|
44
|
-
const byConfidence = {};
|
|
45
|
-
const memories = entries.map((e) => {
|
|
46
|
-
const strength = calculateStrength(e, now);
|
|
47
|
-
const facets = confidenceFacets(e, now);
|
|
48
|
-
const confidence = facets.tier;
|
|
49
|
-
const ageDays = (now.getTime() - new Date(e.created).getTime()) / (1000 * 60 * 60 * 24);
|
|
50
|
-
totalStrength += strength;
|
|
51
|
-
totalHalfLife += e.half_life_days;
|
|
52
|
-
if (strength < 0.1 && !e.pinned)
|
|
53
|
-
atRisk++;
|
|
54
|
-
byLayer[e.layer] = (byLayer[e.layer] ?? 0) + 1;
|
|
55
|
-
byConfidence[confidence] = (byConfidence[confidence] ?? 0) + 1;
|
|
56
|
-
if (facets.agedOut)
|
|
57
|
-
agedOut++;
|
|
58
|
-
// Project strength at +7d and +30d
|
|
59
|
-
const future7 = { ...e, last_retrieved: e.last_retrieved };
|
|
60
|
-
const future30 = { ...e, last_retrieved: e.last_retrieved };
|
|
61
|
-
const in7d = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);
|
|
62
|
-
const in30d = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000);
|
|
63
|
-
return {
|
|
64
|
-
id: e.id,
|
|
65
|
-
content: e.content,
|
|
66
|
-
tags: e.tags,
|
|
67
|
-
layer: e.layer,
|
|
68
|
-
strength,
|
|
69
|
-
half_life_days: e.half_life_days,
|
|
70
|
-
retrieval_count: e.retrieval_count,
|
|
71
|
-
schema_fit: e.schema_fit,
|
|
72
|
-
emotional_valence: e.emotional_valence,
|
|
73
|
-
confidence,
|
|
74
|
-
aged_out: facets.agedOut,
|
|
75
|
-
pinned: e.pinned,
|
|
76
|
-
created: e.created,
|
|
77
|
-
last_retrieved: e.last_retrieved,
|
|
78
|
-
age_days: Math.round(ageDays * 10) / 10,
|
|
79
|
-
projected_strength_7d: calculateStrength(future7, in7d),
|
|
80
|
-
projected_strength_30d: calculateStrength(future30, in30d),
|
|
81
|
-
parents: e.parents ?? [],
|
|
82
|
-
starred: Boolean(e.starred),
|
|
83
|
-
conflicts_with: e.conflicts_with ?? [],
|
|
84
|
-
};
|
|
85
|
-
});
|
|
86
|
-
return {
|
|
87
|
-
memories,
|
|
88
|
-
conflicts: conflicts.map((c) => ({
|
|
89
|
-
id: c.id,
|
|
90
|
-
memory_a_id: c.memory_a_id,
|
|
91
|
-
memory_b_id: c.memory_b_id,
|
|
92
|
-
reason: c.reason,
|
|
93
|
-
score: c.score,
|
|
94
|
-
status: c.status,
|
|
95
|
-
})),
|
|
96
|
-
stats: {
|
|
97
|
-
total: entries.length,
|
|
98
|
-
pinned: entries.filter((e) => e.pinned).length,
|
|
99
|
-
errors: entries.filter((e) => e.tags.includes('error')).length,
|
|
100
|
-
at_risk: atRisk,
|
|
101
|
-
avg_strength: entries.length > 0 ? totalStrength / entries.length : 0,
|
|
102
|
-
avg_half_life: entries.length > 0 ? totalHalfLife / entries.length : 0,
|
|
103
|
-
by_layer: byLayer,
|
|
104
|
-
by_confidence: byConfidence,
|
|
105
|
-
aged_out: agedOut,
|
|
106
|
-
embedding_coverage: entries.length > 0 ? embeddedCount / entries.length : 0,
|
|
107
|
-
// v0.28 — open_conflicts must count only 'open' rows now that
|
|
108
|
-
// `conflicts` includes all statuses. Preserves the existing badge
|
|
109
|
-
// meaning (plan-eng-critic R1 must-fix #2).
|
|
110
|
-
open_conflicts: conflicts.filter((c) => c.status === 'open').length,
|
|
111
|
-
},
|
|
112
|
-
peers,
|
|
113
|
-
config: {
|
|
114
|
-
defaultHalfLifeDays: config.defaultHalfLifeDays,
|
|
115
|
-
defaultBudget: config.defaultBudget,
|
|
116
|
-
embeddingsEnabled: config.embeddings.enabled,
|
|
117
|
-
// Every memory captured in the home folder carries these, so they name no project.
|
|
118
|
-
homePathTags: extractPathTags(os.homedir()),
|
|
119
|
-
},
|
|
120
|
-
};
|
|
121
|
-
}
|
|
13
|
+
import { createSnapshotService, isLiveMemory } from './dashboard-snapshot.js';
|
|
14
|
+
import { buildMemoryDetail, buildMemoryPage, buildOverview, buildProjectDetail, buildSearch, } from './dashboard-queries.js';
|
|
15
|
+
import { forgetMemory, markWrong, pinMemory, resolveOpenConflict, } from './dashboard-actions.js';
|
|
16
|
+
import { ParamError, parseActionBody, parseConflictId, parseMemoryId, parseMemoryQuery, parseSearchText, } from './dashboard-params.js';
|
|
122
17
|
const MIME_TYPES = {
|
|
123
18
|
'.html': 'text/html; charset=utf-8',
|
|
124
19
|
'.js': 'application/javascript; charset=utf-8',
|
|
@@ -127,8 +22,13 @@ const MIME_TYPES = {
|
|
|
127
22
|
'.png': 'image/png',
|
|
128
23
|
'.svg': 'image/svg+xml',
|
|
129
24
|
'.ico': 'image/x-icon',
|
|
25
|
+
'.woff': 'font/woff',
|
|
130
26
|
'.woff2': 'font/woff2',
|
|
131
27
|
};
|
|
28
|
+
const BODY_MAX_BYTES = 4096;
|
|
29
|
+
const BODY_DRAIN_MAX_BYTES = 64 * 1024;
|
|
30
|
+
const JSON_CONTENT_TYPE = /^application\/json\s*(;|$)/i;
|
|
31
|
+
const CARD_ID = /^[A-Za-z0-9_-]+$/;
|
|
132
32
|
function isStaticFileExtension(ext) {
|
|
133
33
|
return Object.hasOwn(MIME_TYPES, ext);
|
|
134
34
|
}
|
|
@@ -139,6 +39,13 @@ function jsonResponse(res, data, status = 200) {
|
|
|
139
39
|
});
|
|
140
40
|
res.end(body);
|
|
141
41
|
}
|
|
42
|
+
function forbidden(res) {
|
|
43
|
+
res.writeHead(403, { 'Content-Type': 'text/plain' });
|
|
44
|
+
res.end('Forbidden');
|
|
45
|
+
}
|
|
46
|
+
function notFoundJson(res) {
|
|
47
|
+
jsonResponse(res, { error: 'Not found' }, 404);
|
|
48
|
+
}
|
|
142
49
|
function serveStaticFile(res, filePath) {
|
|
143
50
|
const ext = path.extname(filePath).toLowerCase();
|
|
144
51
|
if (!isStaticFileExtension(ext))
|
|
@@ -156,87 +63,129 @@ function serveStaticFile(res, filePath) {
|
|
|
156
63
|
return false; // an unreadable asset falls through to the next route, which answers 404 or the fallback page
|
|
157
64
|
}
|
|
158
65
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
if (starMatch && req.method === 'POST') {
|
|
176
|
-
if (isCrossSite(req)) {
|
|
177
|
-
res.writeHead(403, { 'Content-Type': 'text/plain' });
|
|
178
|
-
res.end('Forbidden');
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
const id = starMatch[1];
|
|
182
|
-
const entry = readEntry(hippoRoot, id, resolveTenantId({}));
|
|
183
|
-
if (!entry)
|
|
184
|
-
return jsonResponse(res, { error: 'Not found' }, 404);
|
|
185
|
-
entry.starred = !entry.starred;
|
|
186
|
-
writeEntry(hippoRoot, entry);
|
|
187
|
-
return jsonResponse(res, { id, starred: entry.starred });
|
|
188
|
-
}
|
|
189
|
-
if (pathname === '/api/cards' && req.method === 'GET') {
|
|
190
|
-
return jsonResponse(res, { cards: listCards(hippoRoot, resolveTenantId({})) });
|
|
191
|
-
}
|
|
192
|
-
const cardMatch = pathname.match(/^\/api\/cards\/([A-Za-z0-9_-]+)$/);
|
|
193
|
-
if (cardMatch && req.method === 'GET') {
|
|
194
|
-
const detail = loadCardDetail(hippoRoot, resolveTenantId({}), cardMatch[1]);
|
|
195
|
-
return detail ? jsonResponse(res, detail) : jsonResponse(res, { error: 'Not found' }, 404);
|
|
196
|
-
}
|
|
197
|
-
const data = buildDashboardData(hippoRoot);
|
|
198
|
-
if (pathname === '/api/memories')
|
|
199
|
-
return jsonResponse(res, data.memories);
|
|
200
|
-
if (pathname === '/api/stats')
|
|
201
|
-
return jsonResponse(res, data.stats);
|
|
202
|
-
if (pathname === '/api/conflicts')
|
|
203
|
-
return jsonResponse(res, data.conflicts);
|
|
204
|
-
if (pathname === '/api/peers')
|
|
205
|
-
return jsonResponse(res, data.peers);
|
|
206
|
-
if (pathname === '/api/config')
|
|
207
|
-
return jsonResponse(res, data.config);
|
|
208
|
-
if (pathname === '/api/embeddings') {
|
|
209
|
-
const index = loadEmbeddingIndex(hippoRoot);
|
|
210
|
-
return jsonResponse(res, index);
|
|
66
|
+
/** Raised once a body passes the drain ceiling; the caller answers 400 and drops the socket. */
|
|
67
|
+
class BodyDrainExceeded extends ParamError {
|
|
68
|
+
}
|
|
69
|
+
// Past the cap it keeps draining so the socket stays usable, up to a ceiling, then refuses.
|
|
70
|
+
function readActionBody(req) {
|
|
71
|
+
return new Promise((resolve, reject) => {
|
|
72
|
+
const chunks = [];
|
|
73
|
+
let size = 0;
|
|
74
|
+
let aborted = false;
|
|
75
|
+
req.on('data', (chunk) => {
|
|
76
|
+
size += chunk.length;
|
|
77
|
+
if (size <= BODY_MAX_BYTES)
|
|
78
|
+
chunks.push(chunk);
|
|
79
|
+
if (size > BODY_DRAIN_MAX_BYTES && !aborted) {
|
|
80
|
+
aborted = true;
|
|
81
|
+
reject(new BodyDrainExceeded(`Body must be at most ${BODY_DRAIN_MAX_BYTES} bytes`));
|
|
211
82
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
// Block traversal outside dist-ui
|
|
220
|
-
if (!filePath.startsWith(distUiDir)) {
|
|
221
|
-
res.writeHead(403, { 'Content-Type': 'text/plain' });
|
|
222
|
-
res.end('Forbidden');
|
|
223
|
-
return;
|
|
83
|
+
});
|
|
84
|
+
req.on('error', reject);
|
|
85
|
+
req.on('end', () => {
|
|
86
|
+
if (size > BODY_MAX_BYTES)
|
|
87
|
+
return reject(new ParamError(`Body must be at most ${BODY_MAX_BYTES} bytes`));
|
|
88
|
+
try {
|
|
89
|
+
resolve(parseActionBody(Buffer.concat(chunks).toString('utf8').trim()));
|
|
224
90
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
if (serveStaticFile(res, filePath))
|
|
228
|
-
return;
|
|
91
|
+
catch (err) {
|
|
92
|
+
reject(err);
|
|
229
93
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
function sendOrNotFound(res, body) {
|
|
98
|
+
if (body === null)
|
|
99
|
+
notFoundJson(res);
|
|
100
|
+
else
|
|
101
|
+
jsonResponse(res, body);
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
function handleRead(ctx, segments) {
|
|
105
|
+
const { hippoRoot, snapshots, req, res, url } = ctx;
|
|
106
|
+
const tenantId = resolveTenantId({});
|
|
107
|
+
// A cross-site <img src=...?fresh=1> must not force a full rebuild.
|
|
108
|
+
const fresh = url.searchParams.get('fresh') === '1' && !isCrossSite(req);
|
|
109
|
+
const [, head, a, b] = segments;
|
|
110
|
+
if (head === 'overview' && segments.length === 2) {
|
|
111
|
+
return sendOrNotFound(res, buildOverview(snapshots.get(tenantId, fresh)));
|
|
112
|
+
}
|
|
113
|
+
if (head === 'projects' && a !== undefined && segments.length === 4 && b === 'memories') {
|
|
114
|
+
const query = parseMemoryQuery(url.searchParams);
|
|
115
|
+
return sendOrNotFound(res, buildMemoryPage(snapshots.get(tenantId, fresh), a, query));
|
|
116
|
+
}
|
|
117
|
+
if (head === 'projects' && a !== undefined && segments.length === 3) {
|
|
118
|
+
return sendOrNotFound(res, buildProjectDetail(snapshots.get(tenantId, fresh), a));
|
|
119
|
+
}
|
|
120
|
+
if (head === 'memory' && a !== undefined && segments.length === 3) {
|
|
121
|
+
const entry = readEntry(hippoRoot, parseMemoryId(a), tenantId);
|
|
122
|
+
if (entry === null || !isLiveMemory(entry))
|
|
123
|
+
return sendOrNotFound(res, null);
|
|
124
|
+
const snapshotId = snapshots.get(tenantId, fresh).id;
|
|
125
|
+
const embedded = snapshots.embeddedIds()?.has(entry.id) ?? false;
|
|
126
|
+
return sendOrNotFound(res, buildMemoryDetail(hippoRoot, tenantId, entry, snapshotId, ctx.now(), embedded));
|
|
127
|
+
}
|
|
128
|
+
if (head === 'search' && segments.length === 2) {
|
|
129
|
+
return sendOrNotFound(res, buildSearch(snapshots.get(tenantId, fresh), parseSearchText(url.searchParams)));
|
|
130
|
+
}
|
|
131
|
+
if (head === 'cards' && segments.length === 2) {
|
|
132
|
+
return sendOrNotFound(res, { cards: listCards(hippoRoot, tenantId) });
|
|
133
|
+
}
|
|
134
|
+
if (head === 'cards' && a !== undefined && segments.length === 3) {
|
|
135
|
+
// The Board has always answered 404, not 400, for an id that does not fit the pattern.
|
|
136
|
+
return sendOrNotFound(res, CARD_ID.test(a) ? loadCardDetail(hippoRoot, tenantId, a) ?? null : null);
|
|
137
|
+
}
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
// Names the action only; nothing runs until the cross-site, content-type and body guards pass.
|
|
141
|
+
function matchAction(segments) {
|
|
142
|
+
const [, head, id, verb] = segments;
|
|
143
|
+
if (segments.length !== 4 || id === undefined)
|
|
144
|
+
return null;
|
|
145
|
+
if (head === 'memory' && verb === 'pin')
|
|
146
|
+
return (root, tenant, body) => pinMemory(root, tenant, id, body);
|
|
147
|
+
if (head === 'memory' && verb === 'wrong')
|
|
148
|
+
return (root, tenant) => markWrong(root, tenant, id);
|
|
149
|
+
if (head === 'memory' && verb === 'forget')
|
|
150
|
+
return (root, tenant) => forgetMemory(root, tenant, id);
|
|
151
|
+
if (head === 'conflicts' && verb === 'resolve') {
|
|
152
|
+
return (root, tenant, body) => resolveOpenConflict(root, tenant, parseConflictId(id), body);
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
async function handlePost(ctx, segments) {
|
|
157
|
+
const action = matchAction(segments);
|
|
158
|
+
if (action === null)
|
|
159
|
+
return false;
|
|
160
|
+
const { req, res, hippoRoot, snapshots } = ctx;
|
|
161
|
+
if (isCrossSite(req)) {
|
|
162
|
+
forbidden(res);
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
if (!JSON_CONTENT_TYPE.test(req.headers['content-type'] ?? '')) {
|
|
166
|
+
jsonResponse(res, { error: 'Content-Type must be application/json' }, 415);
|
|
167
|
+
req.resume();
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
const body = await readActionBody(req);
|
|
171
|
+
const tenantId = resolveTenantId({});
|
|
172
|
+
const result = action(hippoRoot, tenantId, body);
|
|
173
|
+
if (result.changed)
|
|
174
|
+
snapshots.invalidate();
|
|
175
|
+
if (result.entry) {
|
|
176
|
+
// The write is committed, so a rebuild failure must not turn it into a 500: the next read rebuilds.
|
|
177
|
+
const embedded = snapshots.embeddedIds()?.has(result.entry.id) ?? false;
|
|
178
|
+
const detail = buildMemoryDetail(hippoRoot, tenantId, result.entry, snapshots.currentId(), ctx.now(), embedded);
|
|
179
|
+
jsonResponse(res, detail, result.status);
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
jsonResponse(res, result.body, result.status);
|
|
183
|
+
}
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
function notBuilt(res) {
|
|
187
|
+
res.writeHead(404, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
188
|
+
res.end(`<!doctype html>
|
|
240
189
|
<html lang="en"><head><title>Hippo Dashboard</title><meta charset="utf-8"></head>
|
|
241
190
|
<body style="font-family:Georgia,'Palatino Linotype',serif;max-width:640px;margin:60px auto;padding:24px;line-height:1.6;background:#f4efe6;color:#3a3228">
|
|
242
191
|
<h1 style="color:#c45c3c">Hippo Dashboard</h1>
|
|
@@ -244,22 +193,99 @@ export function serveDashboard(hippoRoot, port = 3333) {
|
|
|
244
193
|
<pre style="background:#faf7f2;padding:16px;border:1px solid #c4b9a8;border-radius:3px;font-family:Consolas,monospace">cd ui && npm install && npm run build</pre>
|
|
245
194
|
<p>Then refresh this page. The dashboard server will serve <code>dist-ui/index.html</code> automatically once present.</p>
|
|
246
195
|
</body></html>`);
|
|
196
|
+
}
|
|
197
|
+
function serveSpa(res, distUiDir, pathname) {
|
|
198
|
+
const safePath = path.normalize(pathname).replace(/^(\.\.[/\\])+/, '');
|
|
199
|
+
const filePath = path.join(distUiDir, safePath);
|
|
200
|
+
if (!filePath.startsWith(distUiDir))
|
|
201
|
+
return forbidden(res);
|
|
202
|
+
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile() && serveStaticFile(res, filePath))
|
|
203
|
+
return;
|
|
204
|
+
// SPA fallback: non-file routes get index.html.
|
|
205
|
+
if (serveStaticFile(res, path.join(distUiDir, 'index.html')))
|
|
206
|
+
return;
|
|
207
|
+
notBuilt(res);
|
|
208
|
+
}
|
|
209
|
+
function sameToken(given, token) {
|
|
210
|
+
if (given === undefined)
|
|
211
|
+
return false;
|
|
212
|
+
const a = Buffer.from(given);
|
|
213
|
+
const b = Buffer.from(token);
|
|
214
|
+
return a.length === b.length && timingSafeEqual(a, b);
|
|
215
|
+
}
|
|
216
|
+
function cookieValue(header, name) {
|
|
217
|
+
for (const part of (header ?? '').split(';')) {
|
|
218
|
+
const eq = part.indexOf('=');
|
|
219
|
+
if (eq > 0 && part.slice(0, eq).trim() === name)
|
|
220
|
+
return part.slice(eq + 1).trim();
|
|
221
|
+
}
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
/** Serves the dashboard on 127.0.0.1 behind a per-start `token` (tests pass one), since loopback alone lets any local process read every memory; `opts` sets the projection and cache clocks. */
|
|
225
|
+
export function serveDashboard(hippoRoot, port = 3333, token = randomBytes(32).toString('base64url'), opts) {
|
|
226
|
+
const distUiDir = path.resolve(import.meta.dirname, '..', 'dist-ui');
|
|
227
|
+
const hasDistUi = fs.existsSync(path.join(distUiDir, 'index.html'));
|
|
228
|
+
const now = opts?.now ?? (() => evalNow().getTime());
|
|
229
|
+
const snapshots = createSnapshotService(hippoRoot, now, opts?.cacheClock);
|
|
230
|
+
const handleRequest = async (req, res) => {
|
|
231
|
+
const host = req.headers.host;
|
|
232
|
+
if (host !== undefined && !LOOPBACK_HOST_HEADER.test(host))
|
|
233
|
+
return forbidden(res);
|
|
234
|
+
const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);
|
|
235
|
+
// Cookies ignore the port, so the name carries it: two dashboards on one host keep separate tokens.
|
|
236
|
+
const cookieName = `hippo_dashboard_${req.socket.localPort ?? port}`;
|
|
237
|
+
if (sameToken(url.searchParams.get('token') ?? undefined, token)) {
|
|
238
|
+
res.setHeader('Set-Cookie', `${cookieName}=${token}; HttpOnly; SameSite=Strict; Path=/`);
|
|
239
|
+
}
|
|
240
|
+
else if (!sameToken(cookieValue(req.headers.cookie, cookieName), token)) {
|
|
241
|
+
res.writeHead(401, { 'Content-Type': 'text/plain' });
|
|
242
|
+
res.end('Unauthorized: open the dashboard with the URL `hippo dashboard` printed; it carries the access token.');
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
if (url.pathname.startsWith('/api/')) {
|
|
246
|
+
// A malformed % sequence throws URIError here; the caller answers 400.
|
|
247
|
+
const segments = url.pathname.split('/').filter((s) => s !== '').map((s) => decodeURIComponent(s));
|
|
248
|
+
const ctx = { hippoRoot, snapshots, now, req, res, url };
|
|
249
|
+
const handled = req.method === 'GET' ? handleRead(ctx, segments)
|
|
250
|
+
: req.method === 'POST' ? await handlePost(ctx, segments)
|
|
251
|
+
: false;
|
|
252
|
+
if (!handled)
|
|
253
|
+
notFoundJson(res);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (hasDistUi)
|
|
257
|
+
return serveSpa(res, distUiDir, url.pathname);
|
|
258
|
+
notBuilt(res);
|
|
247
259
|
};
|
|
248
260
|
const server = http.createServer((req, res) => {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
261
|
+
handleRequest(req, res).catch((err) => {
|
|
262
|
+
const clientFault = err instanceof ParamError || err instanceof URIError;
|
|
263
|
+
// A cut-short response is logged even for a client fault; a bare 400 is not.
|
|
264
|
+
if (res.headersSent || !clientFault) {
|
|
265
|
+
log.error('dashboard request failed', { error: err instanceof Error ? err.message : String(err), path: req.url });
|
|
266
|
+
}
|
|
267
|
+
if (res.headersSent) {
|
|
255
268
|
res.end();
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
if (err instanceof BodyDrainExceeded) {
|
|
272
|
+
res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8', Connection: 'close' });
|
|
273
|
+
res.end(JSON.stringify({ error: err.message }), () => req.destroy());
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
if (err instanceof ParamError)
|
|
277
|
+
return jsonResponse(res, { error: err.message }, 400);
|
|
278
|
+
if (err instanceof URIError)
|
|
279
|
+
return jsonResponse(res, { error: 'Malformed URL path' }, 400);
|
|
280
|
+
jsonResponse(res, { error: 'Internal error' }, 500);
|
|
281
|
+
});
|
|
259
282
|
});
|
|
283
|
+
server.on('close', () => snapshots.close());
|
|
260
284
|
server.listen(port, '127.0.0.1', () => {
|
|
285
|
+
// SAFETY: listen() was given a TCP port, so address() is AddressInfo, never a pipe name.
|
|
286
|
+
const boundPort = server.address().port;
|
|
261
287
|
// The banner is the `hippo dashboard` command's printed result, so it stays on stdout.
|
|
262
|
-
console.log(`Hippo Dashboard running at http://localhost:${
|
|
288
|
+
console.log(`Hippo Dashboard running at http://localhost:${boundPort}/?token=${token}`);
|
|
263
289
|
if (hasDistUi)
|
|
264
290
|
console.log(`Serving React UI from ${distUiDir}`);
|
|
265
291
|
console.log('Press Ctrl+C to stop.');
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { DatabaseSyncLike } from './sqlite.js';
|
|
2
|
+
export declare function isSqliteBusy(error: unknown): boolean;
|
|
3
|
+
export declare const STORE_BUSY_MESSAGE = "store busy (another hippo process holds the write lock); retry shortly";
|
|
4
|
+
export declare function execWithBusyRetry(db: DatabaseSyncLike, sql: string, timeoutMs?: number): void;
|
|
5
|
+
//# sourceMappingURL=busy.d.ts.map
|
package/dist/db/busy.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export function isSqliteBusy(error) {
|
|
2
|
+
const code = error?.errcode;
|
|
3
|
+
return code === 5 || code === 6 || code === 517;
|
|
4
|
+
}
|
|
5
|
+
export const STORE_BUSY_MESSAGE = 'store busy (another hippo process holds the write lock); retry shortly';
|
|
6
|
+
// busy_timeout covers neither of this file's two contended statements: SQLite
|
|
7
|
+
// skips the busy handler for `PRAGMA journal_mode` and for a write that upgrades
|
|
8
|
+
// a deferred read snapshot. Both need an explicit wait instead.
|
|
9
|
+
export function execWithBusyRetry(db, sql, timeoutMs = 30000) {
|
|
10
|
+
const deadline = Date.now() + timeoutMs;
|
|
11
|
+
const idle = new Int32Array(new SharedArrayBuffer(4));
|
|
12
|
+
for (;;) {
|
|
13
|
+
try {
|
|
14
|
+
db.exec(sql);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
if (!isSqliteBusy(error) || Date.now() >= deadline)
|
|
19
|
+
throw error;
|
|
20
|
+
Atomics.wait(idle, 0, 0, 10 + Math.floor(Math.random() * 20));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=busy.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { DatabaseSyncLike } from './sqlite.js';
|
|
2
|
+
export declare const MEMORY_QUARANTINE_DDL = "\n CREATE TABLE IF NOT EXISTS memory_quarantine (\n tenant_id TEXT NOT NULL DEFAULT 'default',\n memory_id TEXT NOT NULL,\n original_scope TEXT,\n reason TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','approved','rejected')),\n quarantined_at TEXT NOT NULL,\n decided_at TEXT,\n decided_by TEXT,\n PRIMARY KEY (tenant_id, memory_id)\n ) WITHOUT ROWID;\n CREATE INDEX IF NOT EXISTS idx_memory_quarantine_status\n ON memory_quarantine(tenant_id, status, quarantined_at DESC);\n";
|
|
3
|
+
export declare function ensureContinuityTables(db: DatabaseSyncLike): void;
|
|
4
|
+
export declare function ensureContinuityIndexes(db: DatabaseSyncLike): void;
|
|
5
|
+
//# sourceMappingURL=continuity.d.ts.map
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// Shared by migration v48 and the stamped-store self-heal below.
|
|
2
|
+
export const MEMORY_QUARANTINE_DDL = `
|
|
3
|
+
CREATE TABLE IF NOT EXISTS memory_quarantine (
|
|
4
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
5
|
+
memory_id TEXT NOT NULL,
|
|
6
|
+
original_scope TEXT,
|
|
7
|
+
reason TEXT NOT NULL,
|
|
8
|
+
status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','approved','rejected')),
|
|
9
|
+
quarantined_at TEXT NOT NULL,
|
|
10
|
+
decided_at TEXT,
|
|
11
|
+
decided_by TEXT,
|
|
12
|
+
PRIMARY KEY (tenant_id, memory_id)
|
|
13
|
+
) WITHOUT ROWID;
|
|
14
|
+
CREATE INDEX IF NOT EXISTS idx_memory_quarantine_status
|
|
15
|
+
ON memory_quarantine(tenant_id, status, quarantined_at DESC);
|
|
16
|
+
`;
|
|
17
|
+
const TASK_SNAPSHOTS_DDL = `
|
|
18
|
+
CREATE TABLE IF NOT EXISTS task_snapshots (
|
|
19
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
20
|
+
task TEXT NOT NULL,
|
|
21
|
+
summary TEXT NOT NULL,
|
|
22
|
+
next_step TEXT NOT NULL,
|
|
23
|
+
status TEXT NOT NULL,
|
|
24
|
+
source TEXT NOT NULL,
|
|
25
|
+
created_at TEXT NOT NULL,
|
|
26
|
+
updated_at TEXT NOT NULL,
|
|
27
|
+
session_id TEXT,
|
|
28
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
29
|
+
scope TEXT
|
|
30
|
+
)
|
|
31
|
+
`;
|
|
32
|
+
const SESSION_EVENTS_DDL = `
|
|
33
|
+
CREATE TABLE IF NOT EXISTS session_events (
|
|
34
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
35
|
+
session_id TEXT NOT NULL,
|
|
36
|
+
task TEXT,
|
|
37
|
+
event_type TEXT NOT NULL,
|
|
38
|
+
content TEXT NOT NULL,
|
|
39
|
+
source TEXT NOT NULL,
|
|
40
|
+
metadata_json TEXT NOT NULL,
|
|
41
|
+
created_at TEXT NOT NULL,
|
|
42
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
43
|
+
scope TEXT
|
|
44
|
+
)
|
|
45
|
+
`;
|
|
46
|
+
const SESSION_HANDOFFS_DDL = `
|
|
47
|
+
CREATE TABLE IF NOT EXISTS session_handoffs (
|
|
48
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
49
|
+
session_id TEXT NOT NULL,
|
|
50
|
+
repo_root TEXT,
|
|
51
|
+
task_id TEXT,
|
|
52
|
+
summary TEXT NOT NULL,
|
|
53
|
+
next_action TEXT,
|
|
54
|
+
artifacts_json TEXT NOT NULL DEFAULT '[]',
|
|
55
|
+
created_at TEXT NOT NULL,
|
|
56
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
57
|
+
scope TEXT,
|
|
58
|
+
constraints_json TEXT,
|
|
59
|
+
evidence_json TEXT,
|
|
60
|
+
outcome TEXT,
|
|
61
|
+
target_runtime TEXT,
|
|
62
|
+
card_id TEXT
|
|
63
|
+
)
|
|
64
|
+
`;
|
|
65
|
+
const CARDS_DDL = `
|
|
66
|
+
CREATE TABLE IF NOT EXISTS cards (
|
|
67
|
+
id TEXT PRIMARY KEY,
|
|
68
|
+
title TEXT NOT NULL,
|
|
69
|
+
status TEXT NOT NULL DEFAULT 'backlog',
|
|
70
|
+
assignee_runtime TEXT,
|
|
71
|
+
repo TEXT,
|
|
72
|
+
contract TEXT,
|
|
73
|
+
budget INTEGER,
|
|
74
|
+
lease_until TEXT,
|
|
75
|
+
heartbeat_at TEXT,
|
|
76
|
+
created_at TEXT NOT NULL,
|
|
77
|
+
updated_at TEXT NOT NULL,
|
|
78
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
79
|
+
scope TEXT
|
|
80
|
+
) WITHOUT ROWID
|
|
81
|
+
`;
|
|
82
|
+
const CARD_DEPS_DDL = `
|
|
83
|
+
CREATE TABLE IF NOT EXISTS card_deps (
|
|
84
|
+
parent TEXT NOT NULL REFERENCES cards(id),
|
|
85
|
+
child TEXT NOT NULL REFERENCES cards(id),
|
|
86
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
87
|
+
created_at TEXT NOT NULL,
|
|
88
|
+
PRIMARY KEY (parent, child)
|
|
89
|
+
) WITHOUT ROWID
|
|
90
|
+
`;
|
|
91
|
+
const CARD_RUNS_DDL = `
|
|
92
|
+
CREATE TABLE IF NOT EXISTS card_runs (
|
|
93
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
94
|
+
card TEXT NOT NULL REFERENCES cards(id),
|
|
95
|
+
runtime TEXT NOT NULL,
|
|
96
|
+
session_id TEXT,
|
|
97
|
+
started TEXT NOT NULL,
|
|
98
|
+
ended TEXT,
|
|
99
|
+
outcome TEXT,
|
|
100
|
+
created_at TEXT NOT NULL,
|
|
101
|
+
updated_at TEXT NOT NULL,
|
|
102
|
+
tenant_id TEXT NOT NULL DEFAULT 'default'
|
|
103
|
+
)
|
|
104
|
+
`;
|
|
105
|
+
const CARD_COMMENTS_DDL = `
|
|
106
|
+
CREATE TABLE IF NOT EXISTS card_comments (
|
|
107
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
108
|
+
card_id TEXT NOT NULL REFERENCES cards(id),
|
|
109
|
+
author TEXT NOT NULL,
|
|
110
|
+
body TEXT NOT NULL,
|
|
111
|
+
created_at TEXT NOT NULL,
|
|
112
|
+
tenant_id TEXT NOT NULL DEFAULT 'default'
|
|
113
|
+
)
|
|
114
|
+
`;
|
|
115
|
+
// Before the loop on stamped stores: a table lost after its migration stamped (2026-08-15
|
|
116
|
+
// incident) is never re-migrated, and v4/v16/v22 ALTER or read it. Fresh stores use the chain.
|
|
117
|
+
export function ensureContinuityTables(db) {
|
|
118
|
+
db.exec(TASK_SNAPSHOTS_DDL);
|
|
119
|
+
db.exec(SESSION_EVENTS_DDL);
|
|
120
|
+
db.exec(SESSION_HANDOFFS_DDL);
|
|
121
|
+
db.exec(CARDS_DDL);
|
|
122
|
+
db.exec(CARD_DEPS_DDL);
|
|
123
|
+
db.exec(CARD_RUNS_DDL);
|
|
124
|
+
db.exec(CARD_COMMENTS_DDL);
|
|
125
|
+
db.exec(MEMORY_QUARANTINE_DDL);
|
|
126
|
+
}
|
|
127
|
+
// After the loop: tenant_id (v16) and scope (v23) do not exist yet on a genuine old store.
|
|
128
|
+
export function ensureContinuityIndexes(db) {
|
|
129
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_task_snapshots_status_updated ON task_snapshots(status, updated_at DESC, id DESC)`);
|
|
130
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_task_snapshots_tenant_status ON task_snapshots(tenant_id, status, updated_at DESC)`);
|
|
131
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_task_snapshots_tenant_scope ON task_snapshots(tenant_id, scope, status)`);
|
|
132
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_session_events_session_created ON session_events(session_id, created_at DESC, id DESC)`);
|
|
133
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_session_events_task_created ON session_events(task, created_at DESC, id DESC)`);
|
|
134
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_session_events_tenant_session ON session_events(tenant_id, session_id, created_at DESC, id DESC)`);
|
|
135
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_session_handoffs_session ON session_handoffs(session_id, created_at DESC)`);
|
|
136
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_session_handoffs_tenant_session ON session_handoffs(tenant_id, session_id, created_at DESC)`);
|
|
137
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_session_handoffs_tenant_outcome ON session_handoffs(tenant_id, outcome, created_at DESC)`);
|
|
138
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_cards_tenant_status ON cards(tenant_id, status, updated_at DESC)`);
|
|
139
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_card_deps_tenant_child ON card_deps(tenant_id, child)`);
|
|
140
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_card_deps_tenant_parent ON card_deps(tenant_id, parent)`);
|
|
141
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_card_runs_tenant_card ON card_runs(tenant_id, card, started DESC)`);
|
|
142
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_card_comments_tenant_card ON card_comments(tenant_id, card_id, created_at DESC)`);
|
|
143
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_session_handoffs_tenant_card ON session_handoffs(tenant_id, card_id, created_at DESC)`);
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=continuity.js.map
|