hippo-memory 1.71.0 → 1.72.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/agent-memories/apply.d.ts +0 -11
- package/dist/agent-memories/apply.js +6 -21
- 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 +1 -1
- package/dist/api/audit.d.ts +29 -1
- package/dist/api/audit.js +44 -5
- package/dist/api/auth.d.ts +7 -4
- package/dist/api/auth.js +12 -8
- 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-render.js +4 -3
- package/dist/api/context-select.js +9 -11
- package/dist/api/context.js +17 -8
- 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 +1 -1
- 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 +6 -2
- package/dist/api/forget.js +18 -7
- package/dist/api/goals.d.ts +1 -1
- package/dist/api/goals.js +5 -9
- 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 +4 -0
- package/dist/api/learn.js +53 -27
- package/dist/api/ledger-db.d.ts +2 -6
- package/dist/api/ledger-db.js +4 -36
- package/dist/api/memories.d.ts +19 -1
- package/dist/api/memories.js +39 -0
- package/dist/api/objects.d.ts +1 -1
- package/dist/api/outcome.js +1 -1
- 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 +6 -0
- package/dist/api/projects.d.ts +21 -0
- package/dist/api/projects.js +24 -0
- package/dist/api/promote.js +1 -1
- package/dist/api/prompt-hook.d.ts +15 -2
- package/dist/api/prompt-hook.js +44 -4
- package/dist/api/recall-cli.d.ts +3 -0
- package/dist/api/recall-cli.js +13 -4
- package/dist/api/recall-core.js +3 -3
- package/dist/api/recall-pipeline.js +6 -7
- package/dist/api/recall-request.js +1 -1
- package/dist/api/recall.d.ts +0 -3
- package/dist/api/recall.js +19 -44
- package/dist/api/remember-local.js +1 -1
- package/dist/api/remember.d.ts +2 -1
- package/dist/api/remember.js +3 -2
- package/dist/api/session-end.d.ts +22 -0
- package/dist/api/session-end.js +101 -0
- package/dist/api/sleep-run.d.ts +1 -1
- package/dist/api/sleep-run.js +13 -9
- 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 +1 -1
- package/dist/api/types.d.ts +2 -0
- 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 +4 -2
- package/dist/capture/compaction-spool.js +20 -6
- 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 +2 -1
- package/dist/capture/session-worker.js +3 -4
- 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 +13 -13
- package/dist/cli/auth.d.ts +1 -1
- package/dist/cli/auth.js +12 -13
- 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 +5 -0
- package/dist/cli/client.js +24 -8
- package/dist/cli/compact-resume-payload.js +1 -1
- package/dist/cli/context.d.ts +1 -1
- package/dist/cli/context.js +12 -12
- package/dist/cli/continuity.d.ts +1 -1
- package/dist/cli/continuity.js +36 -39
- package/dist/cli/curate.d.ts +1 -1
- package/dist/cli/curate.js +23 -27
- 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 +4 -281
- 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 +3 -5
- package/dist/cli/hook-runtime.js +7 -34
- 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/lenient-id.d.ts +2 -0
- package/dist/cli/lenient-id.js +13 -0
- 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/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 +175 -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 +17 -17
- package/dist/cli/remember.js +12 -12
- package/dist/cli/scheduler.js +2 -1
- package/dist/cli/serve.js +3 -3
- package/dist/cli/session-hooks.d.ts +1 -1
- package/dist/cli/session-hooks.js +45 -115
- 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 +1 -1
- package/dist/cli/transfer.js +33 -31
- package/dist/cli/usage.d.ts +0 -79
- package/dist/cli/usage.js +1 -695
- package/dist/cli/verb-row.d.ts +32 -0
- package/dist/cli/verb-row.js +6 -0
- package/dist/cli/verbs/health.d.ts +27 -0
- package/dist/cli/verbs/health.js +232 -0
- package/dist/cli/verbs/memory.d.ts +16 -0
- package/dist/cli/verbs/memory.js +244 -0
- package/dist/cli/verbs/objects.d.ts +20 -0
- package/dist/cli/verbs/objects.js +254 -0
- package/dist/cli/verbs/sessions.d.ts +31 -0
- package/dist/cli/verbs/sessions.js +312 -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 +23 -313
- package/dist/connectors/dlq.d.ts +39 -3
- package/dist/connectors/dlq.js +37 -3
- package/dist/connectors/github/backfill.js +12 -3
- package/dist/connectors/github/dlq.js +31 -73
- package/dist/connectors/github/ingest.d.ts +2 -6
- package/dist/connectors/github/ingest.js +3 -28
- package/dist/connectors/github/webhook.d.ts +2 -1
- package/dist/connectors/github/webhook.js +33 -2
- package/dist/connectors/ingest.d.ts +10 -0
- package/dist/connectors/ingest.js +28 -0
- package/dist/connectors/slack/dlq.js +49 -71
- package/dist/connectors/slack/ingest.d.ts +2 -6
- package/dist/connectors/slack/ingest.js +2 -27
- package/dist/connectors/slack/webhook.d.ts +2 -1
- package/dist/connectors/slack/webhook.js +2 -1
- package/dist/consolidate/conflicts.js +1 -1
- package/dist/consolidate/dag.js +15 -9
- package/dist/consolidate/dedupe.d.ts +7 -0
- package/dist/consolidate/dedupe.js +5 -3
- package/dist/consolidate/llm-passes.js +45 -19
- package/dist/consolidate/memory-value.js +2 -1
- package/dist/consolidate/merge.js +2 -2
- 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 +8 -5
- package/dist/consolidate/traces.js +3 -3
- 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 +7 -2
- 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 +2 -2
- package/dist/core/memory-audit.d.ts +17 -0
- package/dist/core/memory-audit.js +54 -0
- package/dist/core/memory.d.ts +10 -4
- package/dist/core/memory.js +25 -18
- package/dist/{consolidate → core}/merged-row.d.ts +1 -1
- package/dist/{consolidate → core}/merged-row.js +1 -1
- package/dist/core/physics.js +6 -16
- 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 +10 -1
- package/dist/{store → core}/recall-scope.d.ts +1 -10
- package/dist/{store → core}/recall-scope.js +2 -19
- package/dist/db/busy.d.ts +2 -7
- package/dist/db/busy.js +21 -26
- package/dist/db/connect.d.ts +0 -1
- package/dist/db/connect.js +5 -6
- package/dist/db/index.d.ts +3 -3
- package/dist/db/index.js +3 -3
- package/dist/db/migrations/index.d.ts +1 -1
- package/dist/db/migrations/index.js +3 -1
- 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.js +3 -3
- package/dist/db/tables.d.ts +2 -0
- package/dist/db/tables.js +6 -2
- package/dist/db/vector-store.js +17 -4
- package/dist/db/wal-checkpointer.js +2 -1
- package/dist/doctor.js +11 -8
- 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 +18 -15
- 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/eval/eval-suite.js +17 -11
- package/dist/eval/eval.js +1 -1
- package/dist/eval/judgment.js +11 -4
- 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 +4 -2
- 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 +1 -1
- package/dist/importers/sources.js +5 -4
- package/dist/importers/vault.js +7 -5
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/learn/autolearn.d.ts +10 -2
- package/dist/learn/autolearn.js +32 -12
- package/dist/learn/churn-git.js +13 -4
- package/dist/learn/extract.d.ts +3 -2
- package/dist/learn/extract.js +6 -4
- package/dist/learn/forward-claim-detector.js +4 -2
- package/dist/learn/invalidation.js +10 -6
- 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.js +3 -1
- 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 +2 -1
- package/dist/mcp/stdio.js +24 -8
- 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 +63 -97
- 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 +9 -0
- package/dist/search/embed-memories.js +377 -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 +10 -13
- package/dist/server/auth.d.ts +2 -0
- package/dist/server/auth.js +17 -7
- package/dist/server/boot.js +7 -66
- package/dist/server/client-ip.d.ts +0 -2
- package/dist/server/client-ip.js +3 -2
- 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 -132
- 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 +15 -38
- 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/types.d.ts +22 -10
- package/dist/server/validation.d.ts +1 -2
- package/dist/server/validation.js +6 -11
- package/dist/server.d.ts +5 -4
- package/dist/server.js +5 -4
- package/dist/sharing/global-store.js +2 -2
- 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 +1 -1
- package/dist/sharing/share.d.ts +2 -0
- package/dist/sharing/share.js +37 -34
- package/dist/store/ambient.js +5 -6
- 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 +42 -23
- package/dist/store/capture-write.d.ts +17 -0
- package/dist/store/capture-write.js +35 -0
- package/dist/store/cards.js +1 -1
- 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 +417 -0
- package/dist/store/compactions.js +6 -4
- package/dist/store/conflicts.js +7 -4
- package/dist/store/connectors/dlq.d.ts +10 -0
- package/dist/store/connectors/dlq.js +32 -0
- package/dist/store/connectors/github.d.ts +7 -9
- package/dist/store/connectors/github.js +30 -53
- package/dist/store/connectors/slack.d.ts +2 -11
- package/dist/store/connectors/slack.js +18 -47
- package/dist/store/dashboard-reads.js +7 -6
- package/dist/store/delete-and-batch.d.ts +6 -0
- package/dist/store/delete-and-batch.js +36 -3
- package/dist/store/delivery-recorder.d.ts +10 -3
- package/dist/store/delivery-recorder.js +12 -5
- package/dist/store/descendant-page.js +6 -3
- package/dist/store/dormant.d.ts +1 -1
- package/dist/store/dormant.js +3 -1
- package/dist/store/embeddings/index.d.ts +2 -12
- package/dist/store/embeddings/index.js +5 -374
- package/dist/store/entry-reads.d.ts +13 -4
- package/dist/store/entry-reads.js +47 -11
- package/dist/store/entry-writes.d.ts +5 -5
- package/dist/store/entry-writes.js +32 -12
- 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 +20 -18
- package/dist/store/graph-reads.js +4 -2
- package/dist/store/graph-writes.d.ts +6 -6
- package/dist/store/graph-writes.js +48 -71
- package/dist/store/handoffs.d.ts +1 -1
- package/dist/store/handoffs.js +6 -6
- package/dist/store/index-and-stats.js +2 -2
- 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 +10 -6
- package/dist/store/open.d.ts +0 -4
- package/dist/store/open.js +40 -53
- package/dist/store/pilot-arm.d.ts +12 -0
- package/dist/store/pilot-arm.js +42 -0
- package/dist/store/port.d.ts +9 -3
- 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/quarantine.js +2 -1
- package/dist/store/recall-trace.d.ts +2 -0
- package/dist/store/recall-trace.js +18 -2
- package/dist/store/rejected-values.js +2 -1
- 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/rule-sql.d.ts +9 -0
- package/dist/store/rule-sql.js +21 -2
- package/dist/store/search-rows.js +9 -4
- 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 +3 -3
- package/dist/store/sqlite/entry-writes-group.js +2 -2
- 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 +9 -0
- package/dist/store/sqlite/held-connection.js +41 -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 +1 -1
- package/dist/store/sqlite/store.js +14 -12
- package/dist/store/summaries.d.ts +3 -3
- package/dist/store/summaries.js +5 -4
- package/dist/store/token-ledger.d.ts +0 -7
- package/dist/store/token-ledger.js +0 -35
- package/dist/store/vector-index.js +3 -2
- 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 +3 -0
- package/dist/util/anthropic-messages.js +22 -5
- 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 +2 -1
- package/dist/util/env.js +2 -1
- package/dist/util/format.d.ts +3 -0
- package/dist/util/format.js +6 -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 +3 -1
- package/dist/util/request-scope.js +11 -6
- package/dist/util/secret-detect.js +6 -1
- 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/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 +2 -2
- 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
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
// The compaction record's SQL: one record per Claude Code compaction, and the rows its summary's items become.
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { ConflictError } from '../core/api-errors.js';
|
|
4
|
+
import { COMPACTION_DB_WAIT_MS, REPLAY_AFTER_MS, TRANSCRIPT_FILL_WINDOW_MS } from '../core/compaction-timing.js';
|
|
5
|
+
import { isStringValue } from '../core/capture-contract.js';
|
|
6
|
+
import { selectItemRows } from '../util/compaction-items.js';
|
|
7
|
+
import { isSharedStore, loadConfig } from '../core/config.js';
|
|
8
|
+
import { closeHippoDb, isSqliteBusy, openHippoDb, withWriteScopeOr } from '../db/index.js';
|
|
9
|
+
import { gatedWrite } from './gated-write.js';
|
|
10
|
+
import { COMPACTION_MEMORY_TAG, COMPACTION_SOURCE_PREFIX, Layer, createMemory, generateId } from '../core/memory.js';
|
|
11
|
+
import { fallbackOrigin, isGlobalStoreRoot, projectId, projectNames, resolveProjectIdentity } from '../core/project-identity.js';
|
|
12
|
+
import { closeStartedWithoutSummary, compactionProgress, compactionRowsByRequest, heldMemoryRows, insertStartedCompaction, insertSummarisedCompaction, latestCompactionRows, latestStartedRows, markCompactionDone, markCompactionSummarised, markSnapshotSavedRow, nextCompactionStart, openTranscriptRows, stalledSummarisedRows, } from './compactions.js';
|
|
13
|
+
import { strengthenRetrievedOn, writeEntryMirrors } from './entry-writes.js';
|
|
14
|
+
import { isRecallBoostAblated } from '../core/ablation.js';
|
|
15
|
+
import { updateStats } from './index-and-stats.js';
|
|
16
|
+
import { resolveTenantId } from './tenant.js';
|
|
17
|
+
import { errorMessage, reportCompactionFailure } from '../util/log.js';
|
|
18
|
+
/** The marker redactSecretsStrict writes; an item holding it was a secret before it was stored. */
|
|
19
|
+
const REDACTED = '[REDACTED]';
|
|
20
|
+
/** Where the session ran: rows written through the global store or a shared store keep the project the session was in. */
|
|
21
|
+
function compactionProject(hippoRoot, cwd) {
|
|
22
|
+
if (!isGlobalStoreRoot(hippoRoot) && !isSharedStore(hippoRoot))
|
|
23
|
+
return resolveProjectIdentity(path.dirname(hippoRoot));
|
|
24
|
+
// No cwd means user-global, as stampOriginProject gives the global store; undefined would fall back to the hook's own cwd.
|
|
25
|
+
return cwd === null ? '' : resolveProjectIdentity(cwd);
|
|
26
|
+
}
|
|
27
|
+
/** The record column cannot hold NULL, but an item from an unknown folder on a shared store must not read as user-global. */
|
|
28
|
+
function itemOrigin(hippoRoot, ctx) {
|
|
29
|
+
return ctx.cwd === null && isSharedStore(hippoRoot) ? fallbackOrigin(hippoRoot) : ctx.originProject;
|
|
30
|
+
}
|
|
31
|
+
function compactionOrigin(hippoRoot, cwd) {
|
|
32
|
+
return projectId(compactionProject(hippoRoot, cwd));
|
|
33
|
+
}
|
|
34
|
+
/** The names a record's held rows may carry: its origin, plus the folder name rows saved before project ids used. */
|
|
35
|
+
function heldOrigins(hippoRoot, cwd, originProject) {
|
|
36
|
+
const names = projectNames(compactionProject(hippoRoot, cwd));
|
|
37
|
+
return names.includes(originProject) ? names : [originProject];
|
|
38
|
+
}
|
|
39
|
+
function toRecord(row) {
|
|
40
|
+
const listed = row.items_json === null ? [] : JSON.parse(row.items_json);
|
|
41
|
+
return {
|
|
42
|
+
id: row.id,
|
|
43
|
+
tenantId: row.tenant_id,
|
|
44
|
+
sessionId: row.session_id,
|
|
45
|
+
originProject: row.origin_project,
|
|
46
|
+
trigger: row.compact_trigger,
|
|
47
|
+
cwd: row.cwd,
|
|
48
|
+
transcriptPath: row.transcript_path,
|
|
49
|
+
snapshotSaved: row.snapshot_saved === 1,
|
|
50
|
+
startedAt: row.started_at,
|
|
51
|
+
summarisedAt: row.summarised_at,
|
|
52
|
+
summary: row.summary,
|
|
53
|
+
items: Array.isArray(listed) ? listed.filter(isStringValue) : [],
|
|
54
|
+
itemsWritten: row.items_written,
|
|
55
|
+
status: row.status,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export function startCompaction(db, tenantId, start, at = new Date()) {
|
|
59
|
+
const id = generateId('cmp');
|
|
60
|
+
insertStartedCompaction(db, tenantId, { id, ...start, startedAt: at.toISOString() });
|
|
61
|
+
return id;
|
|
62
|
+
}
|
|
63
|
+
/** A session's newest record, or null. */
|
|
64
|
+
export function latestCompaction(db, tenantId, sessionId) {
|
|
65
|
+
return latestCompactionRows(db, tenantId, sessionId).map(toRecord)[0] ?? null;
|
|
66
|
+
}
|
|
67
|
+
/** The record a caller's request made, whatever state it reached, so a retry neither writes its items twice nor starts a second record.
|
|
68
|
+
* Another session's id is a ConflictError. */
|
|
69
|
+
export function compactionByRequest(db, tenantId, requestId, sessionId) {
|
|
70
|
+
const record = compactionRowsByRequest(db, tenantId, requestId).map(toRecord)[0] ?? null;
|
|
71
|
+
// Sessions are owner-bound, so this also keeps one owner from reading or finishing another's record.
|
|
72
|
+
if (record !== null && record.sessionId !== sessionId)
|
|
73
|
+
throw new ConflictError('request id belongs to another session');
|
|
74
|
+
return record;
|
|
75
|
+
}
|
|
76
|
+
/** Pre-compact's record for the compaction that is ending: the session's newest `started` one within REPLAY_AFTER_MS before `at`.
|
|
77
|
+
* An older one is left for the transcript fill. */
|
|
78
|
+
function latestStarted(db, tenantId, sessionId, at) {
|
|
79
|
+
return latestStartedRows(db, tenantId, sessionId, at.toISOString(), new Date(at.getTime() - REPLAY_AFTER_MS).toISOString()).map(toRecord)[0] ?? null;
|
|
80
|
+
}
|
|
81
|
+
function markSummarised(db, tenantId, id, options) {
|
|
82
|
+
const { text, summarisedAt, requestId } = options;
|
|
83
|
+
return markCompactionSummarised(db, tenantId, id, { summary: text.summary, itemsJson: JSON.stringify(text.items), summarisedAt, requestId });
|
|
84
|
+
}
|
|
85
|
+
/** Best effort, never throws: a compaction must not fail because its record could not be written. */
|
|
86
|
+
export function recordCompactionStart(hippoRoot, start, log) {
|
|
87
|
+
let db;
|
|
88
|
+
try {
|
|
89
|
+
db = openHippoDb(hippoRoot, { busyWaitMs: COMPACTION_DB_WAIT_MS });
|
|
90
|
+
return startCompaction(db, resolveTenantId({}), { ...start, originProject: compactionOrigin(hippoRoot, start.cwd) });
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
log(`compaction record not started: ${errorMessage(err)}`);
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
if (db)
|
|
98
|
+
closeHippoDb(db);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/** On the caller's handle and tenant, so a server marks the record under the caller's tenant with its own wait. */
|
|
102
|
+
export function markSnapshotSaved(db, tenantId, recordId) {
|
|
103
|
+
markSnapshotSavedRow(db, tenantId, recordId);
|
|
104
|
+
}
|
|
105
|
+
export function recordSnapshotSaved(hippoRoot, tenantId, recordId, log) {
|
|
106
|
+
let db;
|
|
107
|
+
try {
|
|
108
|
+
db = openHippoDb(hippoRoot, { busyWaitMs: COMPACTION_DB_WAIT_MS });
|
|
109
|
+
markSnapshotSaved(db, tenantId, recordId);
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
log(`compaction record not marked with its snapshot: ${errorMessage(err)}`);
|
|
113
|
+
}
|
|
114
|
+
finally {
|
|
115
|
+
if (db)
|
|
116
|
+
closeHippoDb(db);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/** Puts the summary on the session's `started` record, or inserts a `summarised` one when pre-compact wrote none. One statement each. */
|
|
120
|
+
export function recordSummary(db, hippoRoot, tenantId, options) {
|
|
121
|
+
const { meta, text, at, caller = {} } = options;
|
|
122
|
+
const now = new Date().toISOString();
|
|
123
|
+
const itemsJson = JSON.stringify(text.items);
|
|
124
|
+
const { requestId } = caller;
|
|
125
|
+
const started = latestStarted(db, tenantId, meta.sessionId, at);
|
|
126
|
+
if (started && markSummarised(db, tenantId, started.id, { text, summarisedAt: now, requestId })) {
|
|
127
|
+
return { ...started, summary: text.summary, items: text.items, summarisedAt: now, status: 'summarised' };
|
|
128
|
+
}
|
|
129
|
+
const originProject = caller.originProject ?? compactionOrigin(hippoRoot, meta.cwd);
|
|
130
|
+
const id = generateId('cmp');
|
|
131
|
+
insertSummarisedCompaction(db, tenantId, { ...meta, originProject, startedAt: at.toISOString() }, { id, summary: text.summary, itemsJson, summarisedAt: now, requestId });
|
|
132
|
+
return {
|
|
133
|
+
id, tenantId, sessionId: meta.sessionId, originProject, trigger: meta.trigger, cwd: meta.cwd, transcriptPath: meta.transcriptPath,
|
|
134
|
+
snapshotSaved: false, startedAt: at.toISOString(), summarisedAt: now, summary: text.summary, items: text.items, itemsWritten: 0, status: 'summarised',
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/** Words whose loss reverses or narrows a statement: "do not", "can't", "only", "unless". */
|
|
138
|
+
const PROTECTED_WORDS = new Set(['not', 'no', 'never', 'none', 'nor', 'without', 'cannot', 't', 'only', 'except', 'unless', 'until']);
|
|
139
|
+
/** Below this share of the held text's words, an item is a fragment of something longer, not a restatement. */
|
|
140
|
+
const RESTATE_MIN_SHARE = 0.5;
|
|
141
|
+
function words(text) {
|
|
142
|
+
return text.normalize('NFC').toLowerCase().split(/[^\p{L}\p{M}\p{N}_]+/u).filter(Boolean);
|
|
143
|
+
}
|
|
144
|
+
/** True when `item` is `held` with words left out, in the same order, keeping every number and protected word. Spacing, case and punctuation may differ. */
|
|
145
|
+
function restates(item, held) {
|
|
146
|
+
if (item.length === 0 || item.length < RESTATE_MIN_SHARE * held.length)
|
|
147
|
+
return false;
|
|
148
|
+
const kept = new Set(item);
|
|
149
|
+
let next = 0;
|
|
150
|
+
for (const w of held) {
|
|
151
|
+
if (next < item.length && w === item[next])
|
|
152
|
+
next++;
|
|
153
|
+
else if (!kept.has(w) && (PROTECTED_WORDS.has(w) || /\p{N}/u.test(w)))
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
return next === item.length;
|
|
157
|
+
}
|
|
158
|
+
/** Live rows of one tenant and origin that default recall shows: a compaction often restates what an earlier one, or the user, already saved. */
|
|
159
|
+
function heldRows(db, tenantId, origins) {
|
|
160
|
+
return heldMemoryRows(db, tenantId, origins).map((r) => ({ id: r.id, sessionId: r.source_session_id, words: words(r.content) }));
|
|
161
|
+
}
|
|
162
|
+
function compactionEntry(text, ctx, origin, baseHalfLifeDays) {
|
|
163
|
+
return {
|
|
164
|
+
...createMemory(text, {
|
|
165
|
+
layer: Layer.Episodic,
|
|
166
|
+
tags: [COMPACTION_MEMORY_TAG],
|
|
167
|
+
source: `${COMPACTION_SOURCE_PREFIX}${ctx.sessionId}`,
|
|
168
|
+
confidence: 'observed',
|
|
169
|
+
kind: 'distilled',
|
|
170
|
+
source_session_id: ctx.sessionId,
|
|
171
|
+
tenantId: ctx.tenantId,
|
|
172
|
+
baseHalfLifeDays,
|
|
173
|
+
}),
|
|
174
|
+
origin_project: origin,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/** Runs inside saveItems' transaction: restatements of held rows are counted, the rest go through the write gate. */
|
|
178
|
+
function writeItemRows(db, hippoRoot, ctx, rows, baseHalfLifeDays) {
|
|
179
|
+
const out = { written: [], repeats: 0, refused: 0, restated: [] };
|
|
180
|
+
const held = heldRows(db, ctx.tenantId, ctx.caller?.origins ?? heldOrigins(hippoRoot, ctx.cwd, ctx.originProject));
|
|
181
|
+
const origin = ctx.caller === undefined ? itemOrigin(hippoRoot, ctx) : ctx.originProject;
|
|
182
|
+
const restated = new Set();
|
|
183
|
+
for (const text of rows) {
|
|
184
|
+
const itemWords = words(text);
|
|
185
|
+
const match = held.find((h) => restates(itemWords, h.words));
|
|
186
|
+
if (match) {
|
|
187
|
+
out.repeats++;
|
|
188
|
+
if (match.id !== null && match.sessionId !== ctx.sessionId)
|
|
189
|
+
restated.add(match.id);
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
// createMemory throws below 3 chars, which would sink the whole transaction.
|
|
193
|
+
if (text.trim().length < 3) {
|
|
194
|
+
out.refused++;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
const entry = compactionEntry(text, ctx, origin, baseHalfLifeDays);
|
|
198
|
+
if (gatedWrite(db, hippoRoot, entry, { actor: ctx.caller?.actor ?? 'post-compact' }) === 'written') {
|
|
199
|
+
out.written.push(entry);
|
|
200
|
+
held.push({ id: null, sessionId: ctx.sessionId, words: itemWords });
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
out.refused++;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
out.restated = [...restated];
|
|
207
|
+
return out;
|
|
208
|
+
}
|
|
209
|
+
/** After commit: report skips, mirror the new rows, bump the counter. */
|
|
210
|
+
function finishItemWrites(hippoRoot, writes, log) {
|
|
211
|
+
if (writes.repeats > 0)
|
|
212
|
+
log(`skipped ${writes.repeats} item(s) the store already holds`);
|
|
213
|
+
if (writes.refused > 0)
|
|
214
|
+
log(`skipped ${writes.refused} item(s) the write gate refused`);
|
|
215
|
+
for (const entry of writes.written)
|
|
216
|
+
writeEntryMirrors(hippoRoot, entry);
|
|
217
|
+
if (writes.written.length > 0) {
|
|
218
|
+
try {
|
|
219
|
+
updateStats(hippoRoot, { remembered: writes.written.length });
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
// The rows are committed; a counter that could not be bumped must not turn that into a failed step.
|
|
223
|
+
log(`remembered counter not updated: ${errorMessage(err)}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/** Items that become rows, then the record `done`, in one transaction so two sessions cannot both insert one text. Mirrors after commit. */
|
|
228
|
+
export function saveItems(db, hippoRoot, ctx, log) {
|
|
229
|
+
const usable = ctx.items.filter((item) => !item.includes(REDACTED));
|
|
230
|
+
if (usable.length < ctx.items.length)
|
|
231
|
+
log(`skipped ${ctx.items.length - usable.length} item(s) as secret`);
|
|
232
|
+
const { rows, tooLong, capped } = selectItemRows(usable);
|
|
233
|
+
if (tooLong > 0)
|
|
234
|
+
log(`item too long: ${tooLong} kept in the record only`);
|
|
235
|
+
if (capped > 0)
|
|
236
|
+
log(`capped: ${capped} more kept in the record only`);
|
|
237
|
+
const baseHalfLifeDays = loadConfig(hippoRoot).defaultHalfLifeDays;
|
|
238
|
+
let finishedItems = 0;
|
|
239
|
+
const writes = withWriteScopeOr(db, 'save_items', (rollback) => {
|
|
240
|
+
if (ctx.recordId !== null) {
|
|
241
|
+
// A replayer that read the record before another finished it must not write its items again.
|
|
242
|
+
const current = compactionProgress(db, ctx.tenantId, ctx.recordId);
|
|
243
|
+
if (current?.status !== 'summarised') {
|
|
244
|
+
finishedItems = current?.items_written ?? 0;
|
|
245
|
+
return rollback(null);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
const written = writeItemRows(db, hippoRoot, ctx, rows, baseHalfLifeDays);
|
|
249
|
+
// Said again by another session is the same signal as being recalled; the same session carrying it forward is not.
|
|
250
|
+
strengthenRetrievedOn(db, written.restated, { tenantId: ctx.tenantId, recallBoostAblated: isRecallBoostAblated() });
|
|
251
|
+
if (ctx.recordId !== null) {
|
|
252
|
+
markCompactionDone(db, ctx.tenantId, ctx.recordId, written.written.length);
|
|
253
|
+
}
|
|
254
|
+
return written;
|
|
255
|
+
});
|
|
256
|
+
if (writes === null) {
|
|
257
|
+
log(`${ctx.recordId} was already finished by another process`);
|
|
258
|
+
return finishedItems;
|
|
259
|
+
}
|
|
260
|
+
finishItemWrites(hippoRoot, writes, log);
|
|
261
|
+
return writes.written.length;
|
|
262
|
+
}
|
|
263
|
+
function recordStep(step) {
|
|
264
|
+
try {
|
|
265
|
+
const record = recordSummary(step.db, step.hippoRoot, step.tenantId, { meta: step.meta, text: step.text, at: step.at });
|
|
266
|
+
step.result.snapshotSaved = record.snapshotSaved;
|
|
267
|
+
return record;
|
|
268
|
+
}
|
|
269
|
+
catch (err) {
|
|
270
|
+
if (isSqliteBusy(err))
|
|
271
|
+
throw err;
|
|
272
|
+
reportCompactionFailure(step.log, 'record step', errorMessage(err));
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
/** Busy with a saved record defers to sleep; any other failure goes to the caller. */
|
|
277
|
+
function itemsStep(step, record) {
|
|
278
|
+
const { meta, hippoRoot, log } = step;
|
|
279
|
+
try {
|
|
280
|
+
step.result.written = saveItems(step.db, hippoRoot, {
|
|
281
|
+
tenantId: step.tenantId,
|
|
282
|
+
recordId: record?.id ?? null,
|
|
283
|
+
sessionId: meta.sessionId,
|
|
284
|
+
originProject: record?.originProject ?? compactionOrigin(hippoRoot, meta.cwd),
|
|
285
|
+
cwd: meta.cwd,
|
|
286
|
+
items: step.text.items,
|
|
287
|
+
}, log);
|
|
288
|
+
}
|
|
289
|
+
catch (err) {
|
|
290
|
+
if (!isSqliteBusy(err) || record === null)
|
|
291
|
+
throw err;
|
|
292
|
+
// The record already holds the items; sleep finishes them.
|
|
293
|
+
step.result.deferred = true;
|
|
294
|
+
log(`store busy, items left for the next sleep: ${errorMessage(err)}`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/** The PostCompact store work, on a handle of its own: record the summary, then write its items. Each step is independent. Never throws. */
|
|
298
|
+
export function saveCompactionAt(hippoRoot, meta, text, at, log) {
|
|
299
|
+
const result = { written: null, deferred: false, snapshotSaved: false };
|
|
300
|
+
let db;
|
|
301
|
+
try {
|
|
302
|
+
db = openHippoDb(hippoRoot, { busyWaitMs: COMPACTION_DB_WAIT_MS });
|
|
303
|
+
const step = { db, hippoRoot, tenantId: resolveTenantId({}), meta, text, at, result, log };
|
|
304
|
+
itemsStep(step, recordStep(step));
|
|
305
|
+
}
|
|
306
|
+
catch (err) {
|
|
307
|
+
if (isSqliteBusy(err) || db === undefined)
|
|
308
|
+
return { result, spoolReason: errorMessage(err) };
|
|
309
|
+
reportCompactionFailure(log, 'items step', errorMessage(err));
|
|
310
|
+
}
|
|
311
|
+
finally {
|
|
312
|
+
if (db)
|
|
313
|
+
closeHippoDb(db);
|
|
314
|
+
}
|
|
315
|
+
return { result, spoolReason: null };
|
|
316
|
+
}
|
|
317
|
+
function closeWithoutSummary(db, tenantId, id, log) {
|
|
318
|
+
if (closeStartedWithoutSummary(db, tenantId, id) > 0)
|
|
319
|
+
log(`${id} closed as no-summary: its transcript holds no summary for it`);
|
|
320
|
+
}
|
|
321
|
+
function nextStartedAt(db, record) {
|
|
322
|
+
return nextCompactionStart(db, record.tenantId, record.sessionId, record.startedAt);
|
|
323
|
+
}
|
|
324
|
+
/** The one place a record becomes the argument saveItems takes. */
|
|
325
|
+
function itemContext(tenantId, record, items = record.items) {
|
|
326
|
+
return { tenantId, recordId: record.id, sessionId: record.sessionId, originProject: record.originProject, cwd: record.cwd, items };
|
|
327
|
+
}
|
|
328
|
+
/** Records a spooled summary, then writes its items. */
|
|
329
|
+
function spoolRecorder(db, hippoRoot, log) {
|
|
330
|
+
return (spooled, recorded) => {
|
|
331
|
+
const record = recordSummary(db, hippoRoot, spooled.tenantId, { meta: spooled.payload, text: spooled.text, at: spooled.at });
|
|
332
|
+
// The record holds the items now, so the file is done even if the write below fails.
|
|
333
|
+
recorded();
|
|
334
|
+
saveItems(db, hippoRoot, itemContext(spooled.tenantId, record), log);
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
/** Runs `step` over each record until the deadline; a record that throws is logged and the rest still run. Returns how many `step` finished. */
|
|
338
|
+
function replayEach(run, records, what, step) {
|
|
339
|
+
let finished = 0;
|
|
340
|
+
for (const record of records) {
|
|
341
|
+
if (Date.now() > run.deadline)
|
|
342
|
+
break;
|
|
343
|
+
try {
|
|
344
|
+
if (step(record))
|
|
345
|
+
finished++;
|
|
346
|
+
}
|
|
347
|
+
catch (err) {
|
|
348
|
+
run.log(`${what} of ${record.id} failed: ${errorMessage(err)}`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return finished;
|
|
352
|
+
}
|
|
353
|
+
function replayStalled(run) {
|
|
354
|
+
const stalled = stalledSummarisedRows(run.db, run.tenantId, new Date(run.now - REPLAY_AFTER_MS).toISOString()).map(toRecord);
|
|
355
|
+
return replayEach(run, stalled, 'replay', (record) => {
|
|
356
|
+
saveItems(run.db, run.hippoRoot, itemContext(run.tenantId, record), run.log);
|
|
357
|
+
return true;
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
function replaySpool(run) {
|
|
361
|
+
try {
|
|
362
|
+
return run.sources.importSpool(run.tenantId, run.deadline, spoolRecorder(run.db, run.hippoRoot, run.log));
|
|
363
|
+
}
|
|
364
|
+
catch (err) {
|
|
365
|
+
run.log(`spool import failed: ${errorMessage(err)}`);
|
|
366
|
+
return 0;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
/** Fills one open record from its transcript; true when it saved items. */
|
|
370
|
+
function fillFromTranscript(run, record) {
|
|
371
|
+
const { db, hippoRoot, tenantId, sources, log } = run;
|
|
372
|
+
if (record.transcriptPath === null || !sources.transcriptExists(record.transcriptPath))
|
|
373
|
+
return false;
|
|
374
|
+
const next = nextStartedAt(db, record);
|
|
375
|
+
const found = sources.transcriptText(record.transcriptPath, Date.parse(record.startedAt), next === null ? Number.POSITIVE_INFINITY : Date.parse(next));
|
|
376
|
+
if (found === null) {
|
|
377
|
+
// The record is past REPLAY_AFTER_MS, so a window that is fully scanned or out of reach stays empty.
|
|
378
|
+
closeWithoutSummary(db, tenantId, record.id, log);
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
381
|
+
const { text, listed } = found;
|
|
382
|
+
if (!listed)
|
|
383
|
+
log(`no memories section in the transcript summary for ${record.id}`);
|
|
384
|
+
if (!markSummarised(db, tenantId, record.id, { text, summarisedAt: new Date().toISOString() })) {
|
|
385
|
+
log(`${record.id} was filled by another process`);
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
saveItems(db, hippoRoot, itemContext(tenantId, record, text.items), log);
|
|
389
|
+
return true;
|
|
390
|
+
}
|
|
391
|
+
function replayOpen(run) {
|
|
392
|
+
const open = openTranscriptRows(run.db, run.tenantId, new Date(run.now - REPLAY_AFTER_MS).toISOString(), new Date(run.now - TRANSCRIPT_FILL_WINDOW_MS).toISOString()).map(toRecord);
|
|
393
|
+
return replayEach(run, open, 'transcript fill', (record) => fillFromTranscript(run, record));
|
|
394
|
+
}
|
|
395
|
+
/** Finishes what a killed hook or a busy store left: `summarised` records, spool files, and `started` records the transcript can fill.
|
|
396
|
+
* Returns how many compactions it saved. */
|
|
397
|
+
function replayCompactions(db, hippoRoot, sources, log, deadline) {
|
|
398
|
+
const run = { db, hippoRoot, tenantId: resolveTenantId({}), now: Date.now(), sources, log, deadline };
|
|
399
|
+
return replayStalled(run) + replaySpool(run) + replayOpen(run);
|
|
400
|
+
}
|
|
401
|
+
/** For `hippo sleep` and post-compact: opens the store itself and never throws. */
|
|
402
|
+
export function replayStoredCompactions(hippoRoot, sources, log, opts = {}) {
|
|
403
|
+
let db;
|
|
404
|
+
try {
|
|
405
|
+
db = openHippoDb(hippoRoot, { busyWaitMs: opts.busyWaitMs });
|
|
406
|
+
return replayCompactions(db, hippoRoot, sources, log, opts.deadline ?? Number.POSITIVE_INFINITY);
|
|
407
|
+
}
|
|
408
|
+
catch (err) {
|
|
409
|
+
log(`replay failed: ${errorMessage(err)}`);
|
|
410
|
+
return 0;
|
|
411
|
+
}
|
|
412
|
+
finally {
|
|
413
|
+
if (db)
|
|
414
|
+
closeHippoDb(db);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
//# sourceMappingURL=compactions-record.js.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { originInSql } from '../core/project-identity.js';
|
|
2
|
-
import { scopeAdmitSql } from './
|
|
3
|
-
const COLUMNS = 'tenant_id, id, session_id, origin_project, compact_trigger, cwd, transcript_path, snapshot_saved, started_at, summarised_at, summary,
|
|
2
|
+
import { scopeAdmitSql } from './rule-sql.js';
|
|
3
|
+
const COLUMNS = 'tenant_id, id, session_id, origin_project, compact_trigger, cwd, transcript_path, snapshot_saved, started_at, summarised_at, summary, ' +
|
|
4
|
+
'items_json, items_written, status';
|
|
4
5
|
function selectRows(db, where, ...params) {
|
|
5
6
|
// SAFETY: the SELECT names exactly COLUMNS, matching CompactionRow's field set.
|
|
6
7
|
return db.prepare(`SELECT ${COLUMNS} FROM compactions WHERE ${where}`).all(...params);
|
|
@@ -42,8 +43,9 @@ export function insertSummarisedCompaction(db, tenantId, meta, row) {
|
|
|
42
43
|
// Only a caller names the column, so a store from before it was added still takes local writes.
|
|
43
44
|
const stamp = requestId === undefined ? [] : [requestId];
|
|
44
45
|
const [col, mark] = stamp.length === 0 ? ['', ''] : [', request_id', ', ?'];
|
|
45
|
-
db.prepare(`INSERT INTO compactions(tenant_id, id, session_id, origin_project, compact_trigger, cwd, transcript_path, started_at,
|
|
46
|
-
|
|
46
|
+
db.prepare(`INSERT INTO compactions(tenant_id, id, session_id, origin_project, compact_trigger, cwd, transcript_path, started_at, ` +
|
|
47
|
+
`summarised_at, summary, items_json, status${col})\n` +
|
|
48
|
+
` VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'summarised'${mark})`).run(tenantId, id, meta.sessionId, meta.originProject, meta.trigger, meta.cwd, meta.transcriptPath, meta.startedAt, summarisedAt, summary, itemsJson, ...stamp);
|
|
47
49
|
}
|
|
48
50
|
export function markSnapshotSavedRow(db, tenantId, recordId) {
|
|
49
51
|
db.prepare(`UPDATE compactions SET snapshot_saved = 1 WHERE tenant_id = ? AND id = ?`).run(tenantId, recordId);
|
package/dist/store/conflicts.js
CHANGED
|
@@ -5,11 +5,12 @@ import { archiveRawMemory } from './raw-archive.js';
|
|
|
5
5
|
import { 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';
|
|
14
15
|
// The one place MemoryConflictRow's columns are listed; CONFLICT_COLS_MC is the same list under the `mc` alias.
|
|
15
16
|
const CONFLICT_COLS = 'id, memory_a_id, memory_b_id, reason, score, status, detected_at, updated_at';
|
|
@@ -351,7 +352,8 @@ function recordRejectedLoserValue(db, t, loserRow, digest, who) {
|
|
|
351
352
|
/** Tombstones the loser's digest and removes its same-tenant duplicates into `removal`; returns the digest. */
|
|
352
353
|
function tombstoneLoserValue(db, t, loserRow, who, removal) {
|
|
353
354
|
if (isPersonalScope(loserRow.scope)) {
|
|
354
|
-
throw new BadRequestError(`cannot reject the value of personal memory ${t.loserId}: a rejection reaches the whole tenant,
|
|
355
|
+
throw new BadRequestError(`cannot reject the value of personal memory ${t.loserId}: a rejection reaches the whole tenant, ` +
|
|
356
|
+
`so its reason would show to everyone; resolve with forget instead`);
|
|
355
357
|
}
|
|
356
358
|
const { actor, reason } = who;
|
|
357
359
|
const rejectedDigest = rejectionDigest(loserRow.content);
|
|
@@ -438,7 +440,8 @@ function purgeRemovedLoserMirrors(hippoRoot, db, loserId, removal) {
|
|
|
438
440
|
/** The conflict and loser ids every conflict_resolve audit row names. */
|
|
439
441
|
export function conflictResolveAuditsAt(db) {
|
|
440
442
|
// 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
|
|
443
|
+
return db.prepare(`SELECT json_extract(metadata_json, '$.conflictId') AS conflictId, json_extract(metadata_json, '$.loserId') AS loserId ` +
|
|
444
|
+
`FROM audit_log WHERE op = 'conflict_resolve'`).all();
|
|
442
445
|
}
|
|
443
446
|
/** Every resolved conflict with both sides. */
|
|
444
447
|
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,32 @@
|
|
|
1
|
+
// The dead-letter statements both connectors share; each connector names its table and columns and maps rows to its own item type.
|
|
2
|
+
/** Inserts one row, stamping received_at; `columns` pairs each column name with its value, in insert order. */
|
|
3
|
+
export function insertDlqRow(db, table, columns) {
|
|
4
|
+
const names = [...columns.map(([name]) => name), 'received_at'];
|
|
5
|
+
const values = [...columns.map(([, value]) => value), new Date().toISOString()];
|
|
6
|
+
const result = db
|
|
7
|
+
.prepare(`INSERT INTO ${table} (${names.join(', ')}) VALUES (${names.map(() => '?').join(', ')})`)
|
|
8
|
+
.run(...values);
|
|
9
|
+
return Number(result.lastInsertRowid);
|
|
10
|
+
}
|
|
11
|
+
export function listDlqRows(db, table, selectColumns, tenantId, limit) {
|
|
12
|
+
// SAFETY: the caller's selectColumns names exactly the fields its rowToItem reads.
|
|
13
|
+
return db
|
|
14
|
+
.prepare(`SELECT ${selectColumns}
|
|
15
|
+
FROM ${table}
|
|
16
|
+
WHERE tenant_id = ?
|
|
17
|
+
ORDER BY received_at ASC
|
|
18
|
+
LIMIT ?`)
|
|
19
|
+
.all(tenantId, limit);
|
|
20
|
+
}
|
|
21
|
+
export function getDlqRow(db, table, selectColumns, id) {
|
|
22
|
+
// SAFETY: the caller's selectColumns names exactly the fields its rowToItem reads.
|
|
23
|
+
const row = db
|
|
24
|
+
.prepare(`SELECT ${selectColumns} FROM ${table} WHERE id = ?`)
|
|
25
|
+
.get(id);
|
|
26
|
+
return row ?? null;
|
|
27
|
+
}
|
|
28
|
+
export function markDlqRetried(db, table, id) {
|
|
29
|
+
db.prepare(`UPDATE ${table} SET retried_at = ?, retry_count = retry_count + 1 WHERE id = ?`)
|
|
30
|
+
.run(new Date().toISOString(), id);
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=dlq.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;
|