hippo-memory 1.61.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 +37 -53
- package/dist/agent-memories/apply.d.ts +3 -1
- package/dist/agent-memories/apply.js +23 -1
- package/dist/agent-memories/claude-code.d.ts +4 -1
- package/dist/agent-memories/claude-code.js +53 -9
- package/dist/agent-memories/report.d.ts +1 -0
- package/dist/agent-memories/report.js +2 -2
- package/dist/agent-memories/sync.d.ts +3 -3
- package/dist/agent-memories/sync.js +46 -24
- package/dist/agent-memories/types.d.ts +0 -2
- 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 +62 -66
- 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 +50 -0
- package/dist/api/context-select.js +342 -0
- package/dist/api/context-types.d.ts +12 -17
- package/dist/api/context.d.ts +5 -4
- package/dist/api/context.js +208 -535
- package/dist/api/dormant.js +1 -1
- package/dist/api/drill-down.d.ts +4 -4
- package/dist/api/drill-down.js +56 -41
- 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/promote.js +55 -66
- 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 +310 -456
- package/dist/api/remember.d.ts +2 -2
- package/dist/api/sleep.d.ts +7 -35
- package/dist/api/sleep.js +206 -220
- 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 +2 -2
- package/dist/capture/compact.js +11 -15
- 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/briefs.js +324 -306
- package/dist/cli/context.js +44 -34
- package/dist/cli/continuity.js +283 -271
- package/dist/cli/curate.d.ts +1 -1
- package/dist/cli/curate.js +43 -58
- package/dist/cli/dag.js +5 -9
- package/dist/cli/decisions.js +334 -345
- package/dist/cli/explain.js +68 -61
- package/dist/cli/goals.js +1 -1
- package/dist/cli/init.js +1 -1
- package/dist/cli/maintenance.js +62 -51
- package/dist/cli/playbooks.js +391 -379
- package/dist/cli/projects.js +11 -6
- package/dist/cli/recall.js +30 -44
- package/dist/cli/remember.js +118 -87
- package/dist/cli/session-hooks.js +106 -115
- package/dist/cli/setup.d.ts +1 -1
- package/dist/cli/setup.js +267 -250
- 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 +80 -76
- package/dist/cli/transfer.js +88 -107
- 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 -2
- 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 +82 -76
- package/dist/dag.d.ts +10 -21
- package/dist/dag.js +189 -203
- 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 +97 -80
- package/dist/dedupe.js +86 -61
- package/dist/delivery-recorder.js +154 -135
- package/dist/doctor.js +129 -110
- 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 +103 -91
- 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 +46 -39
- 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 +96 -81
- 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 +88 -72
- package/dist/project-briefs.d.ts +2 -3
- package/dist/project-briefs.js +141 -118
- package/dist/project-identity.d.ts +22 -9
- package/dist/project-identity.js +47 -12
- package/dist/project-merge.d.ts +28 -5
- package/dist/project-merge.js +213 -46
- 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 +26 -17
- package/dist/shared.js +102 -104
- package/dist/skills.d.ts +3 -3
- package/dist/skills.js +88 -72
- 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 +34 -56
- 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/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
|
*/
|
package/dist/rrf.d.ts
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
* Fuses N ranked candidate lists into a single ordering by summing
|
|
5
5
|
* weighted 1/(k + rank) contributions per candidate. The constant K is
|
|
6
6
|
* the canonical 60 from the original paper and the value already in use
|
|
7
|
-
* across hippo's `hybridSearch
|
|
7
|
+
* across hippo's `hybridSearch`. Do NOT tune K without an
|
|
8
8
|
* explicit cross-corpus eval — it is calibrated against IR benchmarks
|
|
9
9
|
* and works robustly across BM25/dense/cross-encoder rank-list shapes.
|
|
10
10
|
*
|
|
11
11
|
* Generic over the candidate id type so this helper can be shared by
|
|
12
12
|
* `src/search.ts::hybridSearch` (T = number, idx into MemoryEntry[]) and
|
|
13
|
-
* the LongMemEval
|
|
13
|
+
* the LongMemEval hybrid retrieve benchmark (T = string, session_id).
|
|
14
14
|
*
|
|
15
15
|
* Behaviour MUST stay byte-identical to the inline implementation that
|
|
16
16
|
* lived in `src/search.ts:354-374` before extraction (commit ab6c5eb).
|
package/dist/rrf.js
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
* Fuses N ranked candidate lists into a single ordering by summing
|
|
5
5
|
* weighted 1/(k + rank) contributions per candidate. The constant K is
|
|
6
6
|
* the canonical 60 from the original paper and the value already in use
|
|
7
|
-
* across hippo's `hybridSearch
|
|
7
|
+
* across hippo's `hybridSearch`. Do NOT tune K without an
|
|
8
8
|
* explicit cross-corpus eval — it is calibrated against IR benchmarks
|
|
9
9
|
* and works robustly across BM25/dense/cross-encoder rank-list shapes.
|
|
10
10
|
*
|
|
11
11
|
* Generic over the candidate id type so this helper can be shared by
|
|
12
12
|
* `src/search.ts::hybridSearch` (T = number, idx into MemoryEntry[]) and
|
|
13
|
-
* the LongMemEval
|
|
13
|
+
* the LongMemEval hybrid retrieve benchmark (T = string, session_id).
|
|
14
14
|
*
|
|
15
15
|
* Behaviour MUST stay byte-identical to the inline implementation that
|
|
16
16
|
* lived in `src/search.ts:354-374` before extraction (commit ab6c5eb).
|
|
@@ -9,6 +9,7 @@ import { currentEntries } from './as-of.js';
|
|
|
9
9
|
import { applyRankBoosts, strengthRecencyMultipliers, NO_SUMMARY_SCORING } from './boosts.js';
|
|
10
10
|
import { temporalContext } from './temporal.js';
|
|
11
11
|
import { dedupeExtracted, fitBudget, withDagChildren } from './finalize.js';
|
|
12
|
+
import { DEFAULT_RECALL_BUDGET } from './types.js';
|
|
12
13
|
/** Synchronous BM25 search: relevance * strength * recency and the rank boosts, without embeddings or the outcome nudge. */
|
|
13
14
|
export function search(query, entries, options = {}) {
|
|
14
15
|
const now = options.now ?? evalNow(); // honors HIPPO_FAKE_NOW (eval-only; see ablation.ts)
|
|
@@ -38,6 +39,6 @@ export function search(query, entries, options = {}) {
|
|
|
38
39
|
scored.push({ entry: pool[i], score, bm25, cosine: 0, tokens: estimateTokens(pool[i].content) });
|
|
39
40
|
}
|
|
40
41
|
scored.sort(compareScoredResults);
|
|
41
|
-
return fitBudget(withDagChildren(dedupeExtracted(scored), pool), options.budget ??
|
|
42
|
+
return fitBudget(withDagChildren(dedupeExtracted(scored), pool), options.budget ?? DEFAULT_RECALL_BUDGET, options.minResults ?? 1, options.cost);
|
|
42
43
|
}
|
|
43
44
|
//# sourceMappingURL=bm25-search.js.map
|
package/dist/search/boosts.js
CHANGED
|
@@ -5,6 +5,7 @@ import { pathBoostMultiplier } from '../path-context.js';
|
|
|
5
5
|
import { scopeMatch } from '../scope.js';
|
|
6
6
|
import { temporalBoost } from './temporal.js';
|
|
7
7
|
export const CHURN_STALE_RANK_MULTIPLIER = 0.5; // SHORTCUT: untuned; measure on real recall before any default.
|
|
8
|
+
const DECISION_TAG_BOOST = 1.2;
|
|
8
9
|
const DEFAULT_SUMMARY_DEBOOST = 0.85;
|
|
9
10
|
const DEFAULT_FRESHNESS_BOOST = 1.05;
|
|
10
11
|
const FRESHNESS_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
|
|
@@ -69,7 +70,7 @@ export function strengthRecencyMultipliers(entry, now) {
|
|
|
69
70
|
}
|
|
70
71
|
/** Applies every rank multiplier one at a time, in a fixed order, so float rounding matches across paths. */
|
|
71
72
|
export function applyRankBoosts(score, entry, ctx) {
|
|
72
|
-
const decisionBoost = entry.tags.includes('decision') ?
|
|
73
|
+
const decisionBoost = entry.tags.includes('decision') ? DECISION_TAG_BOOST : 1.0;
|
|
73
74
|
const churnStaleMultiplier = churnStaleFactor(entry);
|
|
74
75
|
const pathBoost = pathBoostMultiplier(entry.tags, ctx.pathTags);
|
|
75
76
|
const outcomeBoost = ctx.outcome ? outcomeMultiplier(entry) : 1.0;
|
package/dist/search/hybrid.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { RerankerFn, RerankerOptions } from '../rerankers/types.js';
|
|
|
3
3
|
import { type BM25Corpus } from './bm25.js';
|
|
4
4
|
import { type HybridVectorCandidates } from './vector.js';
|
|
5
5
|
import { type GraphStreamOptions } from './fusion.js';
|
|
6
|
-
import type
|
|
6
|
+
import { type ResultCost, type SearchResult } from './types.js';
|
|
7
7
|
export interface HybridSearchOptions {
|
|
8
8
|
budget?: number;
|
|
9
9
|
now?: Date;
|
package/dist/search/hybrid.js
CHANGED
|
@@ -12,6 +12,7 @@ import { fuseRanks } from './fusion.js';
|
|
|
12
12
|
import { scoreHybridPool } from './hybrid-score.js';
|
|
13
13
|
import { applyMmrWindow, applyReranker } from './rerank.js';
|
|
14
14
|
import { dedupeExtracted, fitBudget, withDagChildren } from './finalize.js';
|
|
15
|
+
import { DEFAULT_RECALL_BUDGET } from './types.js';
|
|
15
16
|
/** BM25 blended with cosine similarity when stored vectors and a provider are available, BM25 * strength * recency otherwise. */
|
|
16
17
|
export async function hybridSearch(query, entries, options = {}) {
|
|
17
18
|
const now = options.now ?? evalNow(); // honors HIPPO_FAKE_NOW (eval-only; see ablation.ts)
|
|
@@ -42,7 +43,7 @@ export async function hybridSearch(query, entries, options = {}) {
|
|
|
42
43
|
});
|
|
43
44
|
scored.sort(compareScoredResults);
|
|
44
45
|
const ordered = await orderHybrid(query, withDagChildren(dedupeExtracted(scored), arm.entries), arm, options);
|
|
45
|
-
return fitBudget(ordered, options.budget ??
|
|
46
|
+
return fitBudget(ordered, options.budget ?? DEFAULT_RECALL_BUDGET, options.minResults ?? 1, options.cost);
|
|
46
47
|
}
|
|
47
48
|
function hybridBoostContext(query, pool, now, options) {
|
|
48
49
|
return {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type MemoryEntry } from '../memory.js';
|
|
2
2
|
import { type PhysicsConfig } from '../physics-config.js';
|
|
3
3
|
import { type HybridVectorCandidates } from './vector.js';
|
|
4
|
-
import type
|
|
4
|
+
import { type ResultCost, type SearchResult } from './types.js';
|
|
5
5
|
export interface PhysicsSearchOptions {
|
|
6
6
|
budget?: number;
|
|
7
7
|
now?: Date;
|
|
@@ -15,6 +15,7 @@ import { fitBudget } from './finalize.js';
|
|
|
15
15
|
import { hybridSearch } from './hybrid.js';
|
|
16
16
|
import { currentEntries } from './as-of.js';
|
|
17
17
|
import { vectorCandidatesOutside } from './vector.js';
|
|
18
|
+
import { DEFAULT_RECALL_BUDGET } from './types.js';
|
|
18
19
|
/** Scores memories by gravity, momentum and cluster amplification; rows without physics state rank through hybridSearch. */
|
|
19
20
|
export async function physicsSearch(query, entries, options = {}) {
|
|
20
21
|
const now = options.now ?? evalNow(); // honors HIPPO_FAKE_NOW (eval-only; see ablation.ts)
|
|
@@ -41,7 +42,7 @@ export async function physicsSearch(query, entries, options = {}) {
|
|
|
41
42
|
: [];
|
|
42
43
|
const merged = mergeScorePools(physicsResults, classicResults);
|
|
43
44
|
merged.sort(compareScoredResults);
|
|
44
|
-
return fitBudget(merged, options.budget ??
|
|
45
|
+
return fitBudget(merged, options.budget ?? DEFAULT_RECALL_BUDGET, options.minResults ?? 1, options.cost);
|
|
45
46
|
}
|
|
46
47
|
/** The caller's vector, else the provider's; null sends the caller to hybridSearch. */
|
|
47
48
|
async function physicsQueryVector(query, root, given) {
|
package/dist/search/types.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { MemoryEntry } from '../memory.js';
|
|
2
|
+
export declare const DEFAULT_RECALL_BUDGET = 4000;
|
|
3
|
+
export declare const DEFAULT_LOCAL_BUMP = 1.2;
|
|
2
4
|
/** One score mutation applied after candidate generation; allocated only under `recall --why` or `RecallOpts.explain`. */
|
|
3
5
|
export interface RerankStep {
|
|
4
6
|
/** One of: interference, value, utility, reranker, goal-boost, retrieval-count-downweight. */
|
package/dist/search/types.js
CHANGED
package/dist/secret-detect.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Secret detection for memory content (
|
|
3
|
-
* docs/plans/2026-07-01-memory-scope-isolation.md).
|
|
2
|
+
* Secret detection for memory content (memory scope isolation).
|
|
4
3
|
*
|
|
5
4
|
* Conservative, provider-bounded patterns only - a bare `sk-` in prose must
|
|
6
5
|
* NOT flag. Detection gates two surfaces:
|
|
@@ -11,7 +10,7 @@
|
|
|
11
10
|
* project origin. Explicit recall is unaffected - recalling a secret is
|
|
12
11
|
* a deliberate act.
|
|
13
12
|
*
|
|
14
|
-
* This is deliberately a thin slice of
|
|
13
|
+
* This is deliberately a thin slice of lifecycle compliance
|
|
15
14
|
* (no PII detection). Write-time scrubbing covers only text no person
|
|
16
15
|
* typed into hippo; see `vetSecrets`.
|
|
17
16
|
*
|
|
@@ -37,8 +36,8 @@ export declare function detectSecret(entry: {
|
|
|
37
36
|
* Replace secret-shaped substrings in free text with a redaction marker.
|
|
38
37
|
* Reuses the same `SECRET_PATTERNS` / co-occurrence guard as `detectSecret`
|
|
39
38
|
* (which only flags whole-entry content) so callers that must persist raw
|
|
40
|
-
* text that never passes through the normal capture content gate
|
|
41
|
-
*
|
|
39
|
+
* text that never passes through the normal capture content gate (e.g. the
|
|
40
|
+
* pre-compact snapshot fields) can scrub it in place instead.
|
|
42
41
|
*/
|
|
43
42
|
export declare function redactSecrets(text: string): string;
|
|
44
43
|
/** Memories never hold a raw email address (AGENTS.md); phone numbers are left alone, as their patterns misfire on ids. */
|