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/sharing/share.js
CHANGED
|
@@ -4,15 +4,14 @@ import * as fs from 'fs';
|
|
|
4
4
|
import * as path from 'path';
|
|
5
5
|
import { generateId, COMPACTION_MEMORY_TAG } from '../core/memory.js';
|
|
6
6
|
import { AGENT_MEMORY_SOURCE_PREFIX, AGENT_MEMORY_TAGS } from '../core/agent-memory-tools.js';
|
|
7
|
-
import { writeEntry } from '../store/entry-writes.js';
|
|
8
|
-
import { loadAllEntries, readEntry } from '../store/entry-reads.js';
|
|
7
|
+
import { writeEntriesSkippingRejected, writeEntry } from '../store/entry-writes.js';
|
|
8
|
+
import { loadAllEntries, loadEntryTextKeys, readEntry } from '../store/entry-reads.js';
|
|
9
9
|
import { tallySources } from '../store/candidates.js';
|
|
10
|
-
import { isPersonalScope } from '../
|
|
10
|
+
import { isPersonalScope } from '../core/recall-scope.js';
|
|
11
11
|
import { fallbackOrigin } from '../core/project-identity.js';
|
|
12
12
|
import { detectSecret } from '../util/secret-detect.js';
|
|
13
13
|
import { isQuarantineScope } from '../trust/quarantine.js';
|
|
14
|
-
import {
|
|
15
|
-
import { embedMemory, embedAll } from '../store/embeddings/index.js';
|
|
14
|
+
import { embedMemory, embedAll } from '../search/embed-memories.js';
|
|
16
15
|
import { duplicateKey, storedTextKeys } from '../util/same-text.js';
|
|
17
16
|
import { isReusable } from '../core/memory-quality.js';
|
|
18
17
|
import { log } from '../util/log.js';
|
|
@@ -45,6 +44,8 @@ export const NO_MERGE_TAGS = new Set([
|
|
|
45
44
|
COMPACTION_MEMORY_TAG,
|
|
46
45
|
...AGENT_MEMORY_TAGS,
|
|
47
46
|
]);
|
|
47
|
+
/** The transfer score a memory needs before sleep or `hippo share --auto` copies it to the global store. */
|
|
48
|
+
export const AUTO_SHARE_MIN_SCORE = 0.6;
|
|
48
49
|
/** Estimate how well a memory would transfer to other projects.
|
|
49
50
|
* Returns 0..1 where >0.5 = good candidate for sharing. */
|
|
50
51
|
export function transferScore(entry) {
|
|
@@ -74,28 +75,34 @@ export function shareMemory(localRoot, id, options = {}) {
|
|
|
74
75
|
const entry = readEntry(localRoot, id, options.tenantId);
|
|
75
76
|
if (!entry)
|
|
76
77
|
throw new NotFoundError(`Memory not found: ${id}`);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (score < 0.3 && !options.force)
|
|
78
|
+
const globalEntry = toGlobalEntry(localRoot, entry, options.force === true);
|
|
79
|
+
if (!globalEntry)
|
|
80
80
|
return null;
|
|
81
81
|
initGlobal();
|
|
82
82
|
const globalRoot = getGlobalRoot();
|
|
83
|
+
writeEntry(globalRoot, globalEntry);
|
|
84
|
+
// Embed here unless the caller opts out: autoShare sets skipEmbed to batch one embedAll(), since embedMemory rewrites the whole index per call.
|
|
85
|
+
if (!options.skipEmbed) {
|
|
86
|
+
void embedMemory(globalRoot, globalEntry);
|
|
87
|
+
}
|
|
88
|
+
return globalEntry;
|
|
89
|
+
}
|
|
90
|
+
/** The global copy of a local row, or null when its transfer score is too low and `force` is off; throws when the row may not be shared. */
|
|
91
|
+
function toGlobalEntry(localRoot, entry, force) {
|
|
92
|
+
assertShareable(entry, entry.id);
|
|
93
|
+
const score = transferScore(entry);
|
|
94
|
+
if (score < 0.3 && !force)
|
|
95
|
+
return null;
|
|
83
96
|
// The label keeps the folder name for a user-global row; a NULL row gets `shared::`, which originFromSource reads as no project.
|
|
84
97
|
const fallbackName = path.basename(path.resolve(localRoot, '..'));
|
|
85
98
|
const originName = entry.origin_project ?? fallbackOrigin(localRoot);
|
|
86
99
|
const label = originName === '' ? fallbackName : (originName ?? '');
|
|
87
|
-
|
|
100
|
+
return {
|
|
88
101
|
...entry,
|
|
89
102
|
id: generateId('g'),
|
|
90
103
|
source: `shared:${label}:${new Date().toISOString()}`,
|
|
91
104
|
origin_project: originName,
|
|
92
105
|
};
|
|
93
|
-
writeEntry(globalRoot, globalEntry);
|
|
94
|
-
// Embed here unless the caller opts out: autoShare sets skipEmbed to batch one embedAll(), since embedMemory rewrites the whole index per call.
|
|
95
|
-
if (!options.skipEmbed) {
|
|
96
|
-
void embedMemory(globalRoot, globalEntry);
|
|
97
|
-
}
|
|
98
|
-
return globalEntry;
|
|
99
106
|
}
|
|
100
107
|
function assertShareable(entry, id) {
|
|
101
108
|
// Secret veto: secrets never reach the global store, even with --force; throw loudly, since a silent null reads as "low transfer score" and invites retries.
|
|
@@ -174,29 +181,25 @@ function isAutoShareCandidate(entry, globalContentSet, minScore, stats) {
|
|
|
174
181
|
}
|
|
175
182
|
return true;
|
|
176
183
|
}
|
|
177
|
-
//
|
|
178
|
-
//
|
|
179
|
-
function shareCandidates(localRoot, candidates, stats) {
|
|
184
|
+
// A row the GLOBAL store's rejection tombstones refuse is audited and skipped, so it cannot abort the autoShare sleep phase.
|
|
185
|
+
// The candidates are rows already held, written in one transaction; the caller embeds once via embedAll() afterwards.
|
|
186
|
+
function shareCandidates(localRoot, globalRoot, candidates, stats) {
|
|
180
187
|
const shared = [];
|
|
181
188
|
let rejectedSkipped = 0;
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
if (
|
|
188
|
-
shared.push(
|
|
189
|
-
}
|
|
190
|
-
catch (err) {
|
|
191
|
-
if (err instanceof RejectedValueError) {
|
|
192
|
-
rejectedSkipped++;
|
|
193
|
-
if (stats)
|
|
194
|
-
stats.rejectedSkipped = (stats.rejectedSkipped ?? 0) + 1;
|
|
189
|
+
writeEntriesSkippingRejected(globalRoot, (put) => {
|
|
190
|
+
for (const entry of candidates) {
|
|
191
|
+
const globalEntry = toGlobalEntry(localRoot, entry, true);
|
|
192
|
+
if (!globalEntry)
|
|
193
|
+
continue;
|
|
194
|
+
if (put(globalEntry)) {
|
|
195
|
+
shared.push(globalEntry);
|
|
195
196
|
continue;
|
|
196
197
|
}
|
|
197
|
-
|
|
198
|
+
rejectedSkipped++;
|
|
199
|
+
if (stats)
|
|
200
|
+
stats.rejectedSkipped = (stats.rejectedSkipped ?? 0) + 1;
|
|
198
201
|
}
|
|
199
|
-
}
|
|
202
|
+
});
|
|
200
203
|
if (rejectedSkipped > 0) {
|
|
201
204
|
log.warn(`autoShare: skipped ${rejectedSkipped} candidate(s) refused by the global store's rejection tombstone`);
|
|
202
205
|
}
|
|
@@ -205,19 +208,17 @@ function shareCandidates(localRoot, candidates, stats) {
|
|
|
205
208
|
/** Auto-share local memories with high transfer scores, not already global and without a NEVER_AUTO_SHARE_TAGS tag; `options.tenantId` scopes the LOCAL read.
|
|
206
209
|
* `options.stats` is an opt-in out-param: `secretSkipped` counts shares the secret veto prevented, `rejectedSkipped` rejection refusals (0 under `dryRun`). */
|
|
207
210
|
export function autoShare(localRoot, options = {}) {
|
|
208
|
-
const { minScore =
|
|
211
|
+
const { minScore = AUTO_SHARE_MIN_SCORE, dryRun = false } = options;
|
|
209
212
|
const localEntries = loadAllEntries(localRoot, options.tenantId);
|
|
210
213
|
initGlobal();
|
|
211
214
|
const globalRoot = getGlobalRoot();
|
|
212
215
|
// Host-wide read. The global store IS the union across all tenants;
|
|
213
216
|
// per-tenant filtering on the global root would defeat the purpose.
|
|
214
|
-
const
|
|
215
|
-
// Build set of global content hashes to avoid duplicates
|
|
216
|
-
const globalContentSet = storedTextKeys(globalEntries);
|
|
217
|
+
const globalContentSet = storedTextKeys(loadEntryTextKeys(globalRoot));
|
|
217
218
|
const candidates = localEntries.filter((entry) => isAutoShareCandidate(entry, globalContentSet, minScore, options.stats));
|
|
218
219
|
if (dryRun)
|
|
219
220
|
return candidates;
|
|
220
|
-
const shared = shareCandidates(localRoot, candidates, options.stats);
|
|
221
|
+
const shared = shareCandidates(localRoot, globalRoot, candidates, options.stats);
|
|
221
222
|
if (shared.length > 0) {
|
|
222
223
|
void embedAll(globalRoot).catch((err) => logEmbedAllFailure('autoShare', err));
|
|
223
224
|
}
|
package/dist/store/ambient.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { strengthSql } from './rule-sql.js';
|
|
2
|
-
import { scopeAdmitSql } from './
|
|
2
|
+
import { scopeAdmitSql } from './rule-sql.js';
|
|
3
3
|
import { SECRET_TAGS } from '../util/secret-detect.js';
|
|
4
4
|
import { onHandle, openStore } from './open.js';
|
|
5
5
|
import { jsonList } from './candidates.js';
|
|
6
6
|
import { originInSql } from '../core/project-identity.js';
|
|
7
|
-
import { isErrorTagged } from '../core/ambient.js';
|
|
8
|
-
import {
|
|
7
|
+
import { AMBIENT_FRESH_WINDOW_MS, HIGH_SCHEMA_FIT_ABOVE, isErrorTagged } from '../core/ambient.js';
|
|
8
|
+
import { placeholders } from '../util/sql.js';
|
|
9
9
|
// SHORTCUT: mirrors loadContextCandidates' WHERE (request-path snapshots pin the match); share it once store.ts is split.
|
|
10
10
|
function contextRowsWhere(tenantId, filter) {
|
|
11
11
|
const where = ['tenant_id = ?', `COALESCE(superseded_by, '') = ''`, `COALESCE(kind, '') != 'archived'`];
|
|
@@ -30,7 +30,7 @@ function secretTaggedSql() {
|
|
|
30
30
|
const tags = [...SECRET_TAGS];
|
|
31
31
|
return {
|
|
32
32
|
sql: `((${tags.map(() => 'tags_json LIKE ?').join(' OR ')})
|
|
33
|
-
AND EXISTS (SELECT 1 FROM json_each(${jsonList('tags_json')}) WHERE lower(value) IN (${tags.
|
|
33
|
+
AND EXISTS (SELECT 1 FROM json_each(${jsonList('tags_json')}) WHERE lower(value) IN (${placeholders(tags.length)})))`,
|
|
34
34
|
params: [...tags.map((t) => `%"${t}"%`), ...tags],
|
|
35
35
|
};
|
|
36
36
|
}
|
|
@@ -39,7 +39,7 @@ function secretTaggedSql() {
|
|
|
39
39
|
export function loadAmbientTallies(hippoRoot, tenantId, filter) {
|
|
40
40
|
const { where, params } = contextRowsWhere(tenantId, filter);
|
|
41
41
|
const secret = secretTaggedSql();
|
|
42
|
-
const
|
|
42
|
+
const freshAfter = new Date(filter.now.getTime() - AMBIENT_FRESH_WINDOW_MS).toISOString();
|
|
43
43
|
return onHandle(hippoRoot, (db) => {
|
|
44
44
|
// SAFETY: one aggregate row whose columns are the aliases named below. Tag lists come back as one JSON array of
|
|
45
45
|
// arrays and are counted in JS, which maps items through String() as parseJsonArray does.
|
|
@@ -48,7 +48,7 @@ export function loadAmbientTallies(hippoRoot, tenantId, filter) {
|
|
|
48
48
|
COALESCE(SUM(${strengthSql(filter.now)}), 0) AS strengthSum,
|
|
49
49
|
COALESCE(SUM(julianday(created) > julianday(?)), 0) AS fresh,
|
|
50
50
|
COALESCE(SUM(emotional_valence IN ('negative', 'critical')), 0) AS negative,
|
|
51
|
-
COALESCE(SUM(COALESCE(schema_fit, 0.5) >
|
|
51
|
+
COALESCE(SUM(COALESCE(schema_fit, 0.5) > ?), 0) AS highSchemaFit,
|
|
52
52
|
COALESCE(SUM(layer = 'semantic'), 0) AS semantic,
|
|
53
53
|
COALESCE(SUM(layer = 'episodic'), 0) AS episodic,
|
|
54
54
|
COALESCE(SUM(json_array_length(${jsonList('conflicts_with_json')})), 0) AS conflicts,
|
|
@@ -57,7 +57,7 @@ export function loadAmbientTallies(hippoRoot, tenantId, filter) {
|
|
|
57
57
|
'[' || COALESCE(group_concat(${jsonList('tags_json')}, ','), '') || ']' AS tagLists
|
|
58
58
|
FROM memories
|
|
59
59
|
WHERE ${where.join(' AND ')}
|
|
60
|
-
AND ((${originInSql(filter.currentProject)} AND origin_project != '') OR NOT ${secret.sql})`).get(
|
|
60
|
+
AND ((${originInSql(filter.currentProject)} AND origin_project != '') OR NOT ${secret.sql})`).get(freshAfter, HIGH_SCHEMA_FIT_ABOVE, ...params, ...filter.currentProject, ...secret.params);
|
|
61
61
|
const tagCounts = new Map();
|
|
62
62
|
let errors = 0;
|
|
63
63
|
const lists = /* SAFETY: jsonList yields an array per row, joined into one array */ JSON.parse(row.tagLists);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { openHippoDb } from '../db/index.js';
|
|
2
2
|
import { type AuditOp } from './audit.js';
|
|
3
|
-
import { RejectedValueError } from '
|
|
3
|
+
import { RejectedValueError } from '../core/api-errors.js';
|
|
4
4
|
import type { JsonValue } from '../util/json.js';
|
|
5
5
|
export interface AuditOptions {
|
|
6
6
|
readonly targetId?: string;
|
package/dist/store/audit.d.ts
CHANGED
|
@@ -1,22 +1,6 @@
|
|
|
1
|
-
import { type MemoryEntry } from '../core/memory.js';
|
|
2
1
|
import { type DatabaseSyncLike } from '../db/index.js';
|
|
3
2
|
import type { JsonObject } from './working-memory.js';
|
|
4
3
|
import { type KeysetPosition } from '../util/keyset.js';
|
|
5
|
-
export type AuditSeverity = 'warning' | 'error';
|
|
6
|
-
export interface AuditIssue {
|
|
7
|
-
memoryId: string;
|
|
8
|
-
content: string;
|
|
9
|
-
severity: AuditSeverity;
|
|
10
|
-
reason: string;
|
|
11
|
-
}
|
|
12
|
-
export interface AuditResult {
|
|
13
|
-
total: number;
|
|
14
|
-
issues: AuditIssue[];
|
|
15
|
-
clean: number;
|
|
16
|
-
}
|
|
17
|
-
export declare function auditMemory(entry: MemoryEntry, backsObject?: boolean): AuditIssue | null;
|
|
18
|
-
/** `backing`: ids of memories that back an object (store.memoriesBackingObjects). */
|
|
19
|
-
export declare function auditMemories(entries: MemoryEntry[], backing: ReadonlySet<string>): AuditResult;
|
|
20
4
|
export declare const AUDIT_OPS: readonly ["remember", "recall", "promote", "supersede", "forget", "archive_raw", "auth_revoke", "auth_create", "outcome", "consolidate", "audit_prune", "summary_marked_dirty", "summary_marked_clean", "summary_rebuilt", "predict_create", "predict_close", "predict_baserate", "recall_autodebias_hint", "recall_autodebias_hint_no_class_match", "recall_autodebias_hint_tiebreak", "recall_anchor_detected_query_repeat", "recall_anchor_detected_memory_dominance", "recall_anchor_skipped_no_session", "recall_availability_detected", "decision_create", "decision_supersede", "decision_close", "incident_open", "incident_resolve", "incident_close", "process_create", "process_supersede", "process_close", "policy_create", "policy_supersede", "policy_close", "skill_create", "skill_supersede", "skill_close", "project_brief_create", "project_brief_supersede", "project_brief_close", "customer_note_create", "customer_note_supersede", "customer_note_close", "mv_rescue", "reject_value", "reject_refusal", "unreject_value", "conflict_resolve", "half_life_migrate", "dormant_restore", "auth_grant", "auth_ungrant", "quarantine", "quarantine_approve", "quarantine_reject", "agent_memory_restore", "agent_memory_set_aside", "project_merge", "project_repair", "quality_repair"];
|
|
21
5
|
export type AuditOp = (typeof AUDIT_OPS)[number];
|
|
22
6
|
export interface AppendAuditOpts {
|
package/dist/store/audit.js
CHANGED
|
@@ -1,60 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { canAutoDelete } from '../core/memory.js';
|
|
1
|
+
import { DEFAULT_LIST_LIMIT } from '../util/limits.js';
|
|
3
2
|
import { closeHippoDb, openHippoDb, withWriteScope } from '../db/index.js';
|
|
4
3
|
import { log } from '../util/log.js';
|
|
4
|
+
import { blockHash } from '../util/token-text.js';
|
|
5
5
|
import { keysetAfter } from '../util/keyset.js';
|
|
6
6
|
import { warnDamagedColumn } from '../util/stored-json.js';
|
|
7
|
-
import { automaticDefect, hasNoSpecificity, isFragment, isReleaseCommitNoise, substantiveWordCount, } from '../core/memory-quality.js';
|
|
8
|
-
export function auditMemory(entry, backsObject = false) {
|
|
9
|
-
const issue = classifyMemory(entry);
|
|
10
|
-
// Error means "auto-remove"; a pinned, raw, kept-for-good or object-backing row is never auto-removed, so it can only warn.
|
|
11
|
-
if (issue?.severity === 'error' && (!canAutoDelete(entry) || backsObject)) {
|
|
12
|
-
const why = entry.pinned ? 'pinned' : entry.kind === 'raw' ? 'raw' : backsObject ? 'backs an object' : 'keep rule';
|
|
13
|
-
return { ...issue, severity: 'warning', reason: `${issue.reason} (${why}, kept)` };
|
|
14
|
-
}
|
|
15
|
-
return issue;
|
|
16
|
-
}
|
|
17
|
-
function classifyMemory(entry) {
|
|
18
|
-
const content = entry.content.trim();
|
|
19
|
-
if (content.length < 3) {
|
|
20
|
-
return { memoryId: entry.id, content, severity: 'error', reason: 'too short (< 3 chars)' };
|
|
21
|
-
}
|
|
22
|
-
if (content.length < 10) {
|
|
23
|
-
return { memoryId: entry.id, content, severity: 'error', reason: 'too short (< 10 chars)' };
|
|
24
|
-
}
|
|
25
|
-
if (isReleaseCommitNoise(content)) {
|
|
26
|
-
return { memoryId: entry.id, content, severity: 'error', reason: 'release/commit noise, not a useful memory' };
|
|
27
|
-
}
|
|
28
|
-
if (isFragment(content)) {
|
|
29
|
-
return { memoryId: entry.id, content, severity: 'warning', reason: 'sentence fragment — lacks context' };
|
|
30
|
-
}
|
|
31
|
-
const substantive = substantiveWordCount(content);
|
|
32
|
-
if (substantive < 2) {
|
|
33
|
-
return { memoryId: entry.id, content, severity: 'warning', reason: `only ${substantive} substantive word(s) — too vague` };
|
|
34
|
-
}
|
|
35
|
-
if (content.length < 40 && hasNoSpecificity(content)) {
|
|
36
|
-
return { memoryId: entry.id, content, severity: 'warning', reason: 'no specific details (names, paths, numbers, code)' };
|
|
37
|
-
}
|
|
38
|
-
const reason = automaticDefect(entry);
|
|
39
|
-
if (reason !== null) {
|
|
40
|
-
return { memoryId: entry.id, content, severity: 'warning', reason: `automatic memory defect: ${reason}` };
|
|
41
|
-
}
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
/** `backing`: ids of memories that back an object (store.memoriesBackingObjects). */
|
|
45
|
-
export function auditMemories(entries, backing) {
|
|
46
|
-
const issues = [];
|
|
47
|
-
for (const entry of entries) {
|
|
48
|
-
const issue = auditMemory(entry, backing.has(entry.id));
|
|
49
|
-
if (issue)
|
|
50
|
-
issues.push(issue);
|
|
51
|
-
}
|
|
52
|
-
return {
|
|
53
|
-
total: entries.length,
|
|
54
|
-
issues,
|
|
55
|
-
clean: entries.length - issues.length,
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
7
|
// The one list of audit ops: the AuditOp type, `hippo audit list --op` and GET /v1/audit?op= all read it.
|
|
59
8
|
export const AUDIT_OPS = [
|
|
60
9
|
'remember',
|
|
@@ -128,7 +77,7 @@ function bigintSafeReplacer(_key, value) {
|
|
|
128
77
|
}
|
|
129
78
|
export function auditQueryFields(query) {
|
|
130
79
|
return {
|
|
131
|
-
query_hash:
|
|
80
|
+
query_hash: blockHash(query),
|
|
132
81
|
query_length: query.length,
|
|
133
82
|
};
|
|
134
83
|
}
|
|
@@ -172,7 +121,7 @@ export function queryAuditEvents(db, opts) {
|
|
|
172
121
|
}
|
|
173
122
|
const after = keysetAfter('ts', 'id', opts.after);
|
|
174
123
|
// One past the route's 10000 cap: GET /v1/audit reads a row ahead to tell whether another page exists.
|
|
175
|
-
const limit = Math.max(1, Math.min(opts.limit ??
|
|
124
|
+
const limit = Math.max(1, Math.min(opts.limit ?? DEFAULT_LIST_LIMIT, 10001));
|
|
176
125
|
// SAFETY: AUDIT_COLUMNS names exactly the AuditRow columns, in this order.
|
|
177
126
|
const rows = db
|
|
178
127
|
.prepare(`SELECT ${AUDIT_COLUMNS} FROM audit_log WHERE ${where.join(' AND ')}${after.sql} ORDER BY ts DESC, id DESC LIMIT ?`)
|
package/dist/store/auth.d.ts
CHANGED
|
@@ -12,13 +12,15 @@ export declare function apiKeyVerifyStats(): ApiKeyVerifyStats;
|
|
|
12
12
|
export interface CreateApiKeyOpts {
|
|
13
13
|
tenantId: string;
|
|
14
14
|
label?: string;
|
|
15
|
-
/** 'admin' | 'member'. Defaults to '
|
|
15
|
+
/** 'admin' | 'member'. Defaults to 'member', like the API layer. */
|
|
16
16
|
role?: 'admin' | 'member';
|
|
17
17
|
/** The auth-resolver subject a self-service key belongs to; unset for keys an admin or the CLI mints. */
|
|
18
18
|
ownerSubject?: string;
|
|
19
|
-
/** ISO time the key stops working; unset means
|
|
19
|
+
/** ISO time the key stops working; unset means DEFAULT_KEY_TTL_DAYS from now. */
|
|
20
20
|
expiresAt?: string;
|
|
21
21
|
}
|
|
22
|
+
/** Days a key lives when its mint names no expiry, so a key nobody remembers stops working by itself. */
|
|
23
|
+
export declare const DEFAULT_KEY_TTL_DAYS = 90;
|
|
22
24
|
export interface CreateApiKeyResult {
|
|
23
25
|
keyId: string;
|
|
24
26
|
plaintext: string;
|
package/dist/store/auth.js
CHANGED
|
@@ -3,6 +3,7 @@ import { promisify } from 'node:util';
|
|
|
3
3
|
import { closeHippoDb, openHippoDb } from '../db/index.js';
|
|
4
4
|
import { raiseMinBinary } from '../db/meta.js';
|
|
5
5
|
import { keysetAfter } from '../util/keyset.js';
|
|
6
|
+
import { DAY_MS } from '../util/time.js';
|
|
6
7
|
import { EXPIRING_KEYS_MIN_BINARY } from '../util/version.js';
|
|
7
8
|
/** Every minted API key starts with this, so the server can route a bearer token by shape. */
|
|
8
9
|
export const API_KEY_PREFIX = 'hk_';
|
|
@@ -48,6 +49,8 @@ function verifyKey(plaintext, stored) {
|
|
|
48
49
|
return parsed.matches(scryptSync(plaintext, parsed.salt, parsed.keylen));
|
|
49
50
|
}
|
|
50
51
|
const scryptOffLoop = promisify(scrypt);
|
|
52
|
+
/** Days a key lives when its mint names no expiry, so a key nobody remembers stops working by itself. */
|
|
53
|
+
export const DEFAULT_KEY_TTL_DAYS = 90;
|
|
51
54
|
export function mintApiKey() {
|
|
52
55
|
const keyId = `${API_KEY_PREFIX}${randBase32(ID_LEN)}`;
|
|
53
56
|
const plaintext = `${keyId}.${randBase32(SECRET_LEN)}`;
|
|
@@ -63,8 +66,8 @@ export function insertApiKey(db, key) {
|
|
|
63
66
|
export function createApiKey(db, opts) {
|
|
64
67
|
const { keyId, plaintext, keyHash } = mintApiKey();
|
|
65
68
|
insertApiKey(db, {
|
|
66
|
-
keyId, keyHash, tenantId: opts.tenantId, label: opts.label ?? null, role: opts.role ?? '
|
|
67
|
-
ownerSubject: opts.ownerSubject ?? null, expiresAt: opts.expiresAt ??
|
|
69
|
+
keyId, keyHash, tenantId: opts.tenantId, label: opts.label ?? null, role: opts.role ?? 'member', createdAt: new Date().toISOString(),
|
|
70
|
+
ownerSubject: opts.ownerSubject ?? null, expiresAt: opts.expiresAt ?? new Date(Date.now() + DEFAULT_KEY_TTL_DAYS * DAY_MS).toISOString(),
|
|
68
71
|
});
|
|
69
72
|
return { keyId, plaintext };
|
|
70
73
|
}
|
|
@@ -251,7 +254,8 @@ export function listApiKeyRows(db, opts) {
|
|
|
251
254
|
const limitSql = opts.limit === undefined ? '' : ' LIMIT ?';
|
|
252
255
|
if (opts.limit !== undefined)
|
|
253
256
|
params.push(opts.limit);
|
|
254
|
-
const sql = `SELECT id, key_id, tenant_id, label, created_at, revoked_at, role, expires_at, owner_subject FROM api_keys
|
|
257
|
+
const sql = `SELECT id, key_id, tenant_id, label, created_at, revoked_at, role, expires_at, owner_subject FROM api_keys ` +
|
|
258
|
+
`WHERE ${where.join(' AND ')}${after.sql} ORDER BY id DESC${limitSql}`;
|
|
255
259
|
// SAFETY: the SELECT above projects exactly these 9 columns, in this order.
|
|
256
260
|
const rows = db.prepare(sql).all(...params);
|
|
257
261
|
return rows.map(r => ({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type MemoryEntry, type ConfidenceLevel, Layer } from '../core/memory.js';
|
|
2
|
-
import { type SqlFragment } from './
|
|
2
|
+
import { type SqlFragment } from './rule-sql.js';
|
|
3
3
|
export interface AmbientRecallRequest {
|
|
4
4
|
terms: string[];
|
|
5
5
|
limit: number;
|
|
@@ -37,7 +37,8 @@ export declare const EVERY_SCOPE: SqlFragment;
|
|
|
37
37
|
export declare function loadTextsHoldingWords(hippoRoot: string, tenantId: string | undefined, words: readonly string[], project?: readonly string[], admit?: SqlFragment): HeldText[];
|
|
38
38
|
/** A tenant's `limit` newest rows, oldest first: the tail of loadAllEntries' order, without reading the rows before it. */
|
|
39
39
|
export declare function loadNewestEntries(hippoRoot: string, tenantId: string, limit: number): MemoryEntry[];
|
|
40
|
-
/** computeSchemaFit against
|
|
40
|
+
/** computeSchemaFit against a tenant's newest SCHEMA_FIT_WINDOW_ROWS rows: tag counts come from one aggregate
|
|
41
|
+
* and texts stream one column, so no row is loaded. */
|
|
41
42
|
export declare function schemaFitInStore(hippoRoot: string, tenantId: string, content: string, tags: readonly string[]): number;
|
|
42
43
|
export interface SourceTally {
|
|
43
44
|
source: string;
|
package/dist/store/candidates.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { FALLBACK_HALF_LIFE_DAYS, Layer, calculateStrength, facetsOf, schemaFitFrom } from '../core/memory.js';
|
|
2
2
|
import { strengthSql } from './rule-sql.js';
|
|
3
3
|
import { withReadSnapshot } from '../db/index.js';
|
|
4
|
-
import { scopeAdmitSql } from './
|
|
4
|
+
import { rewardFactorSql, scopeAdmitSql } from './rule-sql.js';
|
|
5
5
|
import { MEMORY_SELECT_COLUMNS, rowToEntry, parseJsonArray } from './rows.js';
|
|
6
6
|
import { onHandle, openStore } from './open.js';
|
|
7
7
|
import { originInSql } from '../core/project-identity.js';
|
|
@@ -15,32 +15,44 @@ function inOrigins(e, origins) {
|
|
|
15
15
|
const origin = e.origin_project ?? null;
|
|
16
16
|
return origin === '' ? origins.userGlobal : origin !== null && origins.names.includes(origin);
|
|
17
17
|
}
|
|
18
|
+
// `id DESC` mirrors getContext's comparator, not loadFreshRawMemories'
|
|
19
|
+
// cross-ingest-stable order: that would change what the hook injects.
|
|
20
|
+
const NEWEST = 'ORDER BY created DESC, id DESC';
|
|
18
21
|
// The newest rows `admit` keeps, newest first. The first window stays unfiltered so admit still sees, and the
|
|
19
22
|
// delivery ledger still counts, the other-project rows it refuses; past it, the reads narrow to the caller's origins.
|
|
20
23
|
function loadRecentRows(run, drifted, tenantId, options) {
|
|
21
24
|
const { needed, admit, origins } = options;
|
|
22
25
|
const keep = origins ? (e) => admit(e) && inOrigins(e, origins) : admit;
|
|
23
|
-
// `id DESC` mirrors getContext's comparator, not loadFreshRawMemories'
|
|
24
|
-
// cross-ingest-stable order: that would change what the hook injects.
|
|
25
|
-
const newest = 'ORDER BY created DESC, id DESC';
|
|
26
26
|
const window = Math.max(needed * 4, 32);
|
|
27
27
|
const originSql = origins && (origins.userGlobal ? `(origin_project = '' OR ${originInSql(origins.names)})` : originInSql(origins.names));
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
const own = { where: originSql ? `${AMBIENT_SCOPED} AND ${originSql}` : AMBIENT_SCOPED, params: [tenantId, ...(origins?.names ?? [])] };
|
|
29
|
+
// Text order is chronological only for standard UTC ISO (memory.ts), so a drifted store is read whole and re-sorted downstream.
|
|
30
|
+
if (drifted)
|
|
31
|
+
return run(`${own.where} ${NEWEST}`, [...own.params]).filter(keep);
|
|
32
|
+
let read = run(`${AMBIENT_SCOPED} ${NEWEST} LIMIT ?`, [tenantId, window]);
|
|
33
|
+
let kept = read.filter(keep);
|
|
34
|
+
if (kept.length >= needed || read.length < window)
|
|
35
|
+
return kept;
|
|
36
|
+
if (origins) {
|
|
37
|
+
read = run(`${own.where} ${NEWEST} LIMIT ?`, [...own.params, window]);
|
|
38
|
+
kept = read.filter(keep);
|
|
39
|
+
if (kept.length >= needed || read.length < window)
|
|
34
40
|
return kept;
|
|
35
|
-
if (origins) {
|
|
36
|
-
const ownWindow = run(`${ownWhere} ${newest} LIMIT ?`, [...ownParams, window]);
|
|
37
|
-
const ownKept = ownWindow.filter(keep);
|
|
38
|
-
if (ownKept.length >= needed || ownWindow.length < window)
|
|
39
|
-
return ownKept;
|
|
40
|
-
}
|
|
41
41
|
}
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
return [...kept, ...keptPastWindow(run, own, read, needed - kept.length, keep)];
|
|
43
|
+
}
|
|
44
|
+
// A window of refused rows can hide older ones, so read on in keyset pages, each four times the last, until enough are kept.
|
|
45
|
+
function keptPastWindow(run, own, read, wanted, keep) {
|
|
46
|
+
const out = [];
|
|
47
|
+
let last = read[read.length - 1];
|
|
48
|
+
for (let size = read.length * 4; out.length < wanted; size *= 4) {
|
|
49
|
+
const page = run(`${own.where} AND created <= ? AND (created < ? OR id < ?) ${NEWEST} LIMIT ?`, [...own.params, last.created, last.created, last.id, size]);
|
|
50
|
+
out.push(...page.filter(keep));
|
|
51
|
+
if (page.length < size)
|
|
52
|
+
break;
|
|
53
|
+
last = page[page.length - 1];
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
44
56
|
}
|
|
45
57
|
// The pins plus the `recentNeeded` newest rows that pass `admit`, for ambient injection. One connection;
|
|
46
58
|
// `recall` piggybacks the prompt-recall FTS query on it too. `origins` narrows the recent rows past the first window.
|
|
@@ -77,10 +89,10 @@ export function loadAmbientCandidates(hippoRoot, tenantId, recentNeeded, admit,
|
|
|
77
89
|
}, openStore);
|
|
78
90
|
}
|
|
79
91
|
// calculateStrength's decay exponent with its reward factor, pins first: the strongest rows, without scoring each one in JS.
|
|
80
|
-
|
|
92
|
+
// A function, not a constant, so the outcome-slow ablation is read per query as strengthSql reads it.
|
|
93
|
+
const decayRankSql = () => `pinned DESC,
|
|
81
94
|
CASE WHEN half_life_days > 0 THEN (julianday(?) - julianday(last_retrieved))
|
|
82
|
-
/ (half_life_days * (
|
|
83
|
-
/ (COALESCE(outcome_positive, 0) + COALESCE(outcome_negative, 0) + 1.0))) END ASC NULLS LAST,
|
|
95
|
+
/ (half_life_days * ${rewardFactorSql()}) END ASC NULLS LAST,
|
|
84
96
|
id ASC`;
|
|
85
97
|
/** Live tenant rows passing `filter`, at most `filter.cap`, in loadAllEntries' order; below the cap, every such row. */
|
|
86
98
|
export function loadContextCandidates(hippoRoot, tenantId, filter) {
|
|
@@ -99,11 +111,21 @@ export function loadContextCandidates(hippoRoot, tenantId, filter) {
|
|
|
99
111
|
where.push(`(origin_project = '' OR ${originInSql(filter.project)})`);
|
|
100
112
|
params.push(...filter.project);
|
|
101
113
|
}
|
|
114
|
+
const cap = Math.max(0, Math.trunc(filter.cap));
|
|
115
|
+
const whereSql = where.join(' AND ');
|
|
102
116
|
return onHandle(hippoRoot, (db) => {
|
|
103
|
-
// SAFETY: the
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
117
|
+
// SAFETY: the count row has the one column named.
|
|
118
|
+
const matched = db.prepare(`SELECT COUNT(*) AS n FROM memories WHERE ${whereSql}`).get(...params).n;
|
|
119
|
+
// Under the cap every match is returned, so the per-row decay math and the sort are skipped.
|
|
120
|
+
// SHORTCUT: past the cap the rank still scans the tenant; a stored decay rank column needs a schema migration, held until one is approved.
|
|
121
|
+
const ranked = matched > cap;
|
|
122
|
+
const sql = ranked
|
|
123
|
+
? `SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE id IN (
|
|
124
|
+
SELECT id FROM memories WHERE ${whereSql} ORDER BY ${decayRankSql()} LIMIT ?
|
|
125
|
+
) ORDER BY created ASC, id ASC`
|
|
126
|
+
: `SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE ${whereSql} ORDER BY created ASC, id ASC`;
|
|
127
|
+
// SAFETY: both SELECTs name exactly MEMORY_SELECT_COLUMNS, matching MemoryRow's field set; the rank sort carries ids only.
|
|
128
|
+
const rows = db.prepare(sql).all(...params, ...(ranked ? [filter.now.toISOString(), cap] : []));
|
|
107
129
|
return rows.map(rowToEntry);
|
|
108
130
|
}, openStore);
|
|
109
131
|
}
|
|
@@ -124,7 +146,8 @@ export function loadTextsHoldingWords(hippoRoot, tenantId, words, project, admit
|
|
|
124
146
|
for (let i = 0; i < unique.length; i += 200) {
|
|
125
147
|
const chunk = unique.slice(i, i + 200);
|
|
126
148
|
// SAFETY: rows' shape matches the three columns named in the SELECT below.
|
|
127
|
-
const rows = db.prepare(`SELECT content, source, origin_project FROM memories WHERE ${tenantWhere}${originWhere} AND ${admit.sql} AND (${chunk.map(() => 'instr(content, ?)
|
|
149
|
+
const rows = db.prepare(`SELECT content, source, origin_project FROM memories WHERE ${tenantWhere}${originWhere} AND ${admit.sql} AND (${chunk.map(() => 'instr(content, ?) ' +
|
|
150
|
+
'> 0').join(' OR ')})`).all(...tenantParams, ...(project ?? []), ...admit.params, ...chunk);
|
|
128
151
|
for (const row of rows)
|
|
129
152
|
out.push({ content: row.content, source: row.source ?? 'cli', origin_project: row.origin_project });
|
|
130
153
|
}
|
|
@@ -143,13 +166,17 @@ function* contentsOf(rows) {
|
|
|
143
166
|
for (const row of rows)
|
|
144
167
|
yield row.content;
|
|
145
168
|
}
|
|
146
|
-
/**
|
|
169
|
+
/** How many of a tenant's newest rows schemaFitInStore reads; older rows no longer move the fit. */
|
|
170
|
+
const SCHEMA_FIT_WINDOW_ROWS = 2000;
|
|
171
|
+
/** computeSchemaFit against a tenant's newest SCHEMA_FIT_WINDOW_ROWS rows: tag counts come from one aggregate
|
|
172
|
+
* and texts stream one column, so no row is loaded. */
|
|
147
173
|
export function schemaFitInStore(hippoRoot, tenantId, content, tags) {
|
|
174
|
+
const newest = 'SELECT tags_json, content FROM memories WHERE tenant_id = ? ORDER BY created DESC, id DESC LIMIT ?';
|
|
148
175
|
return onHandle(hippoRoot, (db) => {
|
|
149
176
|
// One read transaction, so the row count and the texts come from the same snapshot.
|
|
150
177
|
return withReadSnapshot(db, () => {
|
|
151
178
|
// SAFETY: rows' shape matches the two columns named in the SELECT.
|
|
152
|
-
const groups = db.prepare(
|
|
179
|
+
const groups = db.prepare(`SELECT tags_json, COUNT(*) AS n FROM (${newest}) GROUP BY tags_json`).all(tenantId, SCHEMA_FIT_WINDOW_ROWS);
|
|
153
180
|
let rows = 0;
|
|
154
181
|
const tagCounts = new Map();
|
|
155
182
|
for (const group of groups) {
|
|
@@ -157,9 +184,11 @@ export function schemaFitInStore(hippoRoot, tenantId, content, tags) {
|
|
|
157
184
|
for (const tag of parseJsonArray(group.tags_json))
|
|
158
185
|
tagCounts.set(tag, (tagCounts.get(tag) ?? 0) + group.n);
|
|
159
186
|
}
|
|
160
|
-
// SHORTCUT: the
|
|
187
|
+
// SHORTCUT: only the newest SCHEMA_FIT_WINDOW_ROWS rows count, so older tags drop out;
|
|
188
|
+
// a stored per-tenant tag and token tally is the upgrade.
|
|
161
189
|
// SAFETY: the SELECT names exactly the one column read.
|
|
162
|
-
const texts = db.prepare(
|
|
190
|
+
const texts = db.prepare(`SELECT content FROM (${newest})`)
|
|
191
|
+
.iterate(tenantId, SCHEMA_FIT_WINDOW_ROWS);
|
|
163
192
|
return schemaFitFrom(content, tags, { rows, tagCounts, contents: contentsOf(texts) });
|
|
164
193
|
});
|
|
165
194
|
}, openStore);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { MemoryEntry } from '../core/memory.js';
|
|
2
|
+
export type CapturedOutcome = 'captured' | 'skipped' | 'rejected';
|
|
3
|
+
export interface CaptureItem {
|
|
4
|
+
readonly content: string;
|
|
5
|
+
/** Built only for an item that is not a repeat of an earlier one, since building can throw. */
|
|
6
|
+
readonly makeEntry: () => MemoryEntry;
|
|
7
|
+
}
|
|
8
|
+
export interface CaptureWriteOpts {
|
|
9
|
+
readonly actor?: string;
|
|
10
|
+
/** No stats, as in a server process. */
|
|
11
|
+
readonly lean: boolean;
|
|
12
|
+
/** Called with each captured entry after its write, so the caller can embed it; the store never picks a provider. */
|
|
13
|
+
readonly onCaptured?: (entry: MemoryEntry) => void;
|
|
14
|
+
}
|
|
15
|
+
/** One outcome per item, in order. `keys` holds the texts already stored and gains each one written, so a repeat inside the batch is skipped. */
|
|
16
|
+
export declare function writeCapturedItems(hippoRoot: string, items: readonly CaptureItem[], keys: Set<string>, opts: CaptureWriteOpts): CapturedOutcome[];
|
|
17
|
+
//# sourceMappingURL=capture-write.d.ts.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { closeHippoDb } from '../db/index.js';
|
|
2
|
+
import { duplicateKey } from '../util/same-text.js';
|
|
3
|
+
import { writeEntryMirrors } from './entry-writes.js';
|
|
4
|
+
import { stampOriginProject } from './entry-row.js';
|
|
5
|
+
import { gatedWrite } from './gated-write.js';
|
|
6
|
+
import { updateStatsOn } from './index-and-stats.js';
|
|
7
|
+
import { openStore } from './open.js';
|
|
8
|
+
/** One outcome per item, in order. `keys` holds the texts already stored and gains each one written, so a repeat inside the batch is skipped. */
|
|
9
|
+
export function writeCapturedItems(hippoRoot, items, keys, opts) {
|
|
10
|
+
const db = openStore(hippoRoot);
|
|
11
|
+
try {
|
|
12
|
+
return items.map((item) => {
|
|
13
|
+
if (keys.has(duplicateKey(item.content)))
|
|
14
|
+
return 'skipped';
|
|
15
|
+
const entry = item.makeEntry();
|
|
16
|
+
// One rejected item must not abort the rest of this capture's items.
|
|
17
|
+
const stamped = stampOriginProject(hippoRoot, entry);
|
|
18
|
+
const outcome = gatedWrite(db, hippoRoot, stamped, { actor: opts.actor });
|
|
19
|
+
if (outcome === 'skipped:rejected')
|
|
20
|
+
return 'rejected';
|
|
21
|
+
if (outcome !== 'written')
|
|
22
|
+
return 'skipped';
|
|
23
|
+
writeEntryMirrors(hippoRoot, stamped);
|
|
24
|
+
if (!opts.lean)
|
|
25
|
+
updateStatsOn(db, hippoRoot, { remembered: 1 });
|
|
26
|
+
keys.add(duplicateKey(item.content)); // within-batch dedup
|
|
27
|
+
opts.onCaptured?.(entry);
|
|
28
|
+
return 'captured';
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
closeHippoDb(db);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=capture-write.js.map
|
package/dist/store/cards.js
CHANGED
|
@@ -4,8 +4,9 @@ import { rowToSessionHandoff, isHandoffOutcome } from '../core/handoff.js';
|
|
|
4
4
|
import { CARD_LEASE_MS, assertCardTransition } from '../core/card.js';
|
|
5
5
|
import { assertTenantId } from './tenant.js';
|
|
6
6
|
import { onHandle, openStore } from './open.js';
|
|
7
|
-
import { chunked } from '
|
|
7
|
+
import { chunked } from '../util/chunked.js';
|
|
8
8
|
import { HANDOFF_COLUMNS } from './handoffs.js';
|
|
9
|
+
import { placeholders } from '../util/sql.js';
|
|
9
10
|
const CARD_COLUMNS = 'id, title, status, assignee_runtime, repo, contract, budget, lease_until, heartbeat_at, created_at, updated_at, tenant_id, scope';
|
|
10
11
|
function rowToCard(row) {
|
|
11
12
|
return {
|
|
@@ -88,10 +89,10 @@ function transitionCards(db, tenantId, cardIds, options) {
|
|
|
88
89
|
const now = new Date().toISOString();
|
|
89
90
|
// Lease columns follow status: set on the move to running, cleared on every other move (rule 15).
|
|
90
91
|
const lease = to === 'running' ? [leaseUntilFrom(now), now] : [null, null];
|
|
91
|
-
const fromPlaceholders = from.
|
|
92
|
+
const fromPlaceholders = placeholders(from.length);
|
|
92
93
|
const sql = `
|
|
93
94
|
UPDATE cards SET status = ?, updated_at = ?, lease_until = ?, heartbeat_at = ?${extra?.setSql ? `, ${extra.setSql}` : ''}
|
|
94
|
-
WHERE id IN (${cardIds.
|
|
95
|
+
WHERE id IN (${placeholders(cardIds.length)}) AND tenant_id = ? AND status IN (${fromPlaceholders})${extra?.whereSql ? ` AND ${extra.whereSql}` : ''}
|
|
95
96
|
`;
|
|
96
97
|
const params = [to, now, ...lease, ...(extra?.params ?? []), ...cardIds, tenantId, ...from];
|
|
97
98
|
const result = db.prepare(sql).run(...params);
|
|
@@ -134,9 +135,9 @@ export function createCard(hippoRoot, tenantId, input) {
|
|
|
134
135
|
function everyParentDone(db, tenantId, dependsOn) {
|
|
135
136
|
if (dependsOn.length === 0)
|
|
136
137
|
return true;
|
|
137
|
-
const
|
|
138
|
+
const marks = placeholders(dependsOn.length);
|
|
138
139
|
// SAFETY: rows' shape matches the two columns named in the SELECT below.
|
|
139
|
-
const rows = db.prepare(`SELECT id, status FROM cards WHERE tenant_id = ? AND id IN (${
|
|
140
|
+
const rows = db.prepare(`SELECT id, status FROM cards WHERE tenant_id = ? AND id IN (${marks})`).all(tenantId, ...dependsOn);
|
|
140
141
|
const found = new Map(rows.map((r) => [r.id, r.status]));
|
|
141
142
|
// Pre-check before any write: a typo'd --depends-on can never commit a card row.
|
|
142
143
|
for (const parentId of dependsOn) {
|