hippo-memory 1.71.0 → 2.0.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 +7 -5
- package/dist/agent-memories/apply.d.ts +0 -11
- package/dist/agent-memories/apply.js +11 -26
- package/dist/agent-memories/claude-code.js +1 -1
- package/dist/agent-memories/codex.js +1 -1
- package/dist/agent-memories/copilot.d.ts +1 -3
- package/dist/agent-memories/copilot.js +1 -17
- package/dist/agent-memories/project-folds.d.ts +10 -0
- package/dist/agent-memories/project-folds.js +34 -0
- package/dist/agent-memories/sync.js +8 -6
- package/dist/api/assemble.js +3 -2
- package/dist/api/audit.d.ts +30 -2
- package/dist/api/audit.js +48 -7
- package/dist/api/auth.d.ts +11 -9
- package/dist/api/auth.js +39 -40
- package/dist/api/auto-sleep.d.ts +5 -2
- package/dist/api/auto-sleep.js +7 -3
- package/dist/api/availability.js +2 -3
- package/dist/api/cards.d.ts +42 -0
- package/dist/api/cards.js +52 -0
- package/dist/api/conflicts.d.ts +28 -0
- package/dist/api/conflicts.js +61 -0
- package/dist/api/context-auto.js +4 -2
- package/dist/api/context-render.js +4 -3
- package/dist/api/context-select.js +15 -29
- package/dist/api/context.js +29 -18
- package/dist/api/continuity.d.ts +47 -0
- package/dist/api/continuity.js +66 -0
- package/dist/api/dormant.js +4 -2
- package/dist/api/drill-down.js +3 -2
- package/dist/api/embeddings.d.ts +17 -0
- package/dist/api/embeddings.js +28 -0
- package/dist/api/error-reply.d.ts +8 -0
- package/dist/api/error-reply.js +21 -0
- package/dist/api/forget.d.ts +7 -3
- package/dist/api/forget.js +24 -14
- package/dist/api/goals.d.ts +1 -1
- package/dist/api/goals.js +5 -9
- package/dist/api/graph.js +2 -1
- package/dist/api/index.d.ts +4 -2
- package/dist/api/index.js +4 -2
- package/dist/api/instruction-detect.js +5 -1
- package/dist/api/learn.d.ts +5 -1
- package/dist/api/learn.js +71 -44
- package/dist/api/ledger-db.d.ts +2 -6
- package/dist/api/ledger-db.js +4 -36
- package/dist/api/memories.d.ts +21 -1
- package/dist/api/memories.js +51 -1
- package/dist/api/objects.d.ts +1 -1
- package/dist/api/objects.js +2 -1
- package/dist/api/outcome.d.ts +5 -5
- package/dist/api/outcome.js +17 -16
- package/dist/api/pilot-arm.d.ts +1 -11
- package/dist/api/pilot-arm.js +5 -45
- package/dist/api/predictions.d.ts +3 -1
- package/dist/api/predictions.js +8 -1
- package/dist/api/projects.d.ts +21 -0
- package/dist/api/projects.js +24 -0
- package/dist/api/promote.d.ts +3 -3
- package/dist/api/promote.js +26 -28
- package/dist/api/prompt-hook.d.ts +17 -2
- package/dist/api/prompt-hook.js +45 -4
- package/dist/api/quarantine.js +2 -1
- package/dist/api/recall-cli.d.ts +3 -0
- package/dist/api/recall-cli.js +13 -4
- package/dist/api/recall-core.js +10 -5
- package/dist/api/recall-pipeline.js +23 -24
- package/dist/api/recall-request.js +1 -1
- package/dist/api/recall-types.d.ts +3 -1
- package/dist/api/recall.d.ts +0 -3
- package/dist/api/recall.js +51 -73
- package/dist/api/remember-local.d.ts +5 -2
- package/dist/api/remember-local.js +15 -7
- package/dist/api/remember.d.ts +7 -3
- package/dist/api/remember.js +19 -16
- package/dist/api/session-end.d.ts +46 -0
- package/dist/api/session-end.js +131 -0
- package/dist/api/sleep-run.d.ts +1 -1
- package/dist/api/sleep-run.js +16 -11
- package/dist/api/sleep.js +4 -3
- package/dist/api/status.d.ts +24 -0
- package/dist/api/status.js +40 -0
- package/dist/api/store-init.d.ts +4 -0
- package/dist/api/store-init.js +9 -0
- package/dist/api/tokens.js +2 -2
- package/dist/api/types.d.ts +3 -7
- package/dist/api/types.js +6 -1
- package/dist/capture/caller-session.d.ts +0 -3
- package/dist/capture/caller-session.js +1 -12
- package/dist/capture/capture-error.d.ts +2 -2
- package/dist/capture/capture-error.js +23 -18
- package/dist/capture/codex-patch.js +3 -1
- package/dist/capture/command.js +36 -95
- package/dist/capture/compact.js +10 -5
- package/dist/capture/compaction-items-caller.js +11 -20
- package/dist/capture/compaction-record.d.ts +7 -93
- package/dist/capture/compaction-record.js +29 -426
- package/dist/capture/compaction-spool.d.ts +7 -7
- package/dist/capture/compaction-spool.js +60 -51
- package/dist/capture/copilot-transcript.d.ts +2 -1
- package/dist/capture/copilot-transcript.js +2 -1
- package/dist/capture/extract.d.ts +5 -2
- package/dist/capture/extract.js +4 -3
- package/dist/capture/failure-caller.d.ts +1 -1
- package/dist/capture/failure-caller.js +21 -16
- package/dist/capture/failure-reading.d.ts +2 -1
- package/dist/capture/failure-reading.js +7 -9
- package/dist/capture/pre-compact-caller.js +5 -4
- package/dist/capture/session-digest.js +4 -3
- package/dist/capture/session-end-caller.js +3 -2
- package/dist/capture/session-worker.js +22 -10
- package/dist/capture/transcript.d.ts +2 -1
- package/dist/capture/transcript.js +6 -3
- package/dist/capture/working-state.d.ts +4 -2
- package/dist/capture/working-state.js +4 -2
- package/dist/cli/audit-prune.d.ts +0 -25
- package/dist/cli/audit-prune.js +1 -27
- package/dist/cli/audit.js +20 -20
- package/dist/cli/auth.d.ts +1 -1
- package/dist/cli/auth.js +16 -17
- package/dist/cli/briefs.d.ts +1 -4
- package/dist/cli/briefs.js +37 -292
- package/dist/cli/card.js +41 -33
- package/dist/cli/client.d.ts +6 -2
- package/dist/cli/client.js +37 -21
- package/dist/cli/compact-resume-payload.js +1 -1
- package/dist/cli/context.d.ts +1 -1
- package/dist/cli/context.js +13 -13
- package/dist/cli/continuity.d.ts +1 -1
- package/dist/cli/continuity.js +36 -39
- package/dist/cli/curate.d.ts +2 -2
- package/dist/cli/curate.js +32 -36
- package/dist/cli/dag.d.ts +1 -1
- package/dist/cli/dag.js +23 -8
- package/dist/cli/decisions.d.ts +0 -2
- package/dist/cli/decisions.js +7 -285
- package/dist/cli/eval.d.ts +1 -1
- package/dist/cli/eval.js +12 -13
- package/dist/cli/explain.d.ts +1 -1
- package/dist/cli/explain.js +21 -25
- package/dist/cli/flag-values.js +16 -6
- package/dist/cli/flags.d.ts +0 -289
- package/dist/cli/flags.js +3 -138
- package/dist/cli/graph.d.ts +3 -0
- package/dist/cli/graph.js +83 -0
- package/dist/cli/hook-runtime.d.ts +6 -6
- package/dist/cli/hook-runtime.js +9 -36
- package/dist/cli/incidents.d.ts +3 -0
- package/dist/cli/incidents.js +112 -0
- package/dist/cli/init.d.ts +1 -1
- package/dist/cli/init.js +28 -19
- package/dist/cli/install-steps.d.ts +2 -1
- package/dist/cli/install-steps.js +11 -3
- package/dist/cli/last-sleep.js +1 -1
- package/dist/cli/maintenance.d.ts +3 -3
- package/dist/cli/maintenance.js +29 -30
- package/dist/cli/notes.d.ts +2 -0
- package/dist/cli/notes.js +41 -0
- package/dist/cli/object-verbs.d.ts +2 -2
- package/dist/cli/object-verbs.js +4 -4
- package/dist/cli/open-browser.d.ts +14 -0
- package/dist/cli/open-browser.js +26 -0
- package/dist/cli/playbooks.d.ts +3 -4
- package/dist/cli/playbooks.js +126 -290
- package/dist/cli/predictions.d.ts +3 -0
- package/dist/cli/predictions.js +174 -0
- package/dist/cli/print.js +2 -1
- package/dist/cli/projects.js +30 -22
- package/dist/cli/quality-repair-once.d.ts +1 -1
- package/dist/cli/quality-repair-once.js +6 -5
- package/dist/cli/recall.js +13 -7
- package/dist/cli/refine-llm.js +18 -17
- package/dist/cli/remember.js +17 -17
- package/dist/cli/scheduler.js +2 -1
- package/dist/cli/serve.js +11 -6
- package/dist/cli/session-hooks.d.ts +1 -5
- package/dist/cli/session-hooks.js +89 -168
- package/dist/cli/setup.d.ts +1 -1
- package/dist/cli/setup.js +17 -10
- package/dist/cli/shared.d.ts +2 -6
- package/dist/cli/shared.js +9 -29
- package/dist/cli/sleep-render.js +6 -3
- package/dist/cli/sleep.js +6 -37
- package/dist/cli/status.d.ts +3 -3
- package/dist/cli/status.js +32 -35
- package/dist/cli/transfer.d.ts +3 -3
- package/dist/cli/transfer.js +64 -43
- package/dist/cli/usage.d.ts +1 -80
- package/dist/cli/usage.js +2 -696
- package/dist/cli/verb-row.d.ts +34 -0
- package/dist/cli/verb-row.js +6 -0
- package/dist/cli/verbs/health.d.ts +27 -0
- package/dist/cli/verbs/health.js +234 -0
- package/dist/cli/verbs/memory.d.ts +16 -0
- package/dist/cli/verbs/memory.js +236 -0
- package/dist/cli/verbs/objects.d.ts +20 -0
- package/dist/cli/verbs/objects.js +257 -0
- package/dist/cli/verbs/sessions.d.ts +31 -0
- package/dist/cli/verbs/sessions.js +314 -0
- package/dist/cli/verbs.d.ts +81 -0
- package/dist/cli/verbs.js +7 -0
- package/dist/cli/versioned-verbs.d.ts +51 -0
- package/dist/cli/versioned-verbs.js +79 -0
- package/dist/cli.d.ts +1 -272
- package/dist/cli.js +24 -315
- package/dist/connectors/dlq.d.ts +39 -3
- package/dist/connectors/dlq.js +39 -4
- package/dist/connectors/github/backfill.js +12 -3
- package/dist/connectors/github/deletion.js +2 -1
- package/dist/connectors/github/dlq.js +31 -73
- package/dist/connectors/github/ingest.d.ts +2 -6
- package/dist/connectors/github/ingest.js +5 -29
- package/dist/connectors/github/tenant-routing.js +2 -1
- package/dist/connectors/github/webhook.d.ts +2 -1
- package/dist/connectors/github/webhook.js +35 -2
- package/dist/connectors/ingest.d.ts +10 -0
- package/dist/connectors/ingest.js +29 -0
- package/dist/connectors/slack/deletion.js +2 -1
- package/dist/connectors/slack/dlq.js +49 -71
- package/dist/connectors/slack/ingest.d.ts +2 -6
- package/dist/connectors/slack/ingest.js +4 -28
- package/dist/connectors/slack/tenant-routing.js +2 -1
- package/dist/connectors/slack/webhook.d.ts +2 -1
- package/dist/connectors/slack/webhook.js +2 -1
- package/dist/consolidate/conflicts.js +2 -2
- package/dist/consolidate/dag.js +29 -18
- package/dist/consolidate/dedupe.d.ts +7 -0
- package/dist/consolidate/dedupe.js +6 -4
- package/dist/consolidate/llm-passes.js +45 -19
- package/dist/consolidate/memory-value.js +3 -2
- package/dist/consolidate/merge.js +8 -8
- package/dist/consolidate/physics-pass.d.ts +6 -1
- package/dist/consolidate/physics-pass.js +26 -6
- package/dist/consolidate/replay.js +11 -4
- package/dist/consolidate/sleep.js +9 -6
- package/dist/consolidate/traces.js +5 -5
- package/dist/core/ablation.d.ts +10 -0
- package/dist/core/ablation.js +12 -0
- package/dist/{sharing/scope.d.ts → core/active-scope.d.ts} +1 -1
- package/dist/{sharing/scope.js → core/active-scope.js} +1 -1
- package/dist/core/ambient.d.ts +4 -0
- package/dist/core/ambient.js +39 -18
- package/dist/core/api-errors.d.ts +16 -0
- package/dist/core/api-errors.js +20 -0
- package/dist/core/card.js +3 -1
- package/dist/core/compaction-timing.d.ts +7 -0
- package/dist/core/compaction-timing.js +9 -0
- package/dist/core/config.d.ts +1 -1
- package/dist/core/config.js +22 -18
- package/dist/core/cosine.d.ts +3 -0
- package/dist/core/cosine.js +19 -0
- package/dist/{store → core}/markdown.d.ts +1 -1
- package/dist/{store → core}/markdown.js +4 -4
- package/dist/core/memory-audit.d.ts +17 -0
- package/dist/core/memory-audit.js +54 -0
- package/dist/core/memory.d.ts +14 -8
- package/dist/core/memory.js +35 -23
- package/dist/{consolidate → core}/merged-row.d.ts +1 -1
- package/dist/{consolidate → core}/merged-row.js +1 -1
- package/dist/core/physics.d.ts +3 -1
- package/dist/core/physics.js +23 -18
- package/dist/core/prediction.d.ts +3 -0
- package/dist/core/prediction.js +6 -0
- package/dist/core/project-identity.d.ts +3 -0
- package/dist/core/project-identity.js +12 -2
- package/dist/{store → core}/recall-scope.d.ts +1 -10
- package/dist/{store → core}/recall-scope.js +2 -19
- package/dist/core/search-types.d.ts +1 -1
- package/dist/dashboard/dashboard-actions.d.ts +2 -2
- package/dist/dashboard/dashboard-actions.js +5 -5
- package/dist/dashboard/dashboard-queries.js +2 -7
- package/dist/dashboard/dashboard-snapshot.js +1 -3
- package/dist/dashboard/dashboard.js +16 -5
- package/dist/db/busy.d.ts +2 -7
- package/dist/db/busy.js +21 -26
- package/dist/db/connect.d.ts +2 -2
- package/dist/db/connect.js +7 -7
- package/dist/db/index.d.ts +3 -3
- package/dist/db/index.js +3 -3
- package/dist/db/migrate.js +14 -7
- package/dist/db/migrations/index.d.ts +1 -1
- package/dist/db/migrations/index.js +3 -1
- package/dist/db/migrations/v11.js +1 -0
- package/dist/db/migrations/v14.js +1 -0
- package/dist/db/migrations/v55.d.ts +3 -0
- package/dist/db/migrations/v55.js +17 -0
- package/dist/db/open.d.ts +0 -2
- package/dist/db/open.js +6 -10
- package/dist/db/physics-state.d.ts +0 -4
- package/dist/db/physics-state.js +7 -33
- package/dist/db/request-stores.d.ts +1 -0
- package/dist/db/request-stores.js +2 -1
- package/dist/db/tables.d.ts +2 -0
- package/dist/db/tables.js +6 -2
- package/dist/db/vector-store.js +19 -5
- package/dist/db/wal-checkpointer.js +2 -1
- package/dist/doctor.d.ts +3 -0
- package/dist/doctor.js +18 -15
- package/dist/embeddings/local.d.ts +62 -0
- package/dist/embeddings/local.js +146 -0
- package/dist/{store/embeddings → embeddings}/provider.d.ts +5 -2
- package/dist/{store/embeddings → embeddings}/provider.js +19 -25
- package/dist/embeddings/transformers.d.ts +26 -0
- package/dist/embeddings/transformers.js +89 -0
- package/dist/entry/session-text.d.ts +1 -1
- package/dist/entry/session-text.js +1 -1
- package/dist/entry/store-kit.d.ts +37 -0
- package/dist/entry/store-kit.js +25 -0
- package/dist/entry/testing.d.ts +6 -0
- package/dist/entry/testing.js +7 -0
- package/dist/eval/eval-suite.js +17 -11
- package/dist/eval/eval.js +1 -1
- package/dist/eval/judgment.js +12 -5
- package/dist/graph/extract.js +3 -3
- package/dist/graph/recall.js +1 -1
- package/dist/graph/view.js +13 -5
- package/dist/graph/write.d.ts +1 -1
- package/dist/graph/write.js +9 -9
- package/dist/hooks/codex-session.js +3 -2
- package/dist/hooks/codex-wrapper.js +2 -1
- package/dist/hooks/json-hooks.js +10 -4
- package/dist/hooks/opencode.js +8 -5
- package/dist/hooks/shared.d.ts +0 -6
- package/dist/hooks/shared.js +4 -15
- package/dist/importers/core.js +4 -4
- package/dist/importers/sources.js +5 -4
- package/dist/importers/vault.d.ts +1 -1
- package/dist/importers/vault.js +25 -23
- package/dist/index.d.ts +5 -4
- package/dist/index.js +4 -3
- package/dist/learn/autolearn.d.ts +15 -3
- package/dist/learn/autolearn.js +38 -16
- package/dist/learn/churn-git.js +13 -4
- package/dist/learn/extract.d.ts +3 -2
- package/dist/learn/extract.js +7 -5
- package/dist/learn/forward-claim-detector.js +4 -2
- package/dist/learn/invalidation.d.ts +2 -1
- package/dist/learn/invalidation.js +18 -9
- package/dist/mcp/admin-tools.d.ts +2 -2
- package/dist/mcp/admin-tools.js +27 -48
- package/dist/mcp/format.js +3 -2
- package/dist/mcp/framing.d.ts +1 -0
- package/dist/mcp/framing.js +1 -1
- package/dist/mcp/memory-tools.d.ts +1 -1
- package/dist/mcp/memory-tools.js +8 -9
- package/dist/mcp/protocol.d.ts +11 -7
- package/dist/mcp/protocol.js +25 -12
- package/dist/mcp/recall-tools.js +12 -9
- package/dist/mcp/request.js +22 -20
- package/dist/mcp/stdio.js +37 -16
- package/dist/mcp/tool-args.js +2 -1
- package/dist/mcp/tools.js +44 -21
- package/dist/objects/customer-notes.d.ts +2 -2
- package/dist/objects/decisions.d.ts +2 -2
- package/dist/objects/descriptor.d.ts +1 -1
- package/dist/objects/incidents.d.ts +2 -2
- package/dist/objects/lifecycle.d.ts +1 -1
- package/dist/objects/lifecycle.js +2 -1
- package/dist/objects/policies.d.ts +2 -2
- package/dist/objects/policies.js +2 -1
- package/dist/objects/processes.d.ts +2 -2
- package/dist/objects/project-briefs.d.ts +2 -2
- package/dist/objects/project-briefs.js +2 -3
- package/dist/objects/skills.d.ts +2 -2
- package/dist/postinstall.js +1 -1
- package/dist/rerankers/clef.js +3 -6
- package/dist/rerankers/cross-encoder.d.ts +31 -2
- package/dist/rerankers/cross-encoder.js +71 -98
- package/dist/rerankers/index.js +5 -4
- package/dist/rerankers/jev.js +2 -4
- package/dist/rerankers/outage-warning.d.ts +3 -1
- package/dist/rerankers/outage-warning.js +5 -3
- package/dist/search/bm25-search.js +1 -1
- package/dist/search/boosts.js +1 -1
- package/dist/search/embed-memories.d.ts +12 -0
- package/dist/search/embed-memories.js +389 -0
- package/dist/search/engine.d.ts +8 -0
- package/dist/search/engine.js +10 -0
- package/dist/search/goal-boost.d.ts +1 -1
- package/dist/search/goal-boost.js +6 -3
- package/dist/search/hybrid.d.ts +9 -2
- package/dist/search/hybrid.js +9 -4
- package/dist/search/physics-search.d.ts +6 -3
- package/dist/search/physics-search.js +22 -15
- package/dist/search/rerank.js +1 -1
- package/dist/search/vector.d.ts +8 -6
- package/dist/search/vector.js +14 -17
- package/dist/server/auth.d.ts +3 -1
- package/dist/server/auth.js +24 -15
- package/dist/server/boot.d.ts +2 -1
- package/dist/server/boot.js +34 -84
- package/dist/server/client-ip.d.ts +9 -6
- package/dist/server/client-ip.js +36 -17
- package/dist/server/deadline.d.ts +0 -2
- package/dist/server/deadline.js +1 -7
- package/dist/server/defaults.d.ts +1 -0
- package/dist/server/defaults.js +1 -0
- package/dist/server/mcp-http.js +16 -11
- package/dist/server/request.js +7 -2
- package/dist/server/route-checks.d.ts +7 -0
- package/dist/server/route-checks.js +42 -0
- package/dist/server/route-table.d.ts +9 -8
- package/dist/server/route-table.js +127 -133
- package/dist/server/routes/admin.d.ts +8 -8
- package/dist/server/routes/admin.js +13 -21
- package/dist/server/routes/customer-notes.d.ts +6 -6
- package/dist/server/routes/customer-notes.js +1 -2
- package/dist/server/routes/decisions.d.ts +6 -6
- package/dist/server/routes/decisions.js +2 -4
- package/dist/server/routes/incidents.d.ts +6 -6
- package/dist/server/routes/incidents.js +2 -5
- package/dist/server/routes/memories.d.ts +9 -9
- package/dist/server/routes/memories.js +19 -40
- package/dist/server/routes/object-routes.d.ts +6 -6
- package/dist/server/routes/object-routes.js +4 -7
- package/dist/server/routes/policies.d.ts +7 -7
- package/dist/server/routes/policies.js +2 -4
- package/dist/server/routes/predictions.d.ts +6 -6
- package/dist/server/routes/predictions.js +9 -13
- package/dist/server/routes/processes.d.ts +6 -6
- package/dist/server/routes/processes.js +1 -2
- package/dist/server/routes/project-briefs.d.ts +7 -7
- package/dist/server/routes/project-briefs.js +2 -4
- package/dist/server/routes/recall.d.ts +5 -5
- package/dist/server/routes/recall.js +6 -10
- package/dist/server/routes/skills.d.ts +7 -7
- package/dist/server/routes/skills.js +2 -4
- package/dist/server/server-detect.d.ts +7 -1
- package/dist/server/server-detect.js +36 -11
- package/dist/server/sleep-child.js +26 -7
- package/dist/server/sleep-offload.d.ts +11 -5
- package/dist/server/sleep-offload.js +17 -11
- package/dist/server/tls.d.ts +7 -2
- package/dist/server/tls.js +17 -5
- package/dist/server/types.d.ts +29 -12
- package/dist/server/validation.d.ts +1 -2
- package/dist/server/validation.js +6 -11
- package/dist/server.d.ts +4 -40
- package/dist/server.js +3 -29
- package/dist/sharing/global-store.d.ts +4 -5
- package/dist/sharing/global-store.js +8 -13
- package/dist/sharing/global-sync.js +38 -45
- package/dist/sharing/project-merge.d.ts +7 -14
- package/dist/sharing/project-merge.js +57 -121
- package/dist/sharing/search-both.js +10 -3
- package/dist/sharing/share.d.ts +2 -0
- package/dist/sharing/share.js +39 -38
- package/dist/store/ambient.js +7 -7
- package/dist/store/audit-event.d.ts +1 -1
- package/dist/store/audit.d.ts +0 -16
- package/dist/store/audit.js +4 -55
- package/dist/store/auth.d.ts +4 -2
- package/dist/store/auth.js +7 -3
- package/dist/store/candidates.d.ts +3 -2
- package/dist/store/candidates.js +59 -30
- package/dist/store/capture-write.d.ts +17 -0
- package/dist/store/capture-write.js +35 -0
- package/dist/store/cards.js +6 -5
- package/dist/store/compaction-caller.d.ts +18 -0
- package/dist/store/compaction-caller.js +29 -0
- package/dist/store/compactions-record.d.ts +115 -0
- package/dist/store/compactions-record.js +418 -0
- package/dist/store/compactions.js +6 -4
- package/dist/store/conflicts.d.ts +7 -1
- package/dist/store/conflicts.js +13 -9
- package/dist/store/connectors/dlq.d.ts +10 -0
- package/dist/store/connectors/dlq.js +33 -0
- package/dist/store/connectors/event-log.d.ts +13 -0
- package/dist/store/connectors/event-log.js +24 -0
- package/dist/store/connectors/github.d.ts +7 -9
- package/dist/store/connectors/github.js +35 -60
- package/dist/store/connectors/slack.d.ts +2 -11
- package/dist/store/connectors/slack.js +24 -57
- package/dist/store/dashboard-reads.js +8 -6
- package/dist/store/delete-and-batch.d.ts +6 -0
- package/dist/store/delete-and-batch.js +40 -6
- package/dist/store/delivery-recorder.d.ts +12 -5
- package/dist/store/delivery-recorder.js +13 -11
- package/dist/store/descendant-page.js +8 -4
- package/dist/store/dormant.d.ts +1 -1
- package/dist/store/dormant.js +10 -4
- package/dist/store/embeddings/index.d.ts +2 -12
- package/dist/store/embeddings/index.js +5 -374
- package/dist/store/entry-reads.d.ts +35 -4
- package/dist/store/entry-reads.js +86 -19
- package/dist/store/entry-row.js +9 -2
- package/dist/store/entry-writes.d.ts +15 -12
- package/dist/store/entry-writes.js +54 -22
- package/dist/store/failure-log-at.d.ts +8 -0
- package/dist/store/failure-log-at.js +16 -0
- package/dist/store/failure-log.d.ts +2 -2
- package/dist/store/failure-log.js +5 -3
- package/dist/{trust → store}/gated-write.d.ts +3 -1
- package/dist/{trust → store}/gated-write.js +7 -5
- package/dist/store/goals.js +22 -19
- package/dist/store/graph-reads.js +9 -6
- package/dist/store/graph-view-rows.js +2 -1
- package/dist/store/graph-writes.d.ts +6 -6
- package/dist/store/graph-writes.js +48 -71
- package/dist/store/half-life.js +2 -2
- package/dist/store/handoffs.d.ts +10 -4
- package/dist/store/handoffs.js +10 -9
- package/dist/store/index-and-stats.js +2 -2
- package/dist/store/index.d.ts +2 -2
- package/dist/store/index.js +1 -1
- package/dist/store/key-writes.js +1 -1
- package/dist/store/ledger-turn.d.ts +9 -0
- package/dist/store/ledger-turn.js +38 -0
- package/dist/store/mirrors.js +19 -10
- package/dist/store/open.d.ts +2 -5
- package/dist/store/open.js +42 -54
- package/dist/store/pilot-arm.d.ts +12 -0
- package/dist/store/pilot-arm.js +42 -0
- package/dist/store/port.d.ts +68 -6
- package/dist/store/port.js +8 -0
- package/dist/store/predictions.d.ts +1 -2
- package/dist/store/predictions.js +3 -6
- package/dist/store/project-tags.d.ts +62 -0
- package/dist/store/project-tags.js +67 -0
- package/dist/store/quality-repair.js +1 -1
- package/dist/store/quarantine.js +2 -1
- package/dist/store/recall-trace.d.ts +2 -0
- package/dist/store/recall-trace.js +20 -3
- package/dist/store/rejected-values.js +16 -10
- package/dist/store/rejection-probe.d.ts +4 -0
- package/dist/store/rejection-probe.js +19 -0
- package/dist/store/rejection.d.ts +0 -17
- package/dist/store/rejection.js +1 -21
- package/dist/store/rows.js +2 -2
- package/dist/store/rule-sql.d.ts +11 -0
- package/dist/store/rule-sql.js +31 -6
- package/dist/store/search-rows.d.ts +5 -5
- package/dist/store/search-rows.js +18 -16
- package/dist/store/sessions.js +4 -2
- package/dist/store/set-aside.d.ts +14 -0
- package/dist/store/set-aside.js +23 -0
- package/dist/store/sqlite/busy.d.ts +3 -0
- package/dist/store/sqlite/busy.js +8 -0
- package/dist/store/sqlite/connector-events-group.js +4 -7
- package/dist/store/sqlite/connector-writes-group.js +6 -4
- package/dist/store/sqlite/entry-writes-group.d.ts +3 -3
- package/dist/store/sqlite/entry-writes-group.js +3 -3
- package/dist/store/sqlite/error-codec.js +4 -3
- package/dist/store/sqlite/executor.js +10 -4
- package/dist/store/sqlite/graph-reads-group.js +1 -1
- package/dist/store/sqlite/held-connection.d.ts +11 -0
- package/dist/store/sqlite/held-connection.js +41 -0
- package/dist/store/sqlite/last-recall-group.d.ts +13 -0
- package/dist/store/sqlite/last-recall-group.js +57 -0
- package/dist/store/sqlite/local.d.ts +0 -17
- package/dist/store/sqlite/local.js +0 -31
- package/dist/store/sqlite/maintenance-groups.d.ts +6 -0
- package/dist/store/sqlite/maintenance-groups.js +60 -0
- package/dist/store/sqlite/object-rows.d.ts +1 -1
- package/dist/store/sqlite/objects-group.d.ts +1 -1
- package/dist/store/sqlite/objects-group.js +15 -7
- package/dist/store/sqlite/store.d.ts +1 -8
- package/dist/store/sqlite/store.js +13 -22
- package/dist/store/summaries.d.ts +10 -3
- package/dist/store/summaries.js +54 -11
- package/dist/store/token-ledger-rows.js +3 -2
- package/dist/store/token-ledger.d.ts +0 -7
- package/dist/store/token-ledger.js +0 -35
- package/dist/store/vector-index.js +8 -4
- package/dist/support-bundle.js +2 -3
- package/dist/trust/reject-flow.js +2 -2
- package/dist/util/agent-homes.d.ts +15 -0
- package/dist/util/agent-homes.js +34 -0
- package/dist/util/anthropic-messages.d.ts +6 -1
- package/dist/util/anthropic-messages.js +26 -7
- package/dist/util/atomic-write.d.ts +5 -2
- package/dist/util/atomic-write.js +12 -8
- package/dist/util/chunked.d.ts +3 -0
- package/dist/util/chunked.js +10 -0
- package/dist/util/ellipsize.d.ts +3 -0
- package/dist/util/ellipsize.js +5 -0
- package/dist/util/env.d.ts +12 -2
- package/dist/util/env.js +12 -2
- package/dist/util/format.d.ts +5 -0
- package/dist/util/format.js +10 -0
- package/dist/util/hook-payload.d.ts +8 -0
- package/dist/util/hook-payload.js +37 -0
- package/dist/util/http-retry.d.ts +6 -0
- package/dist/util/http-retry.js +43 -5
- package/dist/util/http-util.d.ts +0 -8
- package/dist/util/http-util.js +10 -21
- package/dist/util/limits.d.ts +7 -0
- package/dist/util/limits.js +14 -0
- package/dist/util/log.d.ts +4 -0
- package/dist/util/log.js +9 -0
- package/dist/util/request-scope.d.ts +9 -3
- package/dist/util/request-scope.js +18 -9
- package/dist/util/secret-detect.js +6 -1
- package/dist/util/spawn-argv.d.ts +9 -0
- package/dist/util/spawn-argv.js +57 -0
- package/dist/util/sql.d.ts +3 -0
- package/dist/util/sql.js +6 -0
- package/dist/util/sqlite-blocked.d.ts +2 -0
- package/dist/util/sqlite-blocked.js +5 -0
- package/dist/util/stream-tee.d.ts +3 -0
- package/dist/util/stream-tee.js +35 -0
- package/dist/util/time.d.ts +4 -0
- package/dist/util/time.js +4 -0
- package/dist/util/vector-codec.d.ts +4 -0
- package/dist/util/vector-codec.js +15 -0
- package/dist/util/version.d.ts +1 -1
- package/dist/util/version.js +1 -1
- 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 +4 -2
- package/dist/api/on-store.d.ts +0 -16
- package/dist/api/on-store.js +0 -38
- package/dist/store/embeddings/local.d.ts +0 -15
- package/dist/store/embeddings/local.js +0 -175
- /package/dist/{cli → api}/correction-latency.d.ts +0 -0
- /package/dist/{cli → api}/correction-latency.js +0 -0
- /package/dist/{cli → api}/provenance-coverage.d.ts +0 -0
- /package/dist/{cli → api}/provenance-coverage.js +0 -0
- /package/dist/{store → core}/object-types.d.ts +0 -0
- /package/dist/{store → core}/object-types.js +0 -0
- /package/dist/{capture → util}/compaction-items.d.ts +0 -0
- /package/dist/{capture → util}/compaction-items.js +0 -0
- /package/dist/{store → util}/yaml.d.ts +0 -0
- /package/dist/{store → util}/yaml.js +0 -0
package/dist/store/conflicts.js
CHANGED
|
@@ -2,15 +2,17 @@ import { DEFAULT_TENANT_ID } from '../util/env.js';
|
|
|
2
2
|
import { closeHippoDb, withWriteScope } from '../db/index.js';
|
|
3
3
|
import { rejectionDigest, insertRejectedValue, normalizeValueForRejection } from './rejection.js';
|
|
4
4
|
import { archiveRawMemory } from './raw-archive.js';
|
|
5
|
-
import { rowToMemoryConflict } from './rows.js';
|
|
5
|
+
import { parseJsonArray, rowToMemoryConflict } from './rows.js';
|
|
6
6
|
import { audit } from './audit-event.js';
|
|
7
7
|
import { syncChangedMirrors, purgeMirrorBestEffort } from './mirrors.js';
|
|
8
|
-
import {
|
|
8
|
+
import { selectEntriesByIds } from './entry-reads.js';
|
|
9
|
+
import { chunked } from '../util/chunked.js';
|
|
9
10
|
import { onHandle, openStore } from './open.js';
|
|
10
11
|
import { deleteEntryCore } from './delete-and-batch.js';
|
|
11
12
|
import { BadRequestError } from '../core/api-errors.js';
|
|
12
|
-
import { canTouchScope, isPersonalScope } from '
|
|
13
|
+
import { canTouchScope, isPersonalScope } from '../core/recall-scope.js';
|
|
13
14
|
import { selectMemoryReach } from './tenant-lookup.js';
|
|
15
|
+
import { placeholders } from '../util/sql.js';
|
|
14
16
|
// The one place MemoryConflictRow's columns are listed; CONFLICT_COLS_MC is the same list under the `mc` alias.
|
|
15
17
|
const CONFLICT_COLS = 'id, memory_a_id, memory_b_id, reason, score, status, detected_at, updated_at';
|
|
16
18
|
const CONFLICT_COLS_MC = CONFLICT_COLS.split(', ').map((c) => `mc.${c}`).join(', ');
|
|
@@ -131,7 +133,7 @@ function readConflictRefresh(db, detected) {
|
|
|
131
133
|
}
|
|
132
134
|
const given = [...new Set(detected.flatMap((c) => [c.memory_a_id, c.memory_b_id]))].filter((id) => !tenantById.has(id));
|
|
133
135
|
for (const ids of chunked(given)) {
|
|
134
|
-
const marks = ids.
|
|
136
|
+
const marks = placeholders(ids.length);
|
|
135
137
|
// SAFETY: rows' shape matches the two columns named in the SELECT.
|
|
136
138
|
const found = db.prepare(`SELECT id, tenant_id FROM memories WHERE id IN (${marks})`).all(...ids);
|
|
137
139
|
for (const r of found)
|
|
@@ -252,7 +254,7 @@ function findResolvableConflict(db, conflictId, keepId, tenantId) {
|
|
|
252
254
|
}
|
|
253
255
|
/** Resolve a conflict by keeping one memory and halving the loser's half-life; `--forget` removes the loser, `opts.rejectLoserValue` also tombstones its
|
|
254
256
|
* digest. Every path emits a `conflict_resolve` audit row. Returns the resolved conflict, or null if not found. */
|
|
255
|
-
export function resolveConflict(hippoRoot, conflictId, keepId, forgetLoser = false, tenantId, opts) {
|
|
257
|
+
export function resolveConflict(hippoRoot, { conflictId, keepId, forgetLoser = false, tenantId }, opts) {
|
|
256
258
|
const db = openStore(hippoRoot);
|
|
257
259
|
const scope = conflictMemScope(tenantId);
|
|
258
260
|
try {
|
|
@@ -351,7 +353,8 @@ function recordRejectedLoserValue(db, t, loserRow, digest, who) {
|
|
|
351
353
|
/** Tombstones the loser's digest and removes its same-tenant duplicates into `removal`; returns the digest. */
|
|
352
354
|
function tombstoneLoserValue(db, t, loserRow, who, removal) {
|
|
353
355
|
if (isPersonalScope(loserRow.scope)) {
|
|
354
|
-
throw new BadRequestError(`cannot reject the value of personal memory ${t.loserId}: a rejection reaches the whole tenant,
|
|
356
|
+
throw new BadRequestError(`cannot reject the value of personal memory ${t.loserId}: a rejection reaches the whole tenant, ` +
|
|
357
|
+
`so its reason would show to everyone; resolve with forget instead`);
|
|
355
358
|
}
|
|
356
359
|
const { actor, reason } = who;
|
|
357
360
|
const rejectedDigest = rejectionDigest(loserRow.content);
|
|
@@ -387,7 +390,7 @@ function stripConflictRefs(db, t, loserRemoved) {
|
|
|
387
390
|
// SAFETY: keepRow's shape matches the single `conflicts_with_json` column selected above.
|
|
388
391
|
const keepRow = db.prepare(`SELECT conflicts_with_json FROM memories WHERE id = ?${memScope}`).get(keepId, ...memArgs);
|
|
389
392
|
if (keepRow) {
|
|
390
|
-
const refs =
|
|
393
|
+
const refs = parseJsonArray(keepRow.conflicts_with_json, { table: 'memories', id: keepId, column: 'conflicts_with_json' });
|
|
391
394
|
const cleaned = refs.filter((r) => r !== loserId);
|
|
392
395
|
db.prepare(`UPDATE memories SET conflicts_with_json = ?, updated_at = datetime('now') WHERE id = ?${memScope}`)
|
|
393
396
|
.run(JSON.stringify(cleaned), keepId, ...memArgs);
|
|
@@ -397,7 +400,7 @@ function stripConflictRefs(db, t, loserRemoved) {
|
|
|
397
400
|
// column named in the SELECT below.
|
|
398
401
|
const loserRow = db.prepare(`SELECT conflicts_with_json FROM memories WHERE id = ?${memScope}`).get(loserId, ...memArgs);
|
|
399
402
|
if (loserRow) {
|
|
400
|
-
const refs =
|
|
403
|
+
const refs = parseJsonArray(loserRow.conflicts_with_json, { table: 'memories', id: loserId, column: 'conflicts_with_json' });
|
|
401
404
|
const cleaned = refs.filter((r) => r !== keepId);
|
|
402
405
|
db.prepare(`UPDATE memories SET conflicts_with_json = ?, updated_at = datetime('now') WHERE id = ?${memScope}`)
|
|
403
406
|
.run(JSON.stringify(cleaned), loserId, ...memArgs);
|
|
@@ -438,7 +441,8 @@ function purgeRemovedLoserMirrors(hippoRoot, db, loserId, removal) {
|
|
|
438
441
|
/** The conflict and loser ids every conflict_resolve audit row names. */
|
|
439
442
|
export function conflictResolveAuditsAt(db) {
|
|
440
443
|
// SAFETY: SELECT of two fields every conflict_resolve audit row carries (ConflictResolveMeta above).
|
|
441
|
-
return db.prepare(`SELECT json_extract(metadata_json, '$.conflictId') AS conflictId, json_extract(metadata_json, '$.loserId') AS loserId
|
|
444
|
+
return db.prepare(`SELECT json_extract(metadata_json, '$.conflictId') AS conflictId, json_extract(metadata_json, '$.loserId') AS loserId ` +
|
|
445
|
+
`FROM audit_log WHERE op = 'conflict_resolve'`).all();
|
|
442
446
|
}
|
|
443
447
|
/** Every resolved conflict with both sides. */
|
|
444
448
|
export function resolvedConflictsAt(db) {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { DatabaseSyncLike } from '../../db/index.js';
|
|
2
|
+
export type DlqTable = 'slack_dlq' | 'github_dlq';
|
|
3
|
+
export type DlqRawValue = string | number | bigint | null;
|
|
4
|
+
export type DlqRawRow = Record<string, DlqRawValue>;
|
|
5
|
+
/** Inserts one row, stamping received_at; `columns` pairs each column name with its value, in insert order. */
|
|
6
|
+
export declare function insertDlqRow(db: DatabaseSyncLike, table: DlqTable, columns: ReadonlyArray<readonly [string, string | null]>): number;
|
|
7
|
+
export declare function listDlqRows(db: DatabaseSyncLike, table: DlqTable, selectColumns: string, tenantId: string, limit: number): DlqRawRow[];
|
|
8
|
+
export declare function getDlqRow(db: DatabaseSyncLike, table: DlqTable, selectColumns: string, id: number): DlqRawRow | null;
|
|
9
|
+
export declare function markDlqRetried(db: DatabaseSyncLike, table: DlqTable, id: number): void;
|
|
10
|
+
//# sourceMappingURL=dlq.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// The dead-letter statements both connectors share; each connector names its table and columns and maps rows to its own item type.
|
|
2
|
+
import { placeholders } from '../../util/sql.js';
|
|
3
|
+
/** Inserts one row, stamping received_at; `columns` pairs each column name with its value, in insert order. */
|
|
4
|
+
export function insertDlqRow(db, table, columns) {
|
|
5
|
+
const names = [...columns.map(([name]) => name), 'received_at'];
|
|
6
|
+
const values = [...columns.map(([, value]) => value), new Date().toISOString()];
|
|
7
|
+
const result = db
|
|
8
|
+
.prepare(`INSERT INTO ${table} (${names.join(', ')}) VALUES (${placeholders(names.length)})`)
|
|
9
|
+
.run(...values);
|
|
10
|
+
return Number(result.lastInsertRowid);
|
|
11
|
+
}
|
|
12
|
+
export function listDlqRows(db, table, selectColumns, tenantId, limit) {
|
|
13
|
+
// SAFETY: the caller's selectColumns names exactly the fields its rowToItem reads.
|
|
14
|
+
return db
|
|
15
|
+
.prepare(`SELECT ${selectColumns}
|
|
16
|
+
FROM ${table}
|
|
17
|
+
WHERE tenant_id = ?
|
|
18
|
+
ORDER BY received_at ASC
|
|
19
|
+
LIMIT ?`)
|
|
20
|
+
.all(tenantId, limit);
|
|
21
|
+
}
|
|
22
|
+
export function getDlqRow(db, table, selectColumns, id) {
|
|
23
|
+
// SAFETY: the caller's selectColumns names exactly the fields its rowToItem reads.
|
|
24
|
+
const row = db
|
|
25
|
+
.prepare(`SELECT ${selectColumns} FROM ${table} WHERE id = ?`)
|
|
26
|
+
.get(id);
|
|
27
|
+
return row ?? null;
|
|
28
|
+
}
|
|
29
|
+
export function markDlqRetried(db, table, id) {
|
|
30
|
+
db.prepare(`UPDATE ${table} SET retried_at = ?, retry_count = retry_count + 1 WHERE id = ?`)
|
|
31
|
+
.run(new Date().toISOString(), id);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=dlq.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { DatabaseSyncLike } from '../../db/index.js';
|
|
2
|
+
import type { ConnectorEventRecord } from '../port.js';
|
|
3
|
+
/** Which log table a connector writes and the column its dedupe key lives in. */
|
|
4
|
+
export interface EventLogSpec {
|
|
5
|
+
readonly table: 'slack_event_log' | 'github_event_log';
|
|
6
|
+
readonly keyColumn: 'event_id' | 'idempotency_key';
|
|
7
|
+
}
|
|
8
|
+
export declare function eventSeenAt(db: DatabaseSyncLike, log: EventLogSpec, key: string): boolean;
|
|
9
|
+
export declare function eventMemoryAt(db: DatabaseSyncLike, log: EventLogSpec, key: string): string | null;
|
|
10
|
+
/** Records the key unless a row holds it already; false means another writer got there first. `extra` pairs connector-only columns with values. */
|
|
11
|
+
export declare function markEventSeenAt(db: DatabaseSyncLike, log: EventLogSpec, key: string, extra: ReadonlyArray<readonly [string, string]>, memoryId: string | null): boolean;
|
|
12
|
+
export declare function eventRecord(hippoRoot: string, log: EventLogSpec, key: string): ConnectorEventRecord;
|
|
13
|
+
//# sourceMappingURL=event-log.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// The event-log statements both connectors share; each connector names its table, key column and extra columns.
|
|
2
|
+
import { placeholders } from '../../util/sql.js';
|
|
3
|
+
import { onHandle } from '../open.js';
|
|
4
|
+
export function eventSeenAt(db, log, key) {
|
|
5
|
+
return !!db.prepare(`SELECT 1 FROM ${log.table} WHERE ${log.keyColumn} = ?`).get(key);
|
|
6
|
+
}
|
|
7
|
+
export function eventMemoryAt(db, log, key) {
|
|
8
|
+
// SAFETY: the SELECT projects only `memory_id`, a nullable column; .get() returns undefined when no row matches.
|
|
9
|
+
const row = db.prepare(`SELECT memory_id FROM ${log.table} WHERE ${log.keyColumn} = ?`).get(key);
|
|
10
|
+
return row?.memory_id ?? null;
|
|
11
|
+
}
|
|
12
|
+
/** Records the key unless a row holds it already; false means another writer got there first. `extra` pairs connector-only columns with values. */
|
|
13
|
+
export function markEventSeenAt(db, log, key, extra, memoryId) {
|
|
14
|
+
const names = [log.keyColumn, ...extra.map(([name]) => name), 'ingested_at', 'memory_id'];
|
|
15
|
+
const values = [key, ...extra.map(([, value]) => value), new Date().toISOString(), memoryId];
|
|
16
|
+
const inserted = db
|
|
17
|
+
.prepare(`INSERT OR IGNORE INTO ${log.table} (${names.join(', ')}) VALUES (${placeholders(names.length)})`)
|
|
18
|
+
.run(...values);
|
|
19
|
+
return Number(inserted.changes ?? 0) !== 0;
|
|
20
|
+
}
|
|
21
|
+
export function eventRecord(hippoRoot, log, key) {
|
|
22
|
+
return onHandle(hippoRoot, (db) => eventSeenAt(db, log, key) ? { seen: true, memoryId: eventMemoryAt(db, log, key) } : { seen: false });
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=event-log.js.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type DatabaseSyncLike } from '../../db/index.js';
|
|
2
|
+
import type { ConnectorEventRecord } from '../port.js';
|
|
2
3
|
/** One github_event_log row to write; `memoryId` is null for an event that produced no memory. */
|
|
3
4
|
export interface GitHubEventLogEntry {
|
|
4
5
|
idempotencyKey: string;
|
|
@@ -6,15 +7,12 @@ export interface GitHubEventLogEntry {
|
|
|
6
7
|
eventName: string;
|
|
7
8
|
memoryId: string | null;
|
|
8
9
|
}
|
|
9
|
-
export declare function
|
|
10
|
-
export declare function
|
|
10
|
+
export declare function githubEventSeenAt(db: DatabaseSyncLike, idempotencyKey: string): boolean;
|
|
11
|
+
export declare function githubEventMemoryAt(db: DatabaseSyncLike, idempotencyKey: string): string | null;
|
|
11
12
|
/** False when the key was already logged, so a caller inside a write scope can roll its own row back. */
|
|
12
|
-
export declare function
|
|
13
|
-
|
|
14
|
-
export declare function
|
|
15
|
-
memoryId: string | null;
|
|
16
|
-
} | null;
|
|
17
|
-
export declare function logEvent(hippoRoot: string, entry: GitHubEventLogEntry): void;
|
|
13
|
+
export declare function markGitHubEventSeenAt(db: DatabaseSyncLike, entry: GitHubEventLogEntry): boolean;
|
|
14
|
+
export declare function githubEventRecord(hippoRoot: string, idempotencyKey: string): ConnectorEventRecord;
|
|
15
|
+
export declare function markGitHubEventSeen(hippoRoot: string, entry: GitHubEventLogEntry): void;
|
|
18
16
|
export interface ArtifactDeletion {
|
|
19
17
|
tenantId: string;
|
|
20
18
|
artifactRef: string;
|
|
@@ -60,7 +58,7 @@ export interface GitHubDlqInsert {
|
|
|
60
58
|
export declare function insertGitHubDlq(hippoRoot: string, row: GitHubDlqInsert): number;
|
|
61
59
|
export declare function listGitHubDlq(hippoRoot: string, tenantId: string, limit: number): DlqItem[];
|
|
62
60
|
export declare function githubDlqEntry(hippoRoot: string, id: number): DlqItem | null;
|
|
63
|
-
export declare function
|
|
61
|
+
export declare function markGitHubDlqRetried(hippoRoot: string, id: number): void;
|
|
64
62
|
/** What the routing tables say about one webhook; `tenant` is the installation match, or the repo match when no installation was sent. */
|
|
65
63
|
export interface GitHubRouting {
|
|
66
64
|
installations: number;
|
|
@@ -2,31 +2,29 @@
|
|
|
2
2
|
import { withWriteScope } from '../../db/index.js';
|
|
3
3
|
import { archiveRawMemory } from '../raw-archive.js';
|
|
4
4
|
import { onHandle } from '../open.js';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
import { eventMemoryAt, eventRecord, eventSeenAt, markEventSeenAt } from './event-log.js';
|
|
6
|
+
import { getDlqRow, insertDlqRow, listDlqRows, markDlqRetried } from './dlq.js';
|
|
7
|
+
const GITHUB_LOG = { table: 'github_event_log', keyColumn: 'idempotency_key' };
|
|
8
|
+
export function githubEventSeenAt(db, idempotencyKey) {
|
|
9
|
+
return eventSeenAt(db, GITHUB_LOG, idempotencyKey);
|
|
8
10
|
}
|
|
9
|
-
export function
|
|
10
|
-
|
|
11
|
-
const row = db.prepare(`SELECT memory_id FROM github_event_log WHERE idempotency_key = ?`).get(idempotencyKey);
|
|
12
|
-
return row?.memory_id ?? null;
|
|
11
|
+
export function githubEventMemoryAt(db, idempotencyKey) {
|
|
12
|
+
return eventMemoryAt(db, GITHUB_LOG, idempotencyKey);
|
|
13
13
|
}
|
|
14
14
|
/** False when the key was already logged, so a caller inside a write scope can roll its own row back. */
|
|
15
|
-
export function
|
|
16
|
-
|
|
17
|
-
return Number(inserted.changes ?? 0) !== 0;
|
|
15
|
+
export function markGitHubEventSeenAt(db, entry) {
|
|
16
|
+
return markEventSeenAt(db, GITHUB_LOG, entry.idempotencyKey, [['delivery_id', entry.deliveryId], ['event_name', entry.eventName]], entry.memoryId);
|
|
18
17
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return onHandle(hippoRoot, (db) => eventSeenAt(db, idempotencyKey) ? { memoryId: eventMemoryAt(db, idempotencyKey) } : null);
|
|
18
|
+
export function githubEventRecord(hippoRoot, idempotencyKey) {
|
|
19
|
+
return eventRecord(hippoRoot, GITHUB_LOG, idempotencyKey);
|
|
22
20
|
}
|
|
23
|
-
export function
|
|
24
|
-
onHandle(hippoRoot, (db) => {
|
|
21
|
+
export function markGitHubEventSeen(hippoRoot, entry) {
|
|
22
|
+
onHandle(hippoRoot, (db) => { markGitHubEventSeenAt(db, entry); });
|
|
25
23
|
}
|
|
26
24
|
/** Archives every active raw row of one tenant's artifact and logs the delete event; a failed archive rolls back the batch and the log row. */
|
|
27
25
|
export function archiveDeletedArtifact(hippoRoot, del) {
|
|
28
26
|
return onHandle(hippoRoot, (db) => {
|
|
29
|
-
if (
|
|
27
|
+
if (githubEventSeenAt(db, del.idempotencyKey))
|
|
30
28
|
return { duplicate: true, archived: 0 };
|
|
31
29
|
const logged = { idempotencyKey: del.idempotencyKey, deliveryId: del.deliveryId, eventName: del.eventName };
|
|
32
30
|
// SAFETY: the SELECT projects only the `id` column.
|
|
@@ -36,29 +34,17 @@ export function archiveDeletedArtifact(hippoRoot, del) {
|
|
|
36
34
|
const memoryIds = rows.map((r) => r.id);
|
|
37
35
|
if (memoryIds.length === 0) {
|
|
38
36
|
// Still logged, so a retry of the same delete answers 'duplicate'.
|
|
39
|
-
|
|
37
|
+
markGitHubEventSeenAt(db, { ...logged, memoryId: null });
|
|
40
38
|
return { duplicate: false, archived: 0 };
|
|
41
39
|
}
|
|
42
40
|
withWriteScope(db, 'github_delete_all', () => {
|
|
43
41
|
for (const id of memoryIds)
|
|
44
42
|
archiveRawMemory(db, id, { reason: del.reason, who: del.who });
|
|
45
|
-
|
|
43
|
+
markGitHubEventSeenAt(db, { ...logged, memoryId: memoryIds[0] });
|
|
46
44
|
});
|
|
47
45
|
return { duplicate: false, archived: memoryIds.length };
|
|
48
46
|
});
|
|
49
47
|
}
|
|
50
|
-
function insertGitHubDlqAt(db, row) {
|
|
51
|
-
const result = db
|
|
52
|
-
.prepare(`INSERT INTO github_dlq
|
|
53
|
-
(tenant_id, raw_payload, error, event_name, delivery_id, signature,
|
|
54
|
-
installation_id, repo_full_name, received_at, bucket)
|
|
55
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
56
|
-
.run(row.tenantId, row.rawPayload, row.error, row.eventName ?? null, row.deliveryId ?? null, row.signature, row.installationId ?? null, row.repoFullName ?? null, new Date().toISOString(), row.bucket);
|
|
57
|
-
return Number(result.lastInsertRowid);
|
|
58
|
-
}
|
|
59
|
-
export function insertGitHubDlq(hippoRoot, row) {
|
|
60
|
-
return onHandle(hippoRoot, (db) => insertGitHubDlqAt(db, row));
|
|
61
|
-
}
|
|
62
48
|
const SELECT_COLUMNS = `id, tenant_id, raw_payload, error, event_name, delivery_id,
|
|
63
49
|
signature, installation_id, repo_full_name, retry_count,
|
|
64
50
|
received_at, retried_at, bucket`;
|
|
@@ -79,42 +65,31 @@ function rowToItem(r) {
|
|
|
79
65
|
bucket: r.bucket == null ? 'parse_error' : String(r.bucket),
|
|
80
66
|
};
|
|
81
67
|
}
|
|
82
|
-
function
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
.
|
|
91
|
-
|
|
68
|
+
export function insertGitHubDlq(hippoRoot, row) {
|
|
69
|
+
return onHandle(hippoRoot, (db) => insertDlqRow(db, 'github_dlq', [
|
|
70
|
+
['tenant_id', row.tenantId],
|
|
71
|
+
['raw_payload', row.rawPayload],
|
|
72
|
+
['error', row.error],
|
|
73
|
+
['event_name', row.eventName ?? null],
|
|
74
|
+
['delivery_id', row.deliveryId ?? null],
|
|
75
|
+
['signature', row.signature],
|
|
76
|
+
['installation_id', row.installationId ?? null],
|
|
77
|
+
['repo_full_name', row.repoFullName ?? null],
|
|
78
|
+
['bucket', row.bucket],
|
|
79
|
+
]));
|
|
92
80
|
}
|
|
93
81
|
export function listGitHubDlq(hippoRoot, tenantId, limit) {
|
|
94
|
-
return onHandle(hippoRoot, (db) =>
|
|
95
|
-
}
|
|
96
|
-
function githubDlqEntryAt(db, id) {
|
|
97
|
-
// SAFETY: SELECT_COLUMNS projects exactly DlqRawRow's fields.
|
|
98
|
-
const row = db
|
|
99
|
-
.prepare(`SELECT ${SELECT_COLUMNS}
|
|
100
|
-
FROM github_dlq
|
|
101
|
-
WHERE id = ?`)
|
|
102
|
-
.get(id);
|
|
103
|
-
if (!row)
|
|
104
|
-
return null;
|
|
105
|
-
return rowToItem(row);
|
|
82
|
+
return onHandle(hippoRoot, (db) => listDlqRows(db, 'github_dlq', SELECT_COLUMNS, tenantId, limit).map(rowToItem));
|
|
106
83
|
}
|
|
107
84
|
export function githubDlqEntry(hippoRoot, id) {
|
|
108
|
-
return onHandle(hippoRoot, (db) =>
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
onHandle(hippoRoot, (db) => {
|
|
112
|
-
db.prepare(`UPDATE github_dlq
|
|
113
|
-
SET retry_count = retry_count + 1,
|
|
114
|
-
retried_at = ?
|
|
115
|
-
WHERE id = ?`).run(new Date().toISOString(), id);
|
|
85
|
+
return onHandle(hippoRoot, (db) => {
|
|
86
|
+
const row = getDlqRow(db, 'github_dlq', SELECT_COLUMNS, id);
|
|
87
|
+
return row ? rowToItem(row) : null;
|
|
116
88
|
});
|
|
117
89
|
}
|
|
90
|
+
export function markGitHubDlqRetried(hippoRoot, id) {
|
|
91
|
+
onHandle(hippoRoot, (db) => markDlqRetried(db, 'github_dlq', id));
|
|
92
|
+
}
|
|
118
93
|
export function githubRouting(hippoRoot, args) {
|
|
119
94
|
return onHandle(hippoRoot, (db) => {
|
|
120
95
|
// SAFETY: the SELECT projects exactly one column, `c`, as a COUNT(*).
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { DatabaseSyncLike } from '../../db/index.js';
|
|
2
|
+
import type { ConnectorEventRecord } from '../port.js';
|
|
2
3
|
export declare function slackCursorAt(db: DatabaseSyncLike, tenantId: string, channelId: string): string | null;
|
|
3
4
|
export declare function saveSlackCursorAt(db: DatabaseSyncLike, tenantId: string, channelId: string, latestTs: string): void;
|
|
4
5
|
export declare function slackCursor(hippoRoot: string, tenantId: string, channelId: string): string | null;
|
|
@@ -7,14 +8,7 @@ export declare function slackEventSeenAt(db: DatabaseSyncLike, eventId: string):
|
|
|
7
8
|
/** Records the event unless a row already holds its id; false means another writer got there first. */
|
|
8
9
|
export declare function markSlackEventSeenAt(db: DatabaseSyncLike, eventId: string, memoryId: string | null): boolean;
|
|
9
10
|
export declare function slackEventMemoryAt(db: DatabaseSyncLike, eventId: string): string | null;
|
|
10
|
-
|
|
11
|
-
export type SlackEventRecord = {
|
|
12
|
-
seen: false;
|
|
13
|
-
} | {
|
|
14
|
-
seen: true;
|
|
15
|
-
memoryId: string | null;
|
|
16
|
-
};
|
|
17
|
-
export declare function slackEventRecord(hippoRoot: string, eventId: string): SlackEventRecord;
|
|
11
|
+
export declare function slackEventRecord(hippoRoot: string, eventId: string): ConnectorEventRecord;
|
|
18
12
|
export declare function markSlackEventSeen(hippoRoot: string, eventId: string, memoryId: string | null): void;
|
|
19
13
|
/** The raw memory a Slack message became. The tenant filter keeps one tenant's deletion from reaching another's row with the same ref. */
|
|
20
14
|
export declare function rawMemoryIdForArtifactAt(db: DatabaseSyncLike, artifactRef: string, tenantId: string): string | null;
|
|
@@ -42,13 +36,10 @@ export interface SlackDlqInsert {
|
|
|
42
36
|
signature: string | null;
|
|
43
37
|
slackTimestamp?: string | null;
|
|
44
38
|
}
|
|
45
|
-
/** Counts a failed replay and leaves `retried_at` alone, so the row still reads as never drained. */
|
|
46
|
-
export declare function bumpSlackDlqRetryCountAt(db: DatabaseSyncLike, id: number): void;
|
|
47
39
|
export declare function insertSlackDlq(hippoRoot: string, row: SlackDlqInsert): number;
|
|
48
40
|
export declare function listSlackDlq(hippoRoot: string, tenantId: string, limit: number): DlqItem[];
|
|
49
41
|
export declare function slackDlqEntry(hippoRoot: string, id: number): DlqItem | null;
|
|
50
42
|
export declare function markSlackDlqRetried(hippoRoot: string, id: number): void;
|
|
51
|
-
export declare function bumpSlackDlqRetryCount(hippoRoot: string, id: number): void;
|
|
52
43
|
/** The tenant a team maps to, or how many workspaces are registered when it maps to none. */
|
|
53
44
|
export type SlackTeamRoute = {
|
|
54
45
|
tenantId: string;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Every statement the Slack connector runs on hippo.db: backfill cursors, the event log, the dead-letter queue and workspace routing.
|
|
2
2
|
// `xAt(db, ...)` runs on the caller's handle; the same name without `At` opens hippo.db for that one call.
|
|
3
3
|
import { onHandle } from '../open.js';
|
|
4
|
+
import { eventMemoryAt, eventRecord, eventSeenAt, markEventSeenAt } from './event-log.js';
|
|
5
|
+
import { getDlqRow, insertDlqRow, listDlqRows, markDlqRetried } from './dlq.js';
|
|
4
6
|
// Backfill cursors
|
|
5
7
|
export function slackCursorAt(db, tenantId, channelId) {
|
|
6
8
|
// SAFETY: the SELECT names the single `latest_ts` column; sqlite returns undefined when no row matches.
|
|
@@ -20,24 +22,19 @@ export function saveSlackCursor(hippoRoot, tenantId, channelId, latestTs) {
|
|
|
20
22
|
onHandle(hippoRoot, (db) => saveSlackCursorAt(db, tenantId, channelId, latestTs));
|
|
21
23
|
}
|
|
22
24
|
// Event log
|
|
25
|
+
const SLACK_LOG = { table: 'slack_event_log', keyColumn: 'event_id' };
|
|
23
26
|
export function slackEventSeenAt(db, eventId) {
|
|
24
|
-
|
|
25
|
-
return !!row;
|
|
27
|
+
return eventSeenAt(db, SLACK_LOG, eventId);
|
|
26
28
|
}
|
|
27
29
|
/** Records the event unless a row already holds its id; false means another writer got there first. */
|
|
28
30
|
export function markSlackEventSeenAt(db, eventId, memoryId) {
|
|
29
|
-
|
|
30
|
-
.prepare(`INSERT OR IGNORE INTO slack_event_log (event_id, ingested_at, memory_id) VALUES (?, ?, ?)`)
|
|
31
|
-
.run(eventId, new Date().toISOString(), memoryId);
|
|
32
|
-
return Number(inserted.changes ?? 0) !== 0;
|
|
31
|
+
return markEventSeenAt(db, SLACK_LOG, eventId, [], memoryId);
|
|
33
32
|
}
|
|
34
33
|
export function slackEventMemoryAt(db, eventId) {
|
|
35
|
-
|
|
36
|
-
const row = db.prepare(`SELECT memory_id FROM slack_event_log WHERE event_id = ?`).get(eventId);
|
|
37
|
-
return row?.memory_id ?? null;
|
|
34
|
+
return eventMemoryAt(db, SLACK_LOG, eventId);
|
|
38
35
|
}
|
|
39
36
|
export function slackEventRecord(hippoRoot, eventId) {
|
|
40
|
-
return
|
|
37
|
+
return eventRecord(hippoRoot, SLACK_LOG, eventId);
|
|
41
38
|
}
|
|
42
39
|
export function markSlackEventSeen(hippoRoot, eventId, memoryId) {
|
|
43
40
|
onHandle(hippoRoot, (db) => markSlackEventSeenAt(db, eventId, memoryId));
|
|
@@ -51,6 +48,8 @@ export function rawMemoryIdForArtifactAt(db, artifactRef, tenantId) {
|
|
|
51
48
|
.get(artifactRef, tenantId);
|
|
52
49
|
return row?.id ?? null;
|
|
53
50
|
}
|
|
51
|
+
const SELECT_COLUMNS = `id, tenant_id, team_id, raw_payload, error, received_at, retried_at,
|
|
52
|
+
bucket, retry_count, signature, slack_timestamp`;
|
|
54
53
|
function rowToItem(r) {
|
|
55
54
|
return {
|
|
56
55
|
id: Number(r.id),
|
|
@@ -66,60 +65,28 @@ function rowToItem(r) {
|
|
|
66
65
|
slackTimestamp: r.slack_timestamp == null ? null : String(r.slack_timestamp),
|
|
67
66
|
};
|
|
68
67
|
}
|
|
69
|
-
function insertSlackDlqAt(db, row) {
|
|
70
|
-
const result = db
|
|
71
|
-
.prepare(`INSERT INTO slack_dlq
|
|
72
|
-
(tenant_id, team_id, raw_payload, error, received_at, bucket, signature, slack_timestamp)
|
|
73
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
74
|
-
.run(row.tenantId, row.teamId ?? null, row.rawPayload, row.error, new Date().toISOString(), row.bucket, row.signature, row.slackTimestamp ?? null);
|
|
75
|
-
return Number(result.lastInsertRowid);
|
|
76
|
-
}
|
|
77
|
-
function listSlackDlqAt(db, tenantId, limit) {
|
|
78
|
-
// SAFETY: row shape matches the columns named in the SELECT below.
|
|
79
|
-
const rows = db
|
|
80
|
-
.prepare(`SELECT id, tenant_id, team_id, raw_payload, error, received_at, retried_at,
|
|
81
|
-
bucket, retry_count, signature, slack_timestamp
|
|
82
|
-
FROM slack_dlq
|
|
83
|
-
WHERE tenant_id = ?
|
|
84
|
-
ORDER BY received_at ASC
|
|
85
|
-
LIMIT ?`)
|
|
86
|
-
.all(tenantId, limit);
|
|
87
|
-
return rows.map(rowToItem);
|
|
88
|
-
}
|
|
89
|
-
function slackDlqEntryAt(db, id) {
|
|
90
|
-
// SAFETY: row shape matches the columns named in the SELECT below.
|
|
91
|
-
const row = db
|
|
92
|
-
.prepare(`SELECT id, tenant_id, team_id, raw_payload, error, received_at, retried_at,
|
|
93
|
-
bucket, retry_count, signature, slack_timestamp
|
|
94
|
-
FROM slack_dlq
|
|
95
|
-
WHERE id = ?`)
|
|
96
|
-
.get(id);
|
|
97
|
-
if (!row)
|
|
98
|
-
return null;
|
|
99
|
-
return rowToItem(row);
|
|
100
|
-
}
|
|
101
|
-
function markSlackDlqRetriedAt(db, id) {
|
|
102
|
-
db.prepare(`UPDATE slack_dlq SET retried_at = ?, retry_count = retry_count + 1 WHERE id = ?`)
|
|
103
|
-
.run(new Date().toISOString(), id);
|
|
104
|
-
}
|
|
105
|
-
/** Counts a failed replay and leaves `retried_at` alone, so the row still reads as never drained. */
|
|
106
|
-
export function bumpSlackDlqRetryCountAt(db, id) {
|
|
107
|
-
db.prepare(`UPDATE slack_dlq SET retry_count = retry_count + 1 WHERE id = ?`).run(id);
|
|
108
|
-
}
|
|
109
68
|
export function insertSlackDlq(hippoRoot, row) {
|
|
110
|
-
return onHandle(hippoRoot, (db) =>
|
|
69
|
+
return onHandle(hippoRoot, (db) => insertDlqRow(db, 'slack_dlq', [
|
|
70
|
+
['tenant_id', row.tenantId],
|
|
71
|
+
['team_id', row.teamId ?? null],
|
|
72
|
+
['raw_payload', row.rawPayload],
|
|
73
|
+
['error', row.error],
|
|
74
|
+
['bucket', row.bucket],
|
|
75
|
+
['signature', row.signature],
|
|
76
|
+
['slack_timestamp', row.slackTimestamp ?? null],
|
|
77
|
+
]));
|
|
111
78
|
}
|
|
112
79
|
export function listSlackDlq(hippoRoot, tenantId, limit) {
|
|
113
|
-
return onHandle(hippoRoot, (db) =>
|
|
80
|
+
return onHandle(hippoRoot, (db) => listDlqRows(db, 'slack_dlq', SELECT_COLUMNS, tenantId, limit).map(rowToItem));
|
|
114
81
|
}
|
|
115
82
|
export function slackDlqEntry(hippoRoot, id) {
|
|
116
|
-
return onHandle(hippoRoot, (db) =>
|
|
83
|
+
return onHandle(hippoRoot, (db) => {
|
|
84
|
+
const row = getDlqRow(db, 'slack_dlq', SELECT_COLUMNS, id);
|
|
85
|
+
return row ? rowToItem(row) : null;
|
|
86
|
+
});
|
|
117
87
|
}
|
|
118
88
|
export function markSlackDlqRetried(hippoRoot, id) {
|
|
119
|
-
onHandle(hippoRoot, (db) =>
|
|
120
|
-
}
|
|
121
|
-
export function bumpSlackDlqRetryCount(hippoRoot, id) {
|
|
122
|
-
onHandle(hippoRoot, (db) => bumpSlackDlqRetryCountAt(db, id));
|
|
89
|
+
onHandle(hippoRoot, (db) => markDlqRetried(db, 'slack_dlq', id));
|
|
123
90
|
}
|
|
124
91
|
export function slackTeamRouteAt(db, teamId) {
|
|
125
92
|
// SAFETY: the SELECT names only `tenant_id`; .get() returns undefined when no row matches.
|
|
@@ -8,7 +8,9 @@ import { storedVectorIds } from '../db/vector-store.js';
|
|
|
8
8
|
import { onHandle, openStore } from './open.js';
|
|
9
9
|
import { QUARANTINE_SCOPE_PREFIX } from './quarantine.js';
|
|
10
10
|
import { parseJsonArray } from './rows.js';
|
|
11
|
-
|
|
11
|
+
// Full content stays: dashboard-snapshot's Fact.lower serves search over the whole text, so a preview would change which rows match.
|
|
12
|
+
const LIVE_COLUMNS = 'id, created, last_retrieved, retrieval_count, half_life_days, layer, tags_json, emotional_valence, outcome_positive, ' +
|
|
13
|
+
'outcome_negative, pinned, confidence, content, superseded_by, kind, scope, origin_project';
|
|
12
14
|
// SQL twins of isQuarantineScope and of the two halves of isLiveMemory; substr compares bytes, as startsWith does, where LIKE would fold case.
|
|
13
15
|
const QUARANTINED = `(scope IS NOT NULL AND substr(scope, 1, ${QUARANTINE_SCOPE_PREFIX.length}) = '${QUARANTINE_SCOPE_PREFIX}')`;
|
|
14
16
|
const SUPERSEDED_BY = `(superseded_by IS NOT NULL AND superseded_by <> '')`;
|
|
@@ -44,11 +46,11 @@ export function loadDashboardRows(hippoRoot, tenantId) {
|
|
|
44
46
|
// SAFETY: the SELECT names exactly LiveRow's columns.
|
|
45
47
|
const live = db.prepare(`SELECT ${LIVE_COLUMNS} FROM memories WHERE tenant_id = ? AND ${LIVE} ORDER BY created ASC, id ASC`).all(tenantId);
|
|
46
48
|
// SAFETY: the SELECT names exactly these two fields, and `why` is one of the three CASE arms or NULL.
|
|
47
|
-
const counts = db.prepare(`
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
`).all(tenantId);
|
|
49
|
+
const counts = db.prepare(`\n SELECT CASE WHEN ${QUARANTINED} THEN 'quarantined' ` +
|
|
50
|
+
`WHEN ${SUPERSEDED_BY} OR ${KIND} = 'superseded' THEN 'superseded' ` +
|
|
51
|
+
`WHEN ${KIND} = 'archived' THEN 'archived' END AS why,\n` +
|
|
52
|
+
` COUNT(*) AS n\n` +
|
|
53
|
+
` FROM memories WHERE tenant_id = ? AND NOT (${LIVE}) GROUP BY why\n `).all(tenantId);
|
|
52
54
|
const excluded = { superseded: 0, archived: 0, quarantined: 0 };
|
|
53
55
|
for (const { why, n } of counts)
|
|
54
56
|
if (why !== null)
|
|
@@ -26,6 +26,12 @@ export declare function deleteEntry(hippoRoot: string, id: string, opts?: {
|
|
|
26
26
|
reason?: string;
|
|
27
27
|
automatic?: boolean;
|
|
28
28
|
}): boolean;
|
|
29
|
+
/** deleteEntry with `automatic` for each removal and its own forget row and reason, all in one transaction after one read of the id set.
|
|
30
|
+
* Returns the ids deleted, in `removals` order; a missing, repeated, pinned, raw or object-backing id is left out. */
|
|
31
|
+
export declare function deleteEntriesAutomatically(hippoRoot: string, removals: readonly {
|
|
32
|
+
id: string;
|
|
33
|
+
reason: string;
|
|
34
|
+
}[], actor: string): string[];
|
|
29
35
|
/** Each row as the caller loaded it, by id; a `ReadonlyMap` fits. */
|
|
30
36
|
export type LoadedRows = Pick<ReadonlyMap<string, MemoryEntry>, 'get'>;
|
|
31
37
|
/** Writes, deletes and dormant moves in one transaction. With `snapshot`, a write keeps only the fields the caller changed and never resurrects a gone row.
|
|
@@ -2,7 +2,8 @@ import { DEFAULT_TENANT_ID } from '../util/env.js';
|
|
|
2
2
|
import { RawAppendOnlyError } from '../core/raw-append-only.js';
|
|
3
3
|
import { AUTO_DELETABLE_SQL } from './rule-sql.js';
|
|
4
4
|
import { closeHippoDb, withWriteScope } from '../db/index.js';
|
|
5
|
-
import { checkRejectionGuard
|
|
5
|
+
import { checkRejectionGuard } from './rejection.js';
|
|
6
|
+
import { RejectedValueError } from '../core/api-errors.js';
|
|
6
7
|
import { markSummaryDirtyInTx } from './summary-dirty.js';
|
|
7
8
|
import { insertDormantRow } from './dormant.js';
|
|
8
9
|
import { log } from '../util/log.js';
|
|
@@ -12,6 +13,8 @@ import { deleteFtsRow, replaceFtsRows, stampOriginProject, upsertMemoryRow } fro
|
|
|
12
13
|
import { purgeMirrorBestEffort, mirrorBestEffort, writeMarkdownMirror } from './mirrors.js';
|
|
13
14
|
import { onHandle, openStore } from './open.js';
|
|
14
15
|
import { clock } from '../util/write-budget.js';
|
|
16
|
+
import { chunked } from '../util/chunked.js';
|
|
17
|
+
import { placeholders } from '../util/sql.js';
|
|
15
18
|
/** Tables whose rows keep a first-class object's backing memory in `memory_id` (ON DELETE SET NULL); tests/dormant-memories.test.ts pins it to the schema. */
|
|
16
19
|
export const MEMORY_BACKED_TABLES = ['predictions', 'decisions', 'incidents', 'processes', 'policies', 'skills', 'project_briefs', 'customer_notes'];
|
|
17
20
|
/** Deleting a memory that backs an object nulls the object's link, and no restore can repair it, so no automatic pass may. */
|
|
@@ -61,8 +64,10 @@ export function deleteEntryCore(db, id, opts) {
|
|
|
61
64
|
const row = db
|
|
62
65
|
.prepare(`SELECT id, tenant_id, dag_parent_id FROM memories WHERE id = ?`)
|
|
63
66
|
.get(id);
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
return row?.id ? deleteTargetRow(db, row, opts) : null;
|
|
68
|
+
}
|
|
69
|
+
function deleteTargetRow(db, row, opts) {
|
|
70
|
+
const { id } = row;
|
|
66
71
|
const guard = opts?.automatic ? ` AND ${AUTOMATIC_DELETE_SQL}` : '';
|
|
67
72
|
if (deleteMemoryRow(db, `DELETE FROM memories WHERE id = ?${guard}`, id) === 0)
|
|
68
73
|
return null;
|
|
@@ -92,6 +97,35 @@ function deleteEntryOn(db, hippoRoot, id, opts) {
|
|
|
92
97
|
purgeMirrorBestEffort(hippoRoot, id, false, 'deleteEntry');
|
|
93
98
|
return true;
|
|
94
99
|
}
|
|
100
|
+
/** deleteEntry with `automatic` for each removal and its own forget row and reason, all in one transaction after one read of the id set.
|
|
101
|
+
* Returns the ids deleted, in `removals` order; a missing, repeated, pinned, raw or object-backing id is left out. */
|
|
102
|
+
export function deleteEntriesAutomatically(hippoRoot, removals, actor) {
|
|
103
|
+
if (removals.length === 0)
|
|
104
|
+
return [];
|
|
105
|
+
const deleted = onHandle(hippoRoot, (db) => withWriteScope(db, 'delete_entries_automatically', () => {
|
|
106
|
+
const rows = selectDeleteTargets(db, removals.map((removal) => removal.id));
|
|
107
|
+
return removals
|
|
108
|
+
.filter(({ id, reason }) => {
|
|
109
|
+
const row = rows.get(id);
|
|
110
|
+
return row !== undefined && deleteTargetRow(db, row, { actor, reason, automatic: true }) !== null;
|
|
111
|
+
})
|
|
112
|
+
.map((removal) => removal.id);
|
|
113
|
+
}), openStore);
|
|
114
|
+
for (const id of deleted)
|
|
115
|
+
purgeMirrorBestEffort(hippoRoot, id, false, 'deleteEntry');
|
|
116
|
+
return deleted;
|
|
117
|
+
}
|
|
118
|
+
function selectDeleteTargets(db, ids) {
|
|
119
|
+
const rows = new Map();
|
|
120
|
+
for (const chunk of chunked([...new Set(ids)])) {
|
|
121
|
+
// SAFETY: rows' shape matches the three columns named in the SELECT.
|
|
122
|
+
const found = db.prepare(`SELECT id, tenant_id, dag_parent_id FROM memories WHERE id IN (${placeholders(chunk.length)})`)
|
|
123
|
+
.all(...chunk);
|
|
124
|
+
for (const row of found)
|
|
125
|
+
rows.set(row.id, row);
|
|
126
|
+
}
|
|
127
|
+
return rows;
|
|
128
|
+
}
|
|
95
129
|
// The child fields a level-2/3 summary is built from (loadChildrenOfSummary, generateDagSummary).
|
|
96
130
|
const SUMMARY_INPUTS = ['content', 'created', 'dag_parent_id', 'kind'];
|
|
97
131
|
function mergeOwnChanges(base, ours, live) {
|
|
@@ -215,7 +249,7 @@ function moveDormantAndDelete(db, dormantMoves, deletableIds, out) {
|
|
|
215
249
|
/** The still auto-deletable rows among `ids`, recording each one's DAG parent as dirty. Placeholders come from `ids` itself. */
|
|
216
250
|
function selectAutoDeletableRows(db, ids, dirty) {
|
|
217
251
|
// SAFETY: rows' shape matches the three columns named in the SELECT.
|
|
218
|
-
const rows = db.prepare(`SELECT id, dag_parent_id, tenant_id FROM memories WHERE id IN (${ids.
|
|
252
|
+
const rows = db.prepare(`SELECT id, dag_parent_id, tenant_id FROM memories WHERE id IN (${placeholders(ids.length)}) AND ${AUTOMATIC_DELETE_SQL}`).all(...ids);
|
|
219
253
|
for (const row of rows) {
|
|
220
254
|
if (row.dag_parent_id) {
|
|
221
255
|
dirty.parents.add(row.dag_parent_id);
|
|
@@ -254,13 +288,13 @@ function applyBatchWrites(db, stampedWrites, snapshot, out) {
|
|
|
254
288
|
// Hook for writes: a new child, or a change to what its summary reads, marks the parent dirty; decay alone does not.
|
|
255
289
|
if (row.dag_parent_id && (!live || SUMMARY_INPUTS.some((k) => row[k] !== live[k]))) {
|
|
256
290
|
dirty.parents.add(row.dag_parent_id);
|
|
257
|
-
dirty.tenantById.set(row.dag_parent_id, row.
|
|
291
|
+
dirty.tenantById.set(row.dag_parent_id, row.tenant_id);
|
|
258
292
|
}
|
|
259
293
|
}
|
|
260
294
|
}
|
|
261
295
|
/** True, after auditing the refusal, when the write would introduce a rejected value. */
|
|
262
296
|
function isRejectedBatchWrite(db, row) {
|
|
263
|
-
const entryTenantId = row.
|
|
297
|
+
const entryTenantId = row.tenant_id ?? DEFAULT_TENANT_ID;
|
|
264
298
|
// checkRejectionGuard, not a bare tombstone probe: a tombstone can coexist with a live same-content row,
|
|
265
299
|
// and skipping every re-persist would starve it of decay/replay updates; only new or changed content is refused.
|
|
266
300
|
try {
|