hippo-memory 1.24.1 → 1.26.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/bin/hippo.js +2 -2
- package/dist/api.d.ts +12 -17
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +46 -61
- package/dist/api.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +109 -15
- package/dist/cli.js.map +1 -1
- package/dist/compare.d.ts +78 -0
- package/dist/compare.d.ts.map +1 -0
- package/dist/compare.js +92 -0
- package/dist/compare.js.map +1 -0
- package/dist/connectors/github/backfill.js +4 -4
- package/dist/connectors/github/cli-impl.js +6 -6
- package/dist/connectors/github/dlq.js +14 -14
- package/dist/connectors/slack/backfill.js +1 -1
- package/dist/connectors/slack/dlq.js +10 -10
- package/dist/connectors/slack/workspaces.js +4 -4
- package/dist/dag.js +6 -6
- package/dist/dashboard.js +7 -7
- package/dist/embedding-provider.d.ts +7 -3
- package/dist/embedding-provider.d.ts.map +1 -1
- package/dist/embedding-provider.js +7 -3
- package/dist/embedding-provider.js.map +1 -1
- package/dist/embeddings.d.ts +58 -0
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/embeddings.js +66 -8
- package/dist/embeddings.js.map +1 -1
- package/dist/goals.d.ts.map +1 -1
- package/dist/goals.js +4 -0
- package/dist/goals.js.map +1 -1
- package/dist/graph-recall.d.ts.map +1 -1
- package/dist/graph-recall.js +15 -1
- package/dist/graph-recall.js.map +1 -1
- package/dist/graph.d.ts.map +1 -1
- package/dist/graph.js +3 -0
- package/dist/graph.js.map +1 -1
- package/dist/hooks.js +24 -24
- package/dist/multihop.d.ts.map +1 -1
- package/dist/multihop.js +3 -0
- package/dist/multihop.js.map +1 -1
- package/dist/physics.d.ts +6 -1
- package/dist/physics.d.ts.map +1 -1
- package/dist/physics.js +15 -5
- package/dist/physics.js.map +1 -1
- package/dist/predictions.js +67 -67
- package/dist/recall-scope.d.ts +60 -0
- package/dist/recall-scope.d.ts.map +1 -0
- package/dist/recall-scope.js +84 -0
- package/dist/recall-scope.js.map +1 -0
- package/dist/refine-llm.js +13 -13
- package/dist/rerankers/cross-encoder.d.ts.map +1 -1
- package/dist/rerankers/cross-encoder.js +6 -0
- package/dist/rerankers/cross-encoder.js.map +1 -1
- package/dist/search.d.ts +5 -0
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +30 -11
- package/dist/search.js.map +1 -1
- package/dist/shared.d.ts +30 -0
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +48 -11
- package/dist/shared.js.map +1 -1
- package/dist/sleep-redact.d.ts +3 -0
- package/dist/sleep-redact.d.ts.map +1 -1
- package/dist/sleep-redact.js +3 -0
- package/dist/sleep-redact.js.map +1 -1
- package/dist/src/api.js +46 -61
- package/dist/src/api.js.map +1 -1
- package/dist/src/cli.js +109 -15
- package/dist/src/cli.js.map +1 -1
- package/dist/src/compare.js +92 -0
- package/dist/src/compare.js.map +1 -0
- package/dist/src/connectors/github/backfill.js +4 -4
- package/dist/src/connectors/github/cli-impl.js +6 -6
- package/dist/src/connectors/github/dlq.js +14 -14
- package/dist/src/connectors/slack/backfill.js +1 -1
- package/dist/src/connectors/slack/dlq.js +10 -10
- package/dist/src/connectors/slack/workspaces.js +4 -4
- package/dist/src/dag.js +6 -6
- package/dist/src/dashboard.js +7 -7
- package/dist/src/embedding-provider.js +7 -3
- package/dist/src/embedding-provider.js.map +1 -1
- package/dist/src/embeddings.js +66 -8
- package/dist/src/embeddings.js.map +1 -1
- package/dist/src/goals.js +4 -0
- package/dist/src/goals.js.map +1 -1
- package/dist/src/graph-recall.js +15 -1
- package/dist/src/graph-recall.js.map +1 -1
- package/dist/src/graph.js +3 -0
- package/dist/src/graph.js.map +1 -1
- package/dist/src/hooks.js +24 -24
- package/dist/src/multihop.js +3 -0
- package/dist/src/multihop.js.map +1 -1
- package/dist/src/physics.js +15 -5
- package/dist/src/physics.js.map +1 -1
- package/dist/src/predictions.js +67 -67
- package/dist/src/recall-scope.js +84 -0
- package/dist/src/recall-scope.js.map +1 -0
- package/dist/src/refine-llm.js +13 -13
- package/dist/src/rerankers/cross-encoder.js +6 -0
- package/dist/src/rerankers/cross-encoder.js.map +1 -1
- package/dist/src/search.js +30 -11
- package/dist/src/search.js.map +1 -1
- package/dist/src/shared.js +48 -11
- package/dist/src/shared.js.map +1 -1
- package/dist/src/sleep-redact.js +3 -0
- package/dist/src/sleep-redact.js.map +1 -1
- package/dist/src/store.js +70 -22
- package/dist/src/store.js.map +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/working-memory.js +19 -19
- package/dist/store.d.ts +30 -9
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +70 -22
- package/dist/store.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/working-memory.js +19 -19
- package/dist-ui/index.html +12 -12
- package/extensions/openclaw-plugin/index.ts +650 -650
- 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/benchmarks/e1.3/scenarios.json +0 -2587
package/dist/src/cli.js
CHANGED
|
@@ -35,8 +35,9 @@ import { installJsonHooks, uninstallJsonHooks, resolveJsonHookPaths, detectInsta
|
|
|
35
35
|
import { createMemory, calculateStrength, calculateRewardFactor, deriveHalfLife, resolveConfidence, computeSchemaFit, Layer, } from './memory.js';
|
|
36
36
|
import { resolveProjectIdentity } from './project-identity.js';
|
|
37
37
|
import { detectSecret } from './secret-detect.js';
|
|
38
|
-
import { getHippoRoot, isInitialized, initStore, writeEntry, readEntry, deleteEntry, loadAllEntries, loadSearchEntries, loadIndex, saveIndex, loadStats, updateStats, saveActiveTaskSnapshot, loadActiveTaskSnapshot, clearActiveTaskSnapshot, appendSessionEvent, listSessionEvents, listMemoryConflicts, resolveConflict, saveSessionHandoff, loadLatestHandoff, loadHandoffById, RECALL_DEFAULT_DENY_SCOPES, } from './store.js';
|
|
39
|
-
import { markRetrieved, hybridSearch, physicsSearch, explainMatch, textOverlap } from './search.js';
|
|
38
|
+
import { getHippoRoot, isInitialized, initStore, writeEntry, readEntry, deleteEntry, loadAllEntries, loadSearchEntries, loadRecallSearchEntries, loadIndex, saveIndex, loadStats, updateStats, saveActiveTaskSnapshot, loadActiveTaskSnapshot, clearActiveTaskSnapshot, appendSessionEvent, listSessionEvents, listMemoryConflicts, resolveConflict, saveSessionHandoff, loadLatestHandoff, loadHandoffById, RECALL_DEFAULT_DENY_SCOPES, } from './store.js';
|
|
39
|
+
import { markRetrieved, hybridSearch, physicsSearch, explainMatch, textOverlap, tokenize as tokenizeQuery } from './search.js';
|
|
40
|
+
import { compareEntryIdentity } from './compare.js';
|
|
40
41
|
import { renderTraceContent, parseSteps } from './trace.js';
|
|
41
42
|
import { deduplicateStore } from './dedupe.js';
|
|
42
43
|
import { isEmbeddingAvailable, embedAll, embedMemory, loadEmbeddingIndex, resolveEmbeddingModel, embeddingModelRequiresReindex, } from './embeddings.js';
|
|
@@ -722,15 +723,43 @@ async function cmdRecall(hippoRoot, query, flags) {
|
|
|
722
723
|
// A5 stub auth: resolve the active tenant once and thread it through every
|
|
723
724
|
// recall-time SELECT against `memories`. Cross-tenant rows must never surface.
|
|
724
725
|
const tenantId = resolveTenantId({});
|
|
725
|
-
|
|
726
|
-
|
|
726
|
+
// v1.25.0 (v39 follow-up #1): the direct CLI recall path applies the recall
|
|
727
|
+
// scope rule. SQL half via loadRecallSearchEntries in 'additive' mode —
|
|
728
|
+
// default-deny (`unknown:legacy` quarantine) always, and an explicit
|
|
729
|
+
// `--scope X` UNLOCKS envelope scope X on top of the default-admitted set
|
|
730
|
+
// (the CLI flag is historically a tag-boost hint over scope-NULL rows, so
|
|
731
|
+
// api.recall's narrowing exact-match would empty every tag-scoped recall —
|
|
732
|
+
// see passesCliRecallScopeFilter in recall-scope.ts). The regex-only
|
|
733
|
+
// `<source>:private:*` half is the JS post-filter below. Hoisted here
|
|
734
|
+
// (it previously lived with the boost flags): recallExplicitScope is the
|
|
735
|
+
// FILTER input; recallActiveScope (which falls back to detectScope()) stays
|
|
736
|
+
// boost-only — auto-detection must never become a filter input or
|
|
737
|
+
// detected-project recalls would change shape.
|
|
738
|
+
const recallExplicitScope = flags['scope'] !== undefined ? String(flags['scope']).trim() : null;
|
|
739
|
+
const requestedScopeForFilter = recallExplicitScope || undefined;
|
|
740
|
+
let localEntries = loadRecallSearchEntries(hippoRoot, query, undefined, tenantId, requestedScopeForFilter, 'additive');
|
|
741
|
+
let globalEntries = isInitialized(globalRoot) ? loadRecallSearchEntries(globalRoot, query, undefined, tenantId, requestedScopeForFilter, 'additive') : [];
|
|
727
742
|
// v1.12.13 / C5 — WYSIATI counters. Track filter activity per the plan v3
|
|
728
743
|
// Task 3 mapping table. dropped_pre_rank is the SUM of all non-budget
|
|
729
744
|
// filter drops (pre-rank AND post-rank). Search-engine internal drops
|
|
730
745
|
// (scored-to-zero rows that hybridSearch/physicsSearch returns fewer of)
|
|
731
746
|
// are NOT counted in v1 — they are part of the rank step, not a filter.
|
|
747
|
+
// totalCandidates = post-SQL-predicate count (api.recall parity: measured
|
|
748
|
+
// after loadRecallSearchEntries, before the JS scope filter). NOTE the
|
|
749
|
+
// v1.12.13 accounting convention: SQL-excluded rows (quarantine + the
|
|
750
|
+
// v1.25.0 pre-window ':private:' exclusion) are pre-candidate and are NOT
|
|
751
|
+
// counted as drops; the JS half below normally drops 0 and exists as
|
|
752
|
+
// defense-in-depth (LIKE/regex divergence, exact-mode mismatch).
|
|
732
753
|
const totalCandidatesCountCmd = localEntries.length + globalEntries.length;
|
|
733
754
|
let droppedPreRankCountCmd = 0;
|
|
755
|
+
// v1.25.0: JS half of the recall scope rule (private-scope regex deny with
|
|
756
|
+
// explicit-request unlock), via the canonical helper — do not inline a
|
|
757
|
+
// fourth copy of this predicate.
|
|
758
|
+
const passesRecallScope = (e) => api.passesCliRecallScopeFilter(e.scope ?? null, requestedScopeForFilter);
|
|
759
|
+
const beforeScopeFilterCmd = localEntries.length + globalEntries.length;
|
|
760
|
+
localEntries = localEntries.filter(passesRecallScope);
|
|
761
|
+
globalEntries = globalEntries.filter(passesRecallScope);
|
|
762
|
+
droppedPreRankCountCmd += beforeScopeFilterCmd - (localEntries.length + globalEntries.length);
|
|
734
763
|
// Bi-temporal filtering for physics path (hybridSearch handles it internally)
|
|
735
764
|
if (asOf) {
|
|
736
765
|
const filterAsOf = (entries) => {
|
|
@@ -782,7 +811,8 @@ async function cmdRecall(hippoRoot, query, flags) {
|
|
|
782
811
|
const minResults = flags['min-results'] !== undefined
|
|
783
812
|
? parseInt(String(flags['min-results']), 10)
|
|
784
813
|
: undefined;
|
|
785
|
-
|
|
814
|
+
// recallExplicitScope hoisted above the candidate loads (v1.25.0) — see the
|
|
815
|
+
// scope-filter block near the top of cmdRecall.
|
|
786
816
|
const recallActiveScope = recallExplicitScope || detectScope();
|
|
787
817
|
const useMultihop = flags['multihop'] === true || config.multihop.enabled;
|
|
788
818
|
// L1 — graph-retrieval stream. `--graph-stream` forces rrf fusion + the graph stream
|
|
@@ -859,10 +889,16 @@ async function cmdRecall(hippoRoot, query, flags) {
|
|
|
859
889
|
});
|
|
860
890
|
}
|
|
861
891
|
else if (hasGlobal) {
|
|
862
|
-
// Use searchBothHybrid for merged results with embedding support
|
|
892
|
+
// Use searchBothHybrid for merged results with embedding support.
|
|
893
|
+
// recallScope (v1.25.0): searchBothHybrid re-loads candidates internally,
|
|
894
|
+
// so the scope rule must be plumbed in — the filtered localEntries /
|
|
895
|
+
// globalEntries above are NOT what this path ranks.
|
|
863
896
|
results = await searchBothHybrid(query, hippoRoot, globalRoot, {
|
|
864
897
|
budget, mmr: mmrEnabled, mmrLambda, localBump, minResults, scope: recallActiveScope, tenantId,
|
|
865
898
|
includeSuperseded, asOf,
|
|
899
|
+
recallScope: recallExplicitScope
|
|
900
|
+
? { requested: recallExplicitScope, additive: true }
|
|
901
|
+
: {},
|
|
866
902
|
});
|
|
867
903
|
}
|
|
868
904
|
else {
|
|
@@ -940,15 +976,39 @@ async function cmdRecall(hippoRoot, query, flags) {
|
|
|
940
976
|
const tail = results.slice(poolSize);
|
|
941
977
|
const maxScore = pool.reduce((m, r) => Math.max(m, r.score), 0);
|
|
942
978
|
const scoreFloor = maxScore * 0.5;
|
|
979
|
+
// On-topic test: score floor OR query coverage. The score floor alone
|
|
980
|
+
// proxied topicality via ranking score, but the disambiguating update
|
|
981
|
+
// this mechanic exists to surface is BY NATURE phrased differently
|
|
982
|
+
// (weaker lexical/embedding overlap), so under the #t2 embed-text
|
|
983
|
+
// format (docs/plans/2026-07-09-recall-determinism.md T1, which
|
|
984
|
+
// de-compressed similarity gaps) it fell below any sane floor
|
|
985
|
+
// (measured 0.33x max on the acc-evc micro fixture). Query coverage —
|
|
986
|
+
// the fraction of query tokens present in the candidate — is the
|
|
987
|
+
// mechanic's own definition of "same topic, different fact" applied
|
|
988
|
+
// to the query, and is score-scale-independent. Principled EVC
|
|
989
|
+
// calibration remains roadmapped as B1 depth.
|
|
990
|
+
const queryTokens = new Set(tokenizeQuery(query));
|
|
943
991
|
const onTopic = [];
|
|
944
992
|
const offTopic = [];
|
|
945
993
|
for (const r of pool) {
|
|
946
|
-
|
|
994
|
+
let hits = 0;
|
|
995
|
+
if (queryTokens.size > 0) {
|
|
996
|
+
const candTokens = new Set(tokenizeQuery(r.entry.content));
|
|
997
|
+
for (const t of queryTokens)
|
|
998
|
+
if (candTokens.has(t))
|
|
999
|
+
hits++;
|
|
1000
|
+
}
|
|
1001
|
+
const queryCoverage = queryTokens.size > 0 ? hits / queryTokens.size : 0;
|
|
1002
|
+
(r.score >= scoreFloor || queryCoverage >= 0.6 ? onTopic : offTopic).push(r);
|
|
947
1003
|
}
|
|
1004
|
+
// Recency is the true primary key (unchanged) — that's the whole
|
|
1005
|
+
// point of --evc-adaptive. compareEntryIdentity is only a TAIL for
|
|
1006
|
+
// entries created at the exact same timestamp, which previously fell
|
|
1007
|
+
// to array/scan order (T2, deterministic tie keys).
|
|
948
1008
|
onTopic.sort((a, b) => {
|
|
949
1009
|
const ta = new Date(a.entry.created).getTime();
|
|
950
1010
|
const tb = new Date(b.entry.created).getTime();
|
|
951
|
-
return tb - ta;
|
|
1011
|
+
return tb !== ta ? tb - ta : compareEntryIdentity(a.entry, b.entry);
|
|
952
1012
|
});
|
|
953
1013
|
results = [...onTopic, ...offTopic, ...tail];
|
|
954
1014
|
}
|
|
@@ -985,6 +1045,11 @@ async function cmdRecall(hippoRoot, query, flags) {
|
|
|
985
1045
|
}
|
|
986
1046
|
return next;
|
|
987
1047
|
});
|
|
1048
|
+
// T2 note: PLAIN stable score sort on purpose (here and in the rerank
|
|
1049
|
+
// blocks below) -- these are RE-SORTS of an already deterministically-
|
|
1050
|
+
// ordered ranking, so sort stability inherits the upstream content-tail
|
|
1051
|
+
// determinism, and ties preserve the prior rank rather than reordering
|
|
1052
|
+
// by content (a no-signal boost must not shuffle its input).
|
|
988
1053
|
results.sort((a, b) => b.score - a.score);
|
|
989
1054
|
}
|
|
990
1055
|
// vmPFC continuous value attribution (RESEARCH.md §PFC.vmPFC). Continuous
|
|
@@ -1013,7 +1078,7 @@ async function cmdRecall(hippoRoot, query, flags) {
|
|
|
1013
1078
|
}
|
|
1014
1079
|
return next;
|
|
1015
1080
|
});
|
|
1016
|
-
results.sort((a, b) => b.score - a.score);
|
|
1081
|
+
results.sort((a, b) => b.score - a.score); // T2: plain stable re-sort, see --filter-conflicts note
|
|
1017
1082
|
}
|
|
1018
1083
|
// OFC option-value re-ranker MVP (RESEARCH.md §PFC.OFC). Combine relevance,
|
|
1019
1084
|
// strength, and integration cost into a single utility score and re-sort.
|
|
@@ -1047,7 +1112,7 @@ async function cmdRecall(hippoRoot, query, flags) {
|
|
|
1047
1112
|
}
|
|
1048
1113
|
return next;
|
|
1049
1114
|
})
|
|
1050
|
-
.sort((a, b) => b.score - a.score);
|
|
1115
|
+
.sort((a, b) => b.score - a.score); // T2: plain stable re-sort, see --filter-conflicts note
|
|
1051
1116
|
}
|
|
1052
1117
|
// F6 reranker pass (docs/plans/2026-05-10-f6-reranker-hardening.md). When
|
|
1053
1118
|
// --reranker <name> is set, look up the reranker fn from the registry
|
|
@@ -1120,7 +1185,7 @@ async function cmdRecall(hippoRoot, query, flags) {
|
|
|
1120
1185
|
}
|
|
1121
1186
|
return boosted;
|
|
1122
1187
|
})
|
|
1123
|
-
.sort((a, b) => b.score - a.score);
|
|
1188
|
+
.sort((a, b) => b.score - a.score); // T2: plain stable re-sort, see --filter-conflicts note
|
|
1124
1189
|
}
|
|
1125
1190
|
// dlPFC depth (B3, v0.38; lifted v1.7.4 into applyGoalStackBoost). When
|
|
1126
1191
|
// HIPPO_SESSION_ID is set (env or --session-id flag) and the
|
|
@@ -1199,7 +1264,7 @@ async function cmdRecall(hippoRoot, query, flags) {
|
|
|
1199
1264
|
}
|
|
1200
1265
|
return next;
|
|
1201
1266
|
})
|
|
1202
|
-
.sort((a, b) => b.score - a.score);
|
|
1267
|
+
.sort((a, b) => b.score - a.score); // T2: plain stable re-sort, see --filter-conflicts note
|
|
1203
1268
|
}
|
|
1204
1269
|
// --outcome filter: drop trace entries whose trace_outcome !== target.
|
|
1205
1270
|
// Non-trace entries pass through unaffected (traces are the only layer with
|
|
@@ -1695,8 +1760,30 @@ async function cmdExplain(hippoRoot, query, flags) {
|
|
|
1695
1760
|
const globalRoot = getGlobalRoot();
|
|
1696
1761
|
// A5: scope explain results to the active tenant.
|
|
1697
1762
|
const tenantId = resolveTenantId({});
|
|
1698
|
-
|
|
1699
|
-
|
|
1763
|
+
// v1.25.0 (v39 follow-up #1): cmdExplain shares cmdRecall's leak class —
|
|
1764
|
+
// same unscoped loads, same fix, same semantics (explain explains what
|
|
1765
|
+
// recall would see). Flag hoisted above the loads; the note below keeps the
|
|
1766
|
+
// command honest for an operator debugging a hidden row.
|
|
1767
|
+
const explainExplicitScope = flags['scope'] !== undefined ? String(flags['scope']).trim() : null;
|
|
1768
|
+
const explainRequestedScope = explainExplicitScope || undefined;
|
|
1769
|
+
let explainLocalEntries = loadRecallSearchEntries(hippoRoot, query, undefined, tenantId, explainRequestedScope, 'additive');
|
|
1770
|
+
let explainGlobalEntries = isInitialized(globalRoot) ? loadRecallSearchEntries(globalRoot, query, undefined, tenantId, explainRequestedScope, 'additive') : [];
|
|
1771
|
+
const passesExplainScope = (e) => api.passesCliRecallScopeFilter(e.scope ?? null, explainRequestedScope);
|
|
1772
|
+
explainLocalEntries = explainLocalEntries.filter(passesExplainScope);
|
|
1773
|
+
explainGlobalEntries = explainGlobalEntries.filter(passesExplainScope);
|
|
1774
|
+
// Honesty note (grill finding #2): the SQL predicate excludes denied rows
|
|
1775
|
+
// BEFORE the candidate window (codex P2 fix), so the pipeline never sees
|
|
1776
|
+
// them. For the diagnostic note only, probe the unscoped window and count
|
|
1777
|
+
// what the scope policy hides — window-capped, so the count is a floor on
|
|
1778
|
+
// large stores, which is fine for a "why is my row missing" hint.
|
|
1779
|
+
const explainUnscopedProbe = [
|
|
1780
|
+
...loadSearchEntries(hippoRoot, query, undefined, tenantId),
|
|
1781
|
+
...(isInitialized(globalRoot) ? loadSearchEntries(globalRoot, query, undefined, tenantId) : []),
|
|
1782
|
+
];
|
|
1783
|
+
const explainScopeDropped = explainUnscopedProbe.filter((e) => !passesExplainScope(e)).length;
|
|
1784
|
+
if (explainScopeDropped > 0) {
|
|
1785
|
+
console.error(`[note] ${explainScopeDropped} candidate${explainScopeDropped === 1 ? '' : 's'} hidden by recall scope policy (pass an explicit --scope to inspect).`);
|
|
1786
|
+
}
|
|
1700
1787
|
// Bi-temporal filtering
|
|
1701
1788
|
if (explainAsOf) {
|
|
1702
1789
|
const filterAsOfExplain = (entries) => {
|
|
@@ -1739,7 +1826,7 @@ async function cmdExplain(hippoRoot, query, flags) {
|
|
|
1739
1826
|
: flags['local-bump'] !== undefined
|
|
1740
1827
|
? parseFloat(String(flags['local-bump']))
|
|
1741
1828
|
: config.search.localBump;
|
|
1742
|
-
|
|
1829
|
+
// explainExplicitScope hoisted above the candidate loads (v1.25.0).
|
|
1743
1830
|
const explainActiveScope = explainExplicitScope || detectScope();
|
|
1744
1831
|
let results;
|
|
1745
1832
|
let modeUsed;
|
|
@@ -1757,6 +1844,9 @@ async function cmdExplain(hippoRoot, query, flags) {
|
|
|
1757
1844
|
results = await searchBothHybrid(query, hippoRoot, globalRoot, {
|
|
1758
1845
|
budget, explain: true, mmr: mmrEnabled, mmrLambda, localBump, scope: explainActiveScope,
|
|
1759
1846
|
includeSuperseded: explainIncludeSuperseded, asOf: explainAsOf, tenantId,
|
|
1847
|
+
recallScope: explainExplicitScope
|
|
1848
|
+
? { requested: explainExplicitScope, additive: true }
|
|
1849
|
+
: {},
|
|
1760
1850
|
});
|
|
1761
1851
|
modeUsed = 'searchBothHybrid';
|
|
1762
1852
|
}
|
|
@@ -2429,6 +2519,10 @@ export function renderSleepResult(result) {
|
|
|
2429
2519
|
if (result.shared !== undefined && result.shared > 0) {
|
|
2430
2520
|
console.log(`\nAuto-shared ${result.shared} high-value memories to global store.`);
|
|
2431
2521
|
}
|
|
2522
|
+
if (result.secretSkipped !== undefined && result.secretSkipped > 0) {
|
|
2523
|
+
// v1.25.0 (v39 follow-up #2): the secret veto is no longer silent.
|
|
2524
|
+
console.log(`\nAuto-share: withheld ${result.secretSkipped} secret-flagged ${result.secretSkipped === 1 ? 'memory' : 'memories'} (secret veto).`);
|
|
2525
|
+
}
|
|
2432
2526
|
if (result.ambient) {
|
|
2433
2527
|
console.log(`\n${renderAmbientSummary(result.ambient)}`);
|
|
2434
2528
|
}
|