hippo-memory 1.62.0 → 1.63.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 +9 -0
- package/dist/agent-memories/apply.d.ts +2 -0
- package/dist/agent-memories/apply.js +23 -1
- package/dist/agent-memories/claude-code.d.ts +2 -1
- package/dist/agent-memories/claude-code.js +4 -4
- package/dist/agent-memories/report.d.ts +1 -0
- package/dist/agent-memories/report.js +2 -2
- package/dist/agent-memories/sync.js +37 -23
- package/dist/ambient-store.d.ts +4 -4
- package/dist/ambient-store.js +4 -3
- package/dist/api/assemble.d.ts +7 -10
- package/dist/api/assemble.js +7 -12
- package/dist/api/audit.d.ts +2 -2
- package/dist/api/audit.js +2 -2
- package/dist/api/auth.d.ts +6 -6
- package/dist/api/auth.js +5 -7
- package/dist/api/context-select.d.ts +4 -3
- package/dist/api/context-select.js +9 -11
- package/dist/api/context-types.d.ts +12 -17
- package/dist/api/context.d.ts +3 -2
- package/dist/api/context.js +20 -20
- package/dist/api/dormant.js +1 -1
- package/dist/api/drill-down.d.ts +4 -4
- package/dist/api/drill-down.js +22 -10
- package/dist/api/outcome.d.ts +8 -13
- package/dist/api/outcome.js +13 -17
- package/dist/api/promote.d.ts +5 -8
- package/dist/api/quarantine.js +3 -2
- package/dist/api/recall-types.d.ts +43 -55
- package/dist/api/recall.d.ts +3 -3
- package/dist/api/recall.js +7 -18
- package/dist/api/remember.d.ts +2 -2
- package/dist/api/sleep.d.ts +7 -35
- package/dist/api/sleep.js +3 -2
- package/dist/api/tokens.d.ts +2 -2
- package/dist/api/tokens.js +2 -2
- package/dist/api/types.d.ts +6 -10
- package/dist/api/types.js +2 -4
- package/dist/audit-prune.d.ts +4 -6
- package/dist/audit-prune.js +3 -5
- package/dist/audit.js +11 -33
- package/dist/auth.d.ts +8 -9
- package/dist/auth.js +5 -7
- package/dist/autolearn.d.ts +1 -1
- package/dist/autolearn.js +1 -1
- package/dist/availability.js +3 -5
- package/dist/capture/command.d.ts +5 -4
- package/dist/capture/command.js +8 -22
- package/dist/capture/compact.d.ts +1 -1
- package/dist/capture/compact.js +9 -13
- package/dist/capture/extract.js +37 -115
- package/dist/capture-error.d.ts +1 -1
- package/dist/capture-error.js +1 -1
- package/dist/churn-git.d.ts +1 -1
- package/dist/churn-git.js +1 -1
- package/dist/cli/audit.js +3 -4
- package/dist/cli/auth.js +3 -6
- package/dist/cli/curate.d.ts +1 -1
- package/dist/cli/curate.js +8 -24
- package/dist/cli/dag.js +5 -9
- package/dist/cli/decisions.js +10 -21
- package/dist/cli/explain.js +2 -1
- package/dist/cli/goals.js +1 -1
- package/dist/cli/init.js +1 -1
- package/dist/cli/playbooks.js +4 -9
- package/dist/cli/projects.js +3 -1
- package/dist/cli/recall.js +2 -1
- package/dist/cli/remember.js +6 -18
- package/dist/cli/session-hooks.js +21 -40
- package/dist/cli/setup.d.ts +1 -1
- package/dist/cli/setup.js +2 -6
- package/dist/cli/shared.js +3 -3
- package/dist/cli/slack.js +1 -1
- package/dist/cli/sleep.js +27 -1
- package/dist/cli/status.d.ts +4 -4
- package/dist/cli/status.js +7 -12
- package/dist/cli/transfer.js +8 -13
- package/dist/cli/usage.js +9 -6
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +4 -9
- package/dist/compaction-record.d.ts +2 -0
- package/dist/compaction-record.js +89 -68
- package/dist/compare.d.ts +11 -16
- package/dist/compare.js +11 -16
- package/dist/config.d.ts +19 -18
- package/dist/config.js +90 -67
- package/dist/connectors/github/backfill.d.ts +2 -2
- package/dist/connectors/github/backfill.js +8 -15
- package/dist/connectors/github/cli-impl.js +3 -8
- package/dist/connectors/github/deletion.d.ts +5 -12
- package/dist/connectors/github/deletion.js +5 -12
- package/dist/connectors/github/dlq.d.ts +6 -9
- package/dist/connectors/github/dlq.js +2 -3
- package/dist/connectors/github/ingest.d.ts +5 -7
- package/dist/connectors/github/ingest.js +8 -12
- package/dist/connectors/github/octokit-client.d.ts +3 -5
- package/dist/connectors/github/octokit-client.js +5 -6
- package/dist/connectors/github/signature.d.ts +9 -39
- package/dist/connectors/github/signature.js +9 -39
- package/dist/connectors/github/tenant-routing.d.ts +1 -1
- package/dist/connectors/github/tenant-routing.js +1 -1
- package/dist/connectors/github/transform.js +2 -2
- package/dist/connectors/github/types.d.ts +2 -10
- package/dist/connectors/github/types.js +1 -3
- package/dist/connectors/slack/deletion.d.ts +3 -8
- package/dist/connectors/slack/deletion.js +3 -8
- package/dist/connectors/slack/dlq.d.ts +1 -1
- package/dist/connectors/slack/ingest.d.ts +1 -1
- package/dist/connectors/slack/ingest.js +7 -16
- package/dist/connectors/slack/signature.d.ts +1 -1
- package/dist/connectors/slack/tenant-routing.d.ts +3 -5
- package/dist/connectors/slack/tenant-routing.js +3 -5
- package/dist/connectors/slack/transform.d.ts +5 -6
- package/dist/connectors/slack/transform.js +5 -6
- package/dist/connectors/slack/types.d.ts +2 -6
- package/dist/connectors/slack/types.js +1 -3
- package/dist/connectors/slack/web-client.js +10 -3
- package/dist/connectors/slack/workspaces.d.ts +3 -5
- package/dist/connectors/slack/workspaces.js +3 -5
- package/dist/consolidate/conflicts.js +3 -14
- package/dist/consolidate/decay.js +9 -29
- package/dist/consolidate/llm-passes.js +4 -5
- package/dist/consolidate/merge.js +8 -23
- package/dist/consolidate/run.d.ts +1 -8
- package/dist/consolidate/run.js +3 -25
- package/dist/consolidate/sleep.js +5 -17
- package/dist/consolidate/traces.js +9 -21
- package/dist/customer-notes.d.ts +5 -7
- package/dist/customer-notes.js +6 -9
- package/dist/dag.d.ts +10 -21
- package/dist/dag.js +23 -73
- package/dist/db/continuity.js +2 -2
- package/dist/db/migrations/v14.js +1 -1
- package/dist/db/migrations/v15.js +1 -2
- package/dist/db/migrations/v16.js +3 -4
- package/dist/db/migrations/v17.js +2 -3
- package/dist/db/migrations/v19.js +1 -1
- package/dist/db/migrations/v20.js +1 -1
- package/dist/db/migrations/v21.js +2 -6
- package/dist/db/migrations/v22.js +2 -4
- package/dist/db/migrations/v23.js +1 -1
- package/dist/db/migrations/v24.js +4 -6
- package/dist/db/migrations/v25.js +2 -3
- package/dist/db/migrations/v26.js +3 -3
- package/dist/db/migrations/v27.js +2 -10
- package/dist/db/migrations/v28.js +5 -8
- package/dist/db/migrations/v29.js +3 -4
- package/dist/db/migrations/v30.js +2 -2
- package/dist/db/migrations/v31.js +1 -1
- package/dist/db/migrations/v32.js +1 -1
- package/dist/db/migrations/v33.js +3 -3
- package/dist/db/migrations/v34.js +1 -1
- package/dist/db/migrations/v35.js +3 -4
- package/dist/db/migrations/v36.js +3 -4
- package/dist/db/migrations/v37.js +5 -5
- package/dist/db/migrations/v38.js +7 -8
- package/dist/db/migrations/v39.js +1 -1
- package/dist/db/migrations/v40.js +4 -16
- package/dist/db/migrations/v41.js +3 -4
- package/dist/db/migrations/v42.js +3 -4
- package/dist/db/migrations/v45.js +1 -1
- package/dist/db/migrations/v46.js +1 -1
- package/dist/db/migrations/v47.js +1 -1
- package/dist/db/migrations/v48.js +1 -1
- package/dist/decisions.d.ts +2 -2
- package/dist/decisions.js +6 -6
- package/dist/dedupe.js +86 -61
- package/dist/delivery-recorder.js +154 -135
- package/dist/doctor.js +119 -104
- package/dist/dormant.js +1 -4
- package/dist/embedding-provider.d.ts +4 -8
- package/dist/embedding-provider.js +4 -8
- package/dist/embeddings.js +55 -47
- package/dist/env.d.ts +1 -1
- package/dist/env.js +12 -12
- package/dist/escape.d.ts +5 -0
- package/dist/escape.js +10 -0
- package/dist/eval-stats.d.ts +1 -2
- package/dist/eval-stats.js +1 -2
- package/dist/eval-suite.js +27 -21
- package/dist/extract.js +4 -9
- package/dist/failure-log.d.ts +3 -3
- package/dist/failure-log.js +1 -1
- package/dist/forward-claim-detector.d.ts +2 -4
- package/dist/forward-claim-detector.js +6 -11
- package/dist/goals.d.ts +3 -3
- package/dist/goals.js +5 -6
- package/dist/graph/read.d.ts +2 -2
- package/dist/graph/read.js +5 -6
- package/dist/graph/types.d.ts +8 -8
- package/dist/graph/write.d.ts +7 -14
- package/dist/graph/write.js +16 -23
- package/dist/graph-extract.d.ts +7 -8
- package/dist/graph-extract.js +62 -72
- package/dist/graph-recall.d.ts +2 -2
- package/dist/graph-recall.js +55 -49
- package/dist/graph-stream.d.ts +5 -6
- package/dist/graph-stream.js +66 -57
- package/dist/graph-view.d.ts +2 -2
- package/dist/graph-view.js +7 -7
- package/dist/half-life-migration.d.ts +1 -2
- package/dist/half-life-migration.js +2 -3
- package/dist/hooks/codex-session.js +1 -1
- package/dist/hooks/codex-wrapper.d.ts +1 -1
- package/dist/hooks/codex-wrapper.js +3 -2
- package/dist/hooks/json-hooks.d.ts +2 -2
- package/dist/hooks/json-hooks.js +5 -4
- package/dist/hooks/opencode.d.ts +1 -1
- package/dist/hooks/opencode.js +5 -4
- package/dist/hooks/shared.d.ts +3 -7
- package/dist/hooks/shared.js +1 -8
- package/dist/http-util.d.ts +2 -3
- package/dist/http-util.js +3 -0
- package/dist/importers/core.d.ts +2 -9
- package/dist/importers/core.js +15 -30
- package/dist/importers/sources.js +2 -1
- package/dist/importers/vault.js +2 -20
- package/dist/incidents.d.ts +1 -1
- package/dist/incidents.js +1 -1
- package/dist/instruction-detect.d.ts +1 -1
- package/dist/instruction-detect.js +1 -1
- package/dist/invalidation.d.ts +3 -0
- package/dist/invalidation.js +160 -114
- package/dist/json.d.ts +5 -0
- package/dist/json.js +4 -0
- package/dist/judgment.js +1 -2
- package/dist/local-embedding.js +1 -1
- package/dist/mcp/admin-tools.js +7 -17
- package/dist/mcp/format.js +1 -1
- package/dist/mcp/framing.js +3 -6
- package/dist/mcp/protocol.d.ts +2 -5
- package/dist/mcp/protocol.js +1 -3
- package/dist/mcp/recall-tools.js +12 -15
- package/dist/mcp/request.js +4 -3
- package/dist/mcp/session-state.js +2 -3
- package/dist/mcp/stdio.js +2 -1
- package/dist/mcp/tools.js +9 -6
- package/dist/memory-value-weights.d.ts +5 -8
- package/dist/memory-value-weights.js +5 -8
- package/dist/memory-value.d.ts +13 -13
- package/dist/memory-value.js +26 -37
- package/dist/memory.d.ts +20 -22
- package/dist/memory.js +24 -48
- package/dist/multihop.d.ts +1 -1
- package/dist/multihop.js +3 -2
- package/dist/owner-validation.d.ts +4 -5
- package/dist/owner-validation.js +4 -5
- package/dist/physics.d.ts +4 -4
- package/dist/physics.js +7 -9
- package/dist/policies.d.ts +9 -10
- package/dist/policies.js +12 -14
- package/dist/postinstall.js +3 -6
- package/dist/predictions/planning-fallacy.d.ts +9 -14
- package/dist/predictions/planning-fallacy.js +10 -16
- package/dist/predictions/store.d.ts +15 -23
- package/dist/predictions/store.js +36 -33
- package/dist/processes.d.ts +2 -7
- package/dist/processes.js +3 -3
- package/dist/project-briefs.d.ts +2 -3
- package/dist/project-briefs.js +9 -13
- package/dist/project-identity.d.ts +22 -9
- package/dist/project-identity.js +47 -12
- package/dist/project-merge.d.ts +17 -2
- package/dist/project-merge.js +109 -27
- package/dist/project-remote.d.ts +12 -0
- package/dist/project-remote.js +138 -0
- package/dist/prompt-recall.js +1 -2
- package/dist/rate-limit.d.ts +1 -1
- package/dist/rate-limit.js +1 -1
- package/dist/raw-archive.d.ts +9 -0
- package/dist/raw-archive.js +70 -53
- package/dist/recall-history.d.ts +19 -20
- package/dist/recall-history.js +24 -42
- package/dist/recall-pipeline.js +4 -28
- package/dist/recall-scope.d.ts +7 -8
- package/dist/recall-scope.js +7 -8
- package/dist/recall-trace.d.ts +5 -9
- package/dist/recall-trace.js +6 -10
- package/dist/refine-llm.d.ts +1 -1
- package/dist/refine-llm.js +2 -2
- package/dist/reject-flow.d.ts +3 -4
- package/dist/reject-flow.js +122 -117
- package/dist/rejection.d.ts +5 -6
- package/dist/rejection.js +7 -15
- package/dist/rerankers/clef.d.ts +1 -1
- package/dist/rerankers/jev.d.ts +1 -2
- package/dist/rerankers/jev.js +4 -5
- package/dist/rerankers/llm.d.ts +1 -2
- package/dist/rerankers/llm.js +1 -2
- package/dist/rerankers/types.d.ts +1 -2
- package/dist/rrf.d.ts +2 -2
- package/dist/rrf.js +2 -2
- package/dist/search/bm25-search.d.ts +1 -1
- package/dist/search/bm25-search.js +2 -1
- package/dist/search/boosts.js +2 -1
- package/dist/search/hybrid.d.ts +1 -1
- package/dist/search/hybrid.js +2 -1
- package/dist/search/physics-search.d.ts +1 -1
- package/dist/search/physics-search.js +2 -1
- package/dist/search/types.d.ts +2 -0
- package/dist/search/types.js +3 -1
- package/dist/secret-detect.d.ts +4 -5
- package/dist/secret-detect.js +6 -10
- package/dist/server/auth.js +5 -5
- package/dist/server/client-ip.js +1 -1
- package/dist/server/cursor.js +2 -1
- package/dist/server/mcp-http.js +4 -4
- package/dist/server/request.d.ts +3 -6
- package/dist/server/request.js +6 -7
- package/dist/server/routes/admin.js +5 -4
- package/dist/server/routes/customer-notes.js +6 -5
- package/dist/server/routes/decisions.js +4 -3
- package/dist/server/routes/incidents.js +7 -5
- package/dist/server/routes/memories.js +7 -7
- package/dist/server/routes/policies.js +3 -2
- package/dist/server/routes/predictions.js +12 -15
- package/dist/server/routes/processes.js +3 -2
- package/dist/server/routes/project-briefs.js +8 -7
- package/dist/server/routes/recall.js +95 -93
- package/dist/server/routes/skills.js +6 -5
- package/dist/server/types.d.ts +1 -1
- package/dist/server/validation.d.ts +1 -2
- package/dist/server/validation.js +7 -14
- package/dist/server-detect.js +72 -58
- package/dist/server.d.ts +2 -2
- package/dist/server.js +131 -117
- package/dist/shared.d.ts +17 -17
- package/dist/shared.js +93 -97
- package/dist/skills.d.ts +3 -3
- package/dist/skills.js +8 -8
- package/dist/store/audit-event.d.ts +2 -2
- package/dist/store/audit-event.js +1 -1
- package/dist/store/candidates.d.ts +2 -2
- package/dist/store/candidates.js +4 -3
- package/dist/store/conflicts.js +30 -22
- package/dist/store/delete-and-batch.d.ts +11 -14
- package/dist/store/delete-and-batch.js +40 -91
- package/dist/store/entry-reads.d.ts +17 -25
- package/dist/store/entry-reads.js +59 -40
- package/dist/store/entry-row.d.ts +6 -24
- package/dist/store/entry-row.js +6 -24
- package/dist/store/entry-writes.d.ts +6 -7
- package/dist/store/entry-writes.js +13 -11
- package/dist/store/handoffs.d.ts +1 -1
- package/dist/store/handoffs.js +7 -10
- package/dist/store/index-and-stats.d.ts +2 -6
- package/dist/store/index-and-stats.js +4 -10
- package/dist/store/mirrors.d.ts +6 -19
- package/dist/store/mirrors.js +14 -39
- package/dist/store/open.js +9 -31
- package/dist/store/rows.d.ts +5 -11
- package/dist/store/rows.js +6 -11
- package/dist/store/search-rows.d.ts +17 -34
- package/dist/store/search-rows.js +31 -59
- package/dist/store/sessions.d.ts +4 -5
- package/dist/store/sessions.js +5 -6
- package/dist/store/summaries.d.ts +13 -17
- package/dist/store/summaries.js +26 -70
- package/dist/support-bundle.js +4 -8
- package/dist/tenant.d.ts +1 -5
- package/dist/token-ledger.d.ts +1 -1
- package/dist/token-ledger.js +3 -5
- package/dist/trace.js +1 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/working-memory.d.ts +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 +1 -1
package/dist/recall-scope.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* cycle. Mirrors the v39 `project-identity.ts` precedent. api.ts imports
|
|
2
|
+
* Recall-side scope predicates, in a leaf module so shared.ts (which api.ts
|
|
3
|
+
* imports) can apply the same default-deny rule to searchBothHybrid's internal
|
|
4
|
+
* candidate loads without an import cycle. api.ts imports
|
|
6
5
|
* these for its own call sites AND re-exports them for back-compat
|
|
7
6
|
* (`api.isPrivateScope`, test imports of `passesScopeFilterForRecall`).
|
|
8
7
|
*/
|
|
@@ -35,7 +34,7 @@ export function assertNonEmpty(arr, name) {
|
|
|
35
34
|
}
|
|
36
35
|
assertNonEmpty(RECALL_DEFAULT_DENY_SCOPES, 'RECALL_DEFAULT_DENY_SCOPES');
|
|
37
36
|
/**
|
|
38
|
-
*
|
|
37
|
+
* Source-agnostic private-scope detector. A scope string is treated
|
|
39
38
|
* as private when it has the shape `<lowercase-source>:private:<rest>`.
|
|
40
39
|
*
|
|
41
40
|
* Examples that match:
|
|
@@ -68,7 +67,7 @@ export function isPrivateScope(scope) {
|
|
|
68
67
|
* `<source>:private:*` scope and on the `RECALL_DEFAULT_DENY_SCOPES`
|
|
69
68
|
* quarantine buckets. `null` and public scopes pass.
|
|
70
69
|
*
|
|
71
|
-
* @internal
|
|
70
|
+
* @internal Exported for test parity with
|
|
72
71
|
* `RECALL_DEFAULT_DENY_SCOPES` (single-source-of-truth verification). NOT part
|
|
73
72
|
* of the public API surface; not re-exported from `src/index.ts`. Subject to
|
|
74
73
|
* change without semver bump.
|
|
@@ -80,7 +79,7 @@ export function passesScopeFilterForRecall(scope, requested) {
|
|
|
80
79
|
return !isRestrictedScope(scope);
|
|
81
80
|
}
|
|
82
81
|
/**
|
|
83
|
-
*
|
|
82
|
+
* The CLI `--scope` variant of the recall filter (JS half of the
|
|
84
83
|
* SQL 'default-deny-or-exact' mode in loadSearchRows).
|
|
85
84
|
*
|
|
86
85
|
* The CLI flag predates the envelope column as a TAG-boost ranking hint
|
|
@@ -135,7 +134,7 @@ export function canReadScope(actor, scope) {
|
|
|
135
134
|
return true;
|
|
136
135
|
return (actor.scopes ?? []).includes(scope);
|
|
137
136
|
}
|
|
138
|
-
/** Authorize an explicitly requested scope before any read honours it (
|
|
137
|
+
/** Authorize an explicitly requested scope before any read honours it (member scope grants). */
|
|
139
138
|
export function assertScopeRequestAllowed(actor, requested) {
|
|
140
139
|
if (requested === undefined || requested === '')
|
|
141
140
|
return;
|
package/dist/recall-trace.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* (docs/plans/2026-08-02-lc1-recall-trace-persistence.md).
|
|
2
|
+
* Retrieval-trace persistence.
|
|
4
3
|
*
|
|
5
4
|
* Single producer for the `recall_traces` / `recall_trace_results` /
|
|
6
5
|
* `recall_trace_outcomes` tables (schema v40). Every recall on the three
|
|
@@ -59,14 +58,11 @@ export declare function writeRecallTrace(db: DatabaseSyncLike, input: RecallTrac
|
|
|
59
58
|
* Used at api.getContext and CLI cmdRecall — sites where the block's own
|
|
60
59
|
* convention is per-call handles (writeEntry, saveIndex) and the earlier
|
|
61
60
|
* audit handles are already closed. NOT used by api.recall, which must
|
|
62
|
-
* reuse the caller's open handle (
|
|
61
|
+
* reuse the caller's open handle (no-side-effects contract,
|
|
63
62
|
* tests/api-recall-no-side-effects.test.ts).
|
|
64
63
|
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* originally, on its own connection, separate from the `last_retrieval_ids`
|
|
68
|
-
* write in `saveIndex` — two connections meant two commits, so a crash or
|
|
69
|
-
* a failed second write could advance one without the other. LOCKSTEP
|
|
64
|
+
* This function does NOT touch the `last_trace_id` meta key: its own connection
|
|
65
|
+
* would commit apart from `saveIndex`, so a crash could advance one key alone. LOCKSTEP
|
|
70
66
|
* INVARIANT: `last_trace_id` must only ever advance in the SAME write as
|
|
71
67
|
* `last_retrieval_ids`. The caller now does: call this function FIRST, set
|
|
72
68
|
* `localIndex.last_trace_id` from the returned id, THEN call `saveIndex`
|
|
@@ -98,7 +94,7 @@ export interface RecordTraceOutcomeInput {
|
|
|
98
94
|
* Lives in its own append-only table, not audit_log metadata: audit_log is
|
|
99
95
|
* pruned by `pruneAuditLog`, and pruning must never erase training data.
|
|
100
96
|
*
|
|
101
|
-
*
|
|
97
|
+
* Validation: `traceId`/`memoryIds` reach
|
|
102
98
|
* this function from caller-side state (`last_trace_id` / applied outcome
|
|
103
99
|
* ids) that can go stale relative to the trace it names — a forgotten
|
|
104
100
|
* memory, a tenant switch mid-session, or a race between two callers. Two
|
package/dist/recall-trace.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* (docs/plans/2026-08-02-lc1-recall-trace-persistence.md).
|
|
2
|
+
* Retrieval-trace persistence.
|
|
4
3
|
*
|
|
5
4
|
* Single producer for the `recall_traces` / `recall_trace_results` /
|
|
6
5
|
* `recall_trace_outcomes` tables (schema v40). Every recall on the three
|
|
@@ -21,7 +20,7 @@ import { DELIVERY_LEDGER_VERSION } from './delivery-recorder.js';
|
|
|
21
20
|
import { log } from './log.js';
|
|
22
21
|
/**
|
|
23
22
|
* Strip a RerankStep down to {stage, multiplier, scoreBefore, scoreAfter}
|
|
24
|
-
* before persisting
|
|
23
|
+
* before persisting. `note` is
|
|
25
24
|
* free-form human text — the CLI's goal-boost step embeds matched goal tag
|
|
26
25
|
* text there, so persisting it verbatim would leak raw user content into
|
|
27
26
|
* training data via `rerank_json`. Only the four structured fields survive;
|
|
@@ -89,14 +88,11 @@ export function writeRecallTrace(db, input) {
|
|
|
89
88
|
* Used at api.getContext and CLI cmdRecall — sites where the block's own
|
|
90
89
|
* convention is per-call handles (writeEntry, saveIndex) and the earlier
|
|
91
90
|
* audit handles are already closed. NOT used by api.recall, which must
|
|
92
|
-
* reuse the caller's open handle (
|
|
91
|
+
* reuse the caller's open handle (no-side-effects contract,
|
|
93
92
|
* tests/api-recall-no-side-effects.test.ts).
|
|
94
93
|
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
* originally, on its own connection, separate from the `last_retrieval_ids`
|
|
98
|
-
* write in `saveIndex` — two connections meant two commits, so a crash or
|
|
99
|
-
* a failed second write could advance one without the other. LOCKSTEP
|
|
94
|
+
* This function does NOT touch the `last_trace_id` meta key: its own connection
|
|
95
|
+
* would commit apart from `saveIndex`, so a crash could advance one key alone. LOCKSTEP
|
|
100
96
|
* INVARIANT: `last_trace_id` must only ever advance in the SAME write as
|
|
101
97
|
* `last_retrieval_ids`. The caller now does: call this function FIRST, set
|
|
102
98
|
* `localIndex.last_trace_id` from the returned id, THEN call `saveIndex`
|
|
@@ -135,7 +131,7 @@ export function writeRecallTraceAtRoot(root, input) {
|
|
|
135
131
|
* Lives in its own append-only table, not audit_log metadata: audit_log is
|
|
136
132
|
* pruned by `pruneAuditLog`, and pruning must never erase training data.
|
|
137
133
|
*
|
|
138
|
-
*
|
|
134
|
+
* Validation: `traceId`/`memoryIds` reach
|
|
139
135
|
* this function from caller-side state (`last_trace_id` / applied outcome
|
|
140
136
|
* ids) that can go stale relative to the trace it names — a forgotten
|
|
141
137
|
* memory, a tenant switch mid-session, or a race between two callers. Two
|
package/dist/refine-llm.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export interface RefineOptions {
|
|
|
23
23
|
/** Injected for testing — defaults to the real fetch. */
|
|
24
24
|
fetcher?: typeof fetch;
|
|
25
25
|
/**
|
|
26
|
-
*
|
|
26
|
+
* Tenant scope. When provided, refineStore only scans consolidated
|
|
27
27
|
* entries belonging to this tenant, and parent lookups are scoped to the
|
|
28
28
|
* same tenant. Cross-tenant parents return null from readEntry and are
|
|
29
29
|
* silently skipped (refine still produces output from merged content).
|
package/dist/refine-llm.js
CHANGED
|
@@ -109,7 +109,7 @@ export async function refineStore(hippoRoot, opts) {
|
|
|
109
109
|
failed: 0,
|
|
110
110
|
details: [],
|
|
111
111
|
};
|
|
112
|
-
//
|
|
112
|
+
// When opts.tenantId is provided, scope the top-level scan to this
|
|
113
113
|
// tenant's consolidated entries.
|
|
114
114
|
const entries = loadAllEntries(hippoRoot, opts.tenantId);
|
|
115
115
|
let processed = 0;
|
|
@@ -130,7 +130,7 @@ export async function refineStore(hippoRoot, opts) {
|
|
|
130
130
|
const sources = [];
|
|
131
131
|
const parentIds = Array.isArray(entry.parents) ? entry.parents : [];
|
|
132
132
|
for (const pid of parentIds) {
|
|
133
|
-
//
|
|
133
|
+
// Parent lookup scoped by opts.tenantId when provided.
|
|
134
134
|
// Cross-tenant parents return null and are silently skipped — refine
|
|
135
135
|
// still produces output from the merged content alone (graceful
|
|
136
136
|
// degradation rather than refuse-to-refine).
|
package/dist/reject-flow.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* docs/plans/2026-08-15-at1-rejected-value-tombstone.md (T2, plan §4).
|
|
2
|
+
* Rejected-value tombstone: shared reject/unreject/list flow.
|
|
4
3
|
*
|
|
5
4
|
* The CLI (`hippo reject`/`rejections`/`unreject`) and the Context-based
|
|
6
5
|
* `api.reject`/`api.unreject`/`api.listRejections` surfaces both need the
|
|
@@ -24,12 +23,12 @@ export interface RejectFlowOpts {
|
|
|
24
23
|
}
|
|
25
24
|
export interface RejectFlowResult {
|
|
26
25
|
digest: string;
|
|
27
|
-
/** The rejected content, for the CLI's at-reject-time echo (
|
|
26
|
+
/** The rejected content, for the CLI's at-reject-time echo (the
|
|
28
27
|
* tombstone itself stores no content — this is the only place it's seen
|
|
29
28
|
* again after this call returns). */
|
|
30
29
|
content: string;
|
|
31
30
|
/** Every row removed this call, live or dormant: all whose normalized digest matched (not just the id
|
|
32
|
-
* passed,
|
|
31
|
+
* passed, since duplicates share a digest), and each sleep-merged row holding the value, whose other
|
|
33
32
|
* texts move to a new row: listed in successorIds when it was live, dormantSuccessorIds when dormant. */
|
|
34
33
|
removedIds: string[];
|
|
35
34
|
/** Subset of removedIds that were kind='raw' (archived, not deleted). */
|
package/dist/reject-flow.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* docs/plans/2026-08-15-at1-rejected-value-tombstone.md (T2, plan §4).
|
|
2
|
+
* Rejected-value tombstone: shared reject/unreject/list flow.
|
|
4
3
|
*
|
|
5
4
|
* The CLI (`hippo reject`/`rejections`/`unreject`) and the Context-based
|
|
6
5
|
* `api.reject`/`api.unreject`/`api.listRejections` surfaces both need the
|
|
@@ -24,16 +23,7 @@ import { deleteEntryCore } from './store/delete-and-batch.js';
|
|
|
24
23
|
import { heldTexts } from './same-text.js';
|
|
25
24
|
import { mergedSuccessor } from './merged-row.js';
|
|
26
25
|
import { rejectionDigest, normalizeValueForRejection, insertRejectedValue, deleteRejectedValue, listRejectedValues, } from './rejection.js';
|
|
27
|
-
|
|
28
|
-
* `hippo reject` / `api.reject` core flow. ONE connection, one transaction:
|
|
29
|
-
* insert the tombstone, enumerate + remove every live tenant row whose
|
|
30
|
-
* normalized digest matches (kind-aware), one aggregate `reject_value`
|
|
31
|
-
* audit, COMMIT. Then post-commit (mirrors the existing purge+reaper
|
|
32
|
-
* pattern verbatim from api.archiveRaw, api.ts:1913-1938): best-effort
|
|
33
|
-
* mirror purge per removed id, `mirror_cleaned_at` stamps for raw ids.
|
|
34
|
-
* index.json itself is only refreshed by `rebuildIndex()`.
|
|
35
|
-
*/
|
|
36
|
-
export function rejectValue(opts) {
|
|
26
|
+
function assertRejectOpts(opts) {
|
|
37
27
|
if (!opts.reason.trim()) {
|
|
38
28
|
throw new Error('reject requires a non-empty --reason (the tombstone stores no content; reason is its only identity).');
|
|
39
29
|
}
|
|
@@ -41,10 +31,8 @@ export function rejectValue(opts) {
|
|
|
41
31
|
throw new Error('reject requires either a memory id or --value.');
|
|
42
32
|
}
|
|
43
33
|
if (opts.memoryId !== undefined && opts.value !== undefined) {
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
// passing both silently got the memoryId path with `value` ignored —
|
|
47
|
-
// surprising for a caller who thought they were rejecting `value`.
|
|
34
|
+
// Enforced here, not only in the CLI parser, so a direct api caller passing both
|
|
35
|
+
// is refused instead of silently getting the memoryId path with `value` ignored.
|
|
48
36
|
throw new Error('reject accepts either a memory id or --value, not both.');
|
|
49
37
|
}
|
|
50
38
|
if (opts.value !== undefined && normalizeValueForRejection(opts.value).length === 0) {
|
|
@@ -53,28 +41,122 @@ export function rejectValue(opts) {
|
|
|
53
41
|
// and pollute the listing.
|
|
54
42
|
throw new Error('reject --value requires non-empty content.');
|
|
55
43
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
44
|
+
}
|
|
45
|
+
function contentToReject(db, opts) {
|
|
46
|
+
if (opts.memoryId === undefined)
|
|
47
|
+
return opts.value;
|
|
48
|
+
// SAFETY: row's shape matches the two columns named in the SELECT above.
|
|
49
|
+
const row = db
|
|
50
|
+
.prepare(`SELECT content, tenant_id FROM memories WHERE id = ?`)
|
|
51
|
+
.get(opts.memoryId);
|
|
52
|
+
if (!row || row.tenant_id !== opts.tenantId) {
|
|
53
|
+
throw new Error(`memory not found: ${opts.memoryId}`);
|
|
54
|
+
}
|
|
55
|
+
return row.content;
|
|
56
|
+
}
|
|
57
|
+
function removeLiveRows(db, opts, holdsValue, removal) {
|
|
58
|
+
const { removedIds, removedRawIds, successors } = removal;
|
|
59
|
+
const merged = [];
|
|
60
|
+
for (const row of selectAllEntries(db, opts.tenantId)) {
|
|
61
|
+
if (!holdsValue(row.content)) {
|
|
62
|
+
if (heldTexts(row).some(holdsValue))
|
|
63
|
+
merged.push(row);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (row.kind === 'raw') {
|
|
67
|
+
// Append-only trigger respected — archiveRawMemory is the only
|
|
68
|
+
// legitimate removal path for kind='raw', and its inner SAVEPOINT
|
|
69
|
+
// composes safely inside this BEGIN/COMMIT.
|
|
70
|
+
archiveRawMemory(db, row.id, { reason: opts.reason, who: opts.actor });
|
|
71
|
+
removedRawIds.push(row.id);
|
|
68
72
|
}
|
|
69
73
|
else {
|
|
70
|
-
|
|
74
|
+
// suppressForgetAudit: the aggregate reject_value row below is the
|
|
75
|
+
// trail for these removals, not N individual forget rows.
|
|
76
|
+
deleteEntryCore(db, row.id, { actor: opts.actor, suppressForgetAudit: true });
|
|
77
|
+
}
|
|
78
|
+
removedIds.push(row.id);
|
|
79
|
+
}
|
|
80
|
+
for (const row of merged) {
|
|
81
|
+
const successor = mergedSuccessor(row, holdsValue, new Set(removedIds));
|
|
82
|
+
deleteEntryCore(db, row.id, { actor: opts.actor, suppressForgetAudit: true });
|
|
83
|
+
removedIds.push(row.id);
|
|
84
|
+
if (!successor)
|
|
85
|
+
continue;
|
|
86
|
+
const kept = stampOriginProject(opts.hippoRoot, successor);
|
|
87
|
+
writeEntryDbOnly(db, kept, { actor: opts.actor });
|
|
88
|
+
successors.push(kept);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Dormant copies (src/dormant.ts), whole or inside a merged row, go too, in the same transaction: a
|
|
92
|
+
// rejected value may not linger where `hippo dormant restore` could
|
|
93
|
+
// bring it back. They have no markdown mirror, so the post-commit
|
|
94
|
+
// mirror purge below is a no-op for them; they join removedIds for the
|
|
95
|
+
// audit trail and the caller's report.
|
|
96
|
+
function removeDormantCopies(db, tenantId, digest, holdsValue, removal) {
|
|
97
|
+
const { removedIds, dormantSuccessorIds } = removal;
|
|
98
|
+
removedIds.push(...purgeDormantByDigest(db, tenantId, digest));
|
|
99
|
+
for (const dormant of listDormantSnapshots(db, tenantId)) {
|
|
100
|
+
const successor = mergedSuccessor(dormant.entry, holdsValue, new Set(removedIds));
|
|
101
|
+
if (successor === undefined)
|
|
102
|
+
continue;
|
|
103
|
+
removedIds.push(dormant.entry.id);
|
|
104
|
+
if (!successor) {
|
|
105
|
+
deleteDormantRow(db, tenantId, dormant.entry.id);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
replaceDormantEntry(db, tenantId, dormant.entry.id, successor);
|
|
109
|
+
dormantSuccessorIds.push(successor.id);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function auditRejectValue(db, opts, digest, removedIds) {
|
|
113
|
+
try {
|
|
114
|
+
appendAuditEvent(db, {
|
|
115
|
+
tenantId: opts.tenantId,
|
|
116
|
+
actor: opts.actor,
|
|
117
|
+
op: 'reject_value',
|
|
118
|
+
targetId: opts.memoryId,
|
|
119
|
+
metadata: { digest, removedIds, count: removedIds.length },
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
// Inside the open transaction: the reject commits without its trail row rather than rolling back over bookkeeping.
|
|
124
|
+
reportAuditWriteFailure('reject_value', String(error), opts.memoryId);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// Post-commit, db handle still open (same pattern as api.archiveRaw):
|
|
128
|
+
// best-effort mirror purge per removed id, reaper-backstop stamp for
|
|
129
|
+
// raw ids.
|
|
130
|
+
function purgeRemovedMirrors(db, hippoRoot, removal) {
|
|
131
|
+
for (const id of removal.removedIds) {
|
|
132
|
+
// purgeMirrorBestEffort retries once, then for non-raw ids (which the
|
|
133
|
+
// reaper never scans) reports the EXPLICIT leftover path(s). See its own doc comment (store.ts, near
|
|
134
|
+
// removeEntryMirrors) for the full rationale.
|
|
135
|
+
const mirrorOk = purgeMirrorBestEffort(hippoRoot, id, removal.removedRawIds.includes(id), 'hippo reject');
|
|
136
|
+
if (mirrorOk && removal.removedRawIds.includes(id)) {
|
|
137
|
+
db.prepare(`UPDATE raw_archive SET mirror_cleaned_at = ? WHERE memory_id = ?`).run(new Date().toISOString(), id);
|
|
71
138
|
}
|
|
139
|
+
}
|
|
140
|
+
for (const successor of removal.successors)
|
|
141
|
+
writeEntryMirrors(hippoRoot, successor);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* `hippo reject` / `api.reject` core flow. ONE connection, one transaction:
|
|
145
|
+
* insert the tombstone, enumerate + remove every live tenant row whose
|
|
146
|
+
* normalized digest matches (kind-aware), one aggregate `reject_value`
|
|
147
|
+
* audit, COMMIT. Then post-commit (mirrors the existing purge+reaper
|
|
148
|
+
* pattern verbatim from api.archiveRaw, api.ts:1913-1938): best-effort
|
|
149
|
+
* mirror purge per removed id, `mirror_cleaned_at` stamps for raw ids.
|
|
150
|
+
* index.json itself is only refreshed by `rebuildIndex()`.
|
|
151
|
+
*/
|
|
152
|
+
export function rejectValue(opts) {
|
|
153
|
+
assertRejectOpts(opts);
|
|
154
|
+
const db = openStore(opts.hippoRoot);
|
|
155
|
+
try {
|
|
156
|
+
const content = contentToReject(db, opts);
|
|
72
157
|
const digest = rejectionDigest(content);
|
|
73
158
|
const now = new Date().toISOString();
|
|
74
|
-
const
|
|
75
|
-
const removedRawIds = [];
|
|
76
|
-
const successors = [];
|
|
77
|
-
const dormantSuccessorIds = [];
|
|
159
|
+
const removal = { removedIds: [], removedRawIds: [], successors: [], dormantSuccessorIds: [] };
|
|
78
160
|
db.exec('BEGIN');
|
|
79
161
|
try {
|
|
80
162
|
insertRejectedValue(db, {
|
|
@@ -90,69 +172,9 @@ export function rejectValue(opts) {
|
|
|
90
172
|
// on ~1-5k-row stores — acceptable, documented. A digest column on
|
|
91
173
|
// memories is the escape if stores grow 100x; not needed now.
|
|
92
174
|
const holdsValue = (text) => rejectionDigest(text) === digest;
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (heldTexts(row).some(holdsValue))
|
|
97
|
-
merged.push(row);
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
if (row.kind === 'raw') {
|
|
101
|
-
// Append-only trigger respected — archiveRawMemory is the only
|
|
102
|
-
// legitimate removal path for kind='raw', and its inner SAVEPOINT
|
|
103
|
-
// composes safely inside this BEGIN/COMMIT.
|
|
104
|
-
archiveRawMemory(db, row.id, { reason: opts.reason, who: opts.actor });
|
|
105
|
-
removedRawIds.push(row.id);
|
|
106
|
-
}
|
|
107
|
-
else {
|
|
108
|
-
// suppressForgetAudit: the aggregate reject_value row below is the
|
|
109
|
-
// trail for these removals, not N individual forget rows (plan
|
|
110
|
-
// §4, round-3 advisory 2 — mirrors api.ts:1873-1877).
|
|
111
|
-
deleteEntryCore(db, row.id, { actor: opts.actor, suppressForgetAudit: true });
|
|
112
|
-
}
|
|
113
|
-
removedIds.push(row.id);
|
|
114
|
-
}
|
|
115
|
-
for (const row of merged) {
|
|
116
|
-
const successor = mergedSuccessor(row, holdsValue, new Set(removedIds));
|
|
117
|
-
deleteEntryCore(db, row.id, { actor: opts.actor, suppressForgetAudit: true });
|
|
118
|
-
removedIds.push(row.id);
|
|
119
|
-
if (!successor)
|
|
120
|
-
continue;
|
|
121
|
-
const kept = stampOriginProject(opts.hippoRoot, successor);
|
|
122
|
-
writeEntryDbOnly(db, kept, { actor: opts.actor });
|
|
123
|
-
successors.push(kept);
|
|
124
|
-
}
|
|
125
|
-
// Dormant copies (src/dormant.ts), whole or inside a merged row, go too, in the same transaction: a
|
|
126
|
-
// rejected value may not linger where `hippo dormant restore` could
|
|
127
|
-
// bring it back. They have no markdown mirror, so the post-commit
|
|
128
|
-
// mirror purge below is a no-op for them; they join removedIds for the
|
|
129
|
-
// audit trail and the caller's report.
|
|
130
|
-
removedIds.push(...purgeDormantByDigest(db, opts.tenantId, digest));
|
|
131
|
-
for (const dormant of listDormantSnapshots(db, opts.tenantId)) {
|
|
132
|
-
const successor = mergedSuccessor(dormant.entry, holdsValue, new Set(removedIds));
|
|
133
|
-
if (successor === undefined)
|
|
134
|
-
continue;
|
|
135
|
-
removedIds.push(dormant.entry.id);
|
|
136
|
-
if (!successor) {
|
|
137
|
-
deleteDormantRow(db, opts.tenantId, dormant.entry.id);
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
replaceDormantEntry(db, opts.tenantId, dormant.entry.id, successor);
|
|
141
|
-
dormantSuccessorIds.push(successor.id);
|
|
142
|
-
}
|
|
143
|
-
try {
|
|
144
|
-
appendAuditEvent(db, {
|
|
145
|
-
tenantId: opts.tenantId,
|
|
146
|
-
actor: opts.actor,
|
|
147
|
-
op: 'reject_value',
|
|
148
|
-
targetId: opts.memoryId,
|
|
149
|
-
metadata: { digest, removedIds, count: removedIds.length },
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
catch (error) {
|
|
153
|
-
// Inside the open transaction: the reject commits without its trail row rather than rolling back over bookkeeping.
|
|
154
|
-
reportAuditWriteFailure('reject_value', String(error), opts.memoryId);
|
|
155
|
-
}
|
|
175
|
+
removeLiveRows(db, opts, holdsValue, removal);
|
|
176
|
+
removeDormantCopies(db, opts.tenantId, digest, holdsValue, removal);
|
|
177
|
+
auditRejectValue(db, opts, digest, removal.removedIds);
|
|
156
178
|
db.exec('COMMIT');
|
|
157
179
|
}
|
|
158
180
|
catch (err) {
|
|
@@ -164,22 +186,8 @@ export function rejectValue(opts) {
|
|
|
164
186
|
}
|
|
165
187
|
throw err;
|
|
166
188
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
// raw ids.
|
|
170
|
-
for (const id of removedIds) {
|
|
171
|
-
// AT1 fix: purgeMirrorBestEffort retries once, then — for non-raw ids,
|
|
172
|
-
// which cleanupArchivedMirrors' reaper never scans — reports the
|
|
173
|
-
// EXPLICIT leftover path(s) instead of the false "will retry via
|
|
174
|
-
// reaper" claim. See its own doc comment (store.ts, near
|
|
175
|
-
// removeEntryMirrors) for the full rationale.
|
|
176
|
-
const mirrorOk = purgeMirrorBestEffort(opts.hippoRoot, id, removedRawIds.includes(id), 'hippo reject');
|
|
177
|
-
if (mirrorOk && removedRawIds.includes(id)) {
|
|
178
|
-
db.prepare(`UPDATE raw_archive SET mirror_cleaned_at = ? WHERE memory_id = ?`).run(new Date().toISOString(), id);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
for (const successor of successors)
|
|
182
|
-
writeEntryMirrors(opts.hippoRoot, successor);
|
|
189
|
+
purgeRemovedMirrors(db, opts.hippoRoot, removal);
|
|
190
|
+
const { removedIds, removedRawIds, successors, dormantSuccessorIds } = removal;
|
|
183
191
|
return { digest, content, removedIds, removedRawIds, successorIds: successors.map((s) => s.id), dormantSuccessorIds };
|
|
184
192
|
}
|
|
185
193
|
finally {
|
|
@@ -192,11 +200,8 @@ export function rejectValue(opts) {
|
|
|
192
200
|
* (plan §4): no per-write force flag.
|
|
193
201
|
*/
|
|
194
202
|
export function unrejectValue(hippoRoot, tenantId, digestOrPrefix, actor) {
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
// ambiguous-candidates branch and list the whole tombstone set instead of
|
|
198
|
-
// failing loud on the actually-invalid input. Reject before the DB round
|
|
199
|
-
// trip.
|
|
203
|
+
// An empty/blank prefix startsWith-matches EVERY digest and would list the whole
|
|
204
|
+
// tombstone set as ambiguous, so reject it before the DB round trip.
|
|
200
205
|
if (digestOrPrefix.trim().length === 0) {
|
|
201
206
|
return { status: 'not_found' };
|
|
202
207
|
}
|
package/dist/rejection.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* docs/plans/2026-08-15-at1-rejected-value-tombstone.md
|
|
2
|
+
* Rejected-value tombstone: core invariant.
|
|
4
3
|
*
|
|
5
4
|
* Exact-normalized-value semantics: a human who rejects a fact can refuse
|
|
6
5
|
* byte-stable re-ingestion of the same value across remember/capture/import/
|
|
@@ -64,7 +63,7 @@ export interface RejectedValueRow {
|
|
|
64
63
|
export declare function findRejectedValue(db: DatabaseSyncLike, tenantId: string, digest: string): RejectedValueRow | null;
|
|
65
64
|
/**
|
|
66
65
|
* Insert (or refresh) a tombstone row. Caller owns the transaction — used by
|
|
67
|
-
* the
|
|
66
|
+
* the `reject` verb and `resolveConflict`'s `rejectLoserValue` path.
|
|
68
67
|
*/
|
|
69
68
|
export declare function insertRejectedValue(db: DatabaseSyncLike, opts: {
|
|
70
69
|
tenantId: string;
|
|
@@ -76,11 +75,11 @@ export declare function insertRejectedValue(db: DatabaseSyncLike, opts: {
|
|
|
76
75
|
normalizedChars: number;
|
|
77
76
|
}): void;
|
|
78
77
|
/**
|
|
79
|
-
* Delete a tombstone by tenant + exact digest
|
|
80
|
-
* only
|
|
78
|
+
* Delete a tombstone by tenant + exact digest: the `unreject` verb, the
|
|
79
|
+
* only escape hatch.
|
|
81
80
|
*/
|
|
82
81
|
export declare function deleteRejectedValue(db: DatabaseSyncLike, tenantId: string, digest: string): boolean;
|
|
83
|
-
/** List tombstones for a tenant, newest first
|
|
82
|
+
/** List tombstones for a tenant, newest first: the `rejections` verb. */
|
|
84
83
|
export declare function listRejectedValues(db: DatabaseSyncLike, tenantId: string): RejectedValueRow[];
|
|
85
84
|
/**
|
|
86
85
|
* The write-path guard's check helper, called from `upsertEntryRow`
|
package/dist/rejection.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* docs/plans/2026-08-15-at1-rejected-value-tombstone.md
|
|
2
|
+
* Rejected-value tombstone: core invariant.
|
|
4
3
|
*
|
|
5
4
|
* Exact-normalized-value semantics: a human who rejects a fact can refuse
|
|
6
5
|
* byte-stable re-ingestion of the same value across remember/capture/import/
|
|
@@ -80,7 +79,7 @@ export function findRejectedValue(db, tenantId, digest) {
|
|
|
80
79
|
}
|
|
81
80
|
/**
|
|
82
81
|
* Insert (or refresh) a tombstone row. Caller owns the transaction — used by
|
|
83
|
-
* the
|
|
82
|
+
* the `reject` verb and `resolveConflict`'s `rejectLoserValue` path.
|
|
84
83
|
*/
|
|
85
84
|
export function insertRejectedValue(db, opts) {
|
|
86
85
|
db.prepare(`INSERT INTO rejected_values(tenant_id, digest, reason, rejected_by, rejected_at, source_memory_id, normalized_chars)
|
|
@@ -93,14 +92,14 @@ export function insertRejectedValue(db, opts) {
|
|
|
93
92
|
normalized_chars = excluded.normalized_chars`).run(opts.tenantId, opts.digest, opts.reason, opts.rejectedBy, opts.rejectedAt, opts.sourceMemoryId ?? null, opts.normalizedChars);
|
|
94
93
|
}
|
|
95
94
|
/**
|
|
96
|
-
* Delete a tombstone by tenant + exact digest
|
|
97
|
-
* only
|
|
95
|
+
* Delete a tombstone by tenant + exact digest: the `unreject` verb, the
|
|
96
|
+
* only escape hatch.
|
|
98
97
|
*/
|
|
99
98
|
export function deleteRejectedValue(db, tenantId, digest) {
|
|
100
99
|
const result = db.prepare(`DELETE FROM rejected_values WHERE tenant_id = ? AND digest = ?`).run(tenantId, digest);
|
|
101
100
|
return (result.changes ?? 0) > 0;
|
|
102
101
|
}
|
|
103
|
-
/** List tombstones for a tenant, newest first
|
|
102
|
+
/** List tombstones for a tenant, newest first: the `rejections` verb. */
|
|
104
103
|
export function listRejectedValues(db, tenantId) {
|
|
105
104
|
// SAFETY: rows' shape matches the columns named in the SELECT above.
|
|
106
105
|
const rows = db
|
|
@@ -134,15 +133,8 @@ export function checkRejectionGuard(db, tenantId, entryId, content) {
|
|
|
134
133
|
// id the caller is already writing, and the tombstone lookup above is the
|
|
135
134
|
// tenant-scoped decision. Matches deleteEntry's own by-id SELECT.
|
|
136
135
|
//
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
// re-persist" — content C sitting quietly (never rejected) in tenant A
|
|
140
|
-
// could be re-tagged into tenant B, and since C's digest already matched
|
|
141
|
-
// this row's stored digest, the guard exempted it even though B is the
|
|
142
|
-
// tenant that rejected C (that is WHY `tombstone` above is non-null: the
|
|
143
|
-
// lookup already ran under the INCOMING/destination tenantId). A tenant
|
|
144
|
-
// change on the SAME id is therefore always a content introduction into
|
|
145
|
-
// the destination tenant, exactly as if the row were new there.
|
|
136
|
+
// Also read tenant_id: a same-id upsert that only changes tenantId introduces the
|
|
137
|
+
// content into the destination tenant, exactly as if the row were new there.
|
|
146
138
|
// SAFETY: storedRow's shape matches the two columns named in the SELECT above.
|
|
147
139
|
const storedRow = db.prepare(`SELECT content, tenant_id FROM memories WHERE id = ?`).get(entryId);
|
|
148
140
|
const isNewRow = storedRow === undefined;
|
package/dist/rerankers/clef.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RerankerFn, RerankProvenance } from './types.js';
|
|
2
|
-
import {
|
|
2
|
+
import type { JsonValue } from '../json.js';
|
|
3
3
|
/** The two pretrained CLEF decision models served by Cloudflare Workers AI. */
|
|
4
4
|
export type ClefModel = 'clef-flash' | 'clef';
|
|
5
5
|
/** True when `name` is one of the CLEF reranker names. */
|
package/dist/rerankers/jev.d.ts
CHANGED
|
@@ -16,7 +16,6 @@ export declare function createJevReranker(localFallback: RerankerFn): RerankerFn
|
|
|
16
16
|
/** Orders `head` by `scores[i]`, keeping any upstream pre-rerank rank. Shared with CLEF. */
|
|
17
17
|
export declare function rankByScores(head: readonly SearchResult[], scores: readonly number[]): RerankResult[];
|
|
18
18
|
/** Track 4 reranker: hosted TypeSafe Jev, opt-in and paid (TYPESAFE_API_KEY), one batched call per recall.
|
|
19
|
-
* Any failure warns once and delegates to the local cross-encoder. Scores are not bit-stable run to run.
|
|
20
|
-
* Cost, env vars, evidence and limits: docs/evals/2026-09-19-jev-reranker.md. */
|
|
19
|
+
* Any failure warns once and delegates to the local cross-encoder. Scores are not bit-stable run to run. */
|
|
21
20
|
export declare const jevReranker: RerankerFn;
|
|
22
21
|
//# sourceMappingURL=jev.d.ts.map
|
package/dist/rerankers/jev.js
CHANGED
|
@@ -5,10 +5,10 @@ import { log } from '../log.js';
|
|
|
5
5
|
const ENDPOINT = 'https://api.typesafe.ai/v1/systemone';
|
|
6
6
|
const DEFAULT_TIMEOUT_MS = 5_000;
|
|
7
7
|
const TRUNCATE_CHARS = 1200;
|
|
8
|
-
// Pinned, not `jev-latest`:
|
|
9
|
-
//
|
|
8
|
+
// Pinned, not `jev-latest`: the eval numbers were measured on this version,
|
|
9
|
+
// and the alias moves whenever the vendor ships a release.
|
|
10
10
|
const DEFAULT_MODEL = 'jev-1.13.0';
|
|
11
|
-
// The pool size
|
|
11
|
+
// The pool size the eval numbers were measured at.
|
|
12
12
|
export const JEV_DEFAULT_TOP_K = 40;
|
|
13
13
|
function truncate(s, n) {
|
|
14
14
|
return s.length <= n ? s : `${s.slice(0, n)}...`;
|
|
@@ -124,7 +124,6 @@ export function rankByScores(head, scores) {
|
|
|
124
124
|
return scored;
|
|
125
125
|
}
|
|
126
126
|
/** Track 4 reranker: hosted TypeSafe Jev, opt-in and paid (TYPESAFE_API_KEY), one batched call per recall.
|
|
127
|
-
* Any failure warns once and delegates to the local cross-encoder. Scores are not bit-stable run to run.
|
|
128
|
-
* Cost, env vars, evidence and limits: docs/evals/2026-09-19-jev-reranker.md. */
|
|
127
|
+
* Any failure warns once and delegates to the local cross-encoder. Scores are not bit-stable run to run. */
|
|
129
128
|
export const jevReranker = createJevReranker(crossEncoderReranker);
|
|
130
129
|
//# sourceMappingURL=jev.js.map
|
package/dist/rerankers/llm.d.ts
CHANGED
|
@@ -4,8 +4,7 @@ import type { RerankerFn } from './types.js';
|
|
|
4
4
|
* OpenAI-compatible endpoint. Gated on HIPPO_LLM_RERANKER_URL to prevent
|
|
5
5
|
* accidental cost.
|
|
6
6
|
*
|
|
7
|
-
* Skeleton only
|
|
8
|
-
* Full characterisation deferred to a follow-on plan.
|
|
7
|
+
* Skeleton only; full characterisation is deferred.
|
|
9
8
|
*
|
|
10
9
|
* Timeout: defaults to 30s; overridable via HIPPO_LLM_RERANKER_TIMEOUT_MS.
|
|
11
10
|
* On timeout or any fetch failure, the reranker warns once per process and
|
package/dist/rerankers/llm.js
CHANGED
|
@@ -7,8 +7,7 @@ const DEFAULT_TIMEOUT_MS = 30_000;
|
|
|
7
7
|
* OpenAI-compatible endpoint. Gated on HIPPO_LLM_RERANKER_URL to prevent
|
|
8
8
|
* accidental cost.
|
|
9
9
|
*
|
|
10
|
-
* Skeleton only
|
|
11
|
-
* Full characterisation deferred to a follow-on plan.
|
|
10
|
+
* Skeleton only; full characterisation is deferred.
|
|
12
11
|
*
|
|
13
12
|
* Timeout: defaults to 30s; overridable via HIPPO_LLM_RERANKER_TIMEOUT_MS.
|
|
14
13
|
* On timeout or any fetch failure, the reranker warns once per process and
|
|
@@ -9,8 +9,7 @@ import type { SearchResult } from '../search/types.js';
|
|
|
9
9
|
* Rerankers MUST be deterministic for a given (query, results) input
|
|
10
10
|
* unless explicitly documented as stochastic (the LLM track, and the
|
|
11
11
|
* hosted jev reranker, whose scores move slightly run to run).
|
|
12
|
-
* Determinism is required for paired A/B and for the workload-validity
|
|
13
|
-
* gate in docs/evals/2026-05-10-f6-reranker-prereg.md.
|
|
12
|
+
* Determinism is required for paired A/B and for the workload-validity gate.
|
|
14
13
|
*
|
|
15
14
|
* @returns Reordered (and optionally rescaled) results.
|
|
16
15
|
*/
|