hippo-memory 1.52.8 → 1.53.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 +185 -101
- package/dist/agent-memories/apply.d.ts +47 -0
- package/dist/agent-memories/apply.js +253 -0
- package/dist/agent-memories/claude-code.d.ts +11 -0
- package/dist/agent-memories/claude-code.js +113 -0
- package/dist/agent-memories/codex.d.ts +3 -0
- package/dist/agent-memories/codex.js +47 -0
- package/dist/agent-memories/copilot.d.ts +3 -0
- package/dist/agent-memories/copilot.js +125 -0
- package/dist/agent-memories/files.d.ts +37 -0
- package/dist/agent-memories/files.js +77 -0
- package/dist/agent-memories/folder-store.d.ts +17 -0
- package/dist/agent-memories/folder-store.js +44 -0
- package/dist/agent-memories/gemini.d.ts +3 -0
- package/dist/agent-memories/gemini.js +103 -0
- package/dist/agent-memories/git.d.ts +8 -0
- package/dist/agent-memories/git.js +11 -0
- package/dist/agent-memories/keys.d.ts +9 -0
- package/dist/agent-memories/keys.js +20 -0
- package/dist/agent-memories/legacy.d.ts +17 -0
- package/dist/agent-memories/legacy.js +45 -0
- package/dist/agent-memories/markdown.d.ts +13 -0
- package/dist/agent-memories/markdown.js +123 -0
- package/dist/agent-memories/openclaw.d.ts +3 -0
- package/dist/agent-memories/openclaw.js +42 -0
- package/dist/agent-memories/plan.d.ts +78 -0
- package/dist/agent-memories/plan.js +123 -0
- package/dist/agent-memories/qwen-code.d.ts +5 -0
- package/dist/agent-memories/qwen-code.js +50 -0
- package/dist/agent-memories/report.d.ts +52 -0
- package/dist/agent-memories/report.js +88 -0
- package/dist/agent-memories/source.d.ts +16 -0
- package/dist/agent-memories/source.js +32 -0
- package/dist/agent-memories/sync.d.ts +33 -0
- package/dist/agent-memories/sync.js +336 -0
- package/dist/agent-memories/tools.d.ts +33 -0
- package/dist/agent-memories/tools.js +19 -0
- package/dist/agent-memories/types.d.ts +42 -0
- package/dist/agent-memories/types.js +2 -0
- package/dist/api.d.ts +53 -20
- package/dist/api.js +141 -97
- package/dist/audit.d.ts +2 -1
- package/dist/audit.js +68 -2
- package/dist/capture.d.ts +48 -22
- package/dist/capture.js +186 -161
- package/dist/cli.d.ts +0 -2
- package/dist/cli.js +750 -797
- package/dist/codex-patch.d.ts +12 -0
- package/dist/codex-patch.js +71 -0
- package/dist/compaction-items.d.ts +18 -0
- package/dist/compaction-items.js +60 -0
- package/dist/compaction-record.d.ts +94 -0
- package/dist/compaction-record.js +546 -0
- package/dist/config.d.ts +4 -1
- package/dist/config.js +13 -4
- package/dist/connectors/slack/types.d.ts +0 -1
- package/dist/consolidate.js +87 -34
- package/dist/context-render.d.ts +36 -0
- package/dist/context-render.js +154 -0
- package/dist/dag.js +3 -2
- package/dist/db.d.ts +5 -1
- package/dist/db.js +46 -14
- package/dist/dedupe.d.ts +6 -6
- package/dist/dedupe.js +10 -9
- package/dist/doctor.d.ts +1 -1
- package/dist/doctor.js +69 -4
- package/dist/dormant.d.ts +9 -3
- package/dist/dormant.js +26 -2
- package/dist/embedding-provider.d.ts +2 -1
- package/dist/embedding-provider.js +2 -1
- package/dist/embeddings.js +23 -3
- package/dist/extract.js +5 -1
- package/dist/forward-claim-detector.d.ts +1 -1
- package/dist/forward-claim-detector.js +1 -1
- package/dist/gated-write.d.ts +9 -0
- package/dist/gated-write.js +24 -0
- package/dist/graph-recall.d.ts +3 -1
- package/dist/graph-recall.js +5 -3
- package/dist/hooks.d.ts +18 -2
- package/dist/hooks.js +128 -32
- package/dist/importers.js +5 -12
- package/dist/judgment.d.ts +30 -0
- package/dist/judgment.js +122 -0
- package/dist/mcp/server.js +171 -210
- package/dist/memory.d.ts +19 -2
- package/dist/memory.js +35 -3
- package/dist/merged-row.d.ts +6 -0
- package/dist/merged-row.js +35 -0
- package/dist/multihop.d.ts +2 -1
- package/dist/multihop.js +7 -4
- package/dist/physics-state.d.ts +0 -4
- package/dist/physics-state.js +0 -6
- package/dist/predictions.d.ts +2 -17
- package/dist/predictions.js +2 -15
- package/dist/reject-flow.d.ts +7 -5
- package/dist/reject-flow.js +41 -12
- package/dist/salience.js +12 -5
- package/dist/same-text.d.ts +17 -0
- package/dist/same-text.js +38 -0
- package/dist/scheduler.d.ts +4 -0
- package/dist/scheduler.js +8 -0
- package/dist/search.d.ts +7 -0
- package/dist/search.js +16 -32
- package/dist/secret-detect.d.ts +2 -0
- package/dist/secret-detect.js +6 -0
- package/dist/server-detect.js +9 -33
- package/dist/server.js +6 -62
- package/dist/session-digest.d.ts +79 -0
- package/dist/session-digest.js +528 -0
- package/dist/shared.d.ts +10 -2
- package/dist/shared.js +44 -36
- package/dist/store.d.ts +9 -2
- package/dist/store.js +25 -2
- package/dist/token-ledger.d.ts +46 -8
- package/dist/token-ledger.js +140 -21
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/extensions/openclaw-plugin/README.md +4 -4
- package/extensions/openclaw-plugin/openclaw.plugin.json +2 -2
- package/extensions/openclaw-plugin/package.json +1 -1
- package/openclaw.plugin.json +2 -2
- package/package.json +2 -2
package/dist/api.js
CHANGED
|
@@ -17,7 +17,7 @@ import { detectInstruction } from './instruction-detect.js';
|
|
|
17
17
|
import { quarantineScopeFor, recordQuarantine, getQuarantineRow, listQuarantineRows, approveQuarantineRow, rejectQuarantineRow, } from './quarantine.js';
|
|
18
18
|
import { summarizeFailures } from './failure-log.js';
|
|
19
19
|
import { formatHandoffEvidenceLine } from './handoff.js';
|
|
20
|
-
import { createMemory, applyOutcome, calculateStrength,
|
|
20
|
+
import { createMemory, createSuccessor, applyOutcome, calculateStrength, CHURN_STALE_TAG, COMPACTION_MEMORY_TAG, } from './memory.js';
|
|
21
21
|
import { appendAuditEvent, queryAuditEvents, auditMemories, isContentWorthStoring, } from './audit.js';
|
|
22
22
|
import { promoteToGlobal, getGlobalRoot, autoShare, searchBothHybrid } from './shared.js';
|
|
23
23
|
import { writeRecallTrace, writeRecallTraceAtRoot, recordTraceOutcome } from './recall-trace.js';
|
|
@@ -27,12 +27,14 @@ import { createApiKey, listApiKeys, revokeApiKey, grantScope, ungrantScope, } fr
|
|
|
27
27
|
import { applyGoalStackBoost } from './goals.js';
|
|
28
28
|
import { markRetrieved, estimateTokens, hybridSearch, physicsSearch, churnStaleFactor } from './search.js';
|
|
29
29
|
import { compareEntryIdentity, compareScoredResults } from './compare.js';
|
|
30
|
+
import { dropHeldCopies, duplicateKey, storedTextKeys } from './same-text.js';
|
|
30
31
|
import { scopeMatch } from './scope.js';
|
|
31
32
|
import { consolidate } from './consolidate.js';
|
|
32
33
|
import { loadConfig } from './config.js';
|
|
33
34
|
import { resolveProjectIdentity, classifyOriginProject, isGlobalStoreRoot } from './project-identity.js';
|
|
34
35
|
import { promptTokens, contentTokens, gatePromptRecall, } from './prompt-recall.js';
|
|
35
36
|
import { detectSecret } from './secret-detect.js';
|
|
37
|
+
import { isSessionDigestRow } from './session-digest.js';
|
|
36
38
|
import { deduplicateStore } from './dedupe.js';
|
|
37
39
|
import { computeAmbientState } from './ambient.js';
|
|
38
40
|
import { loadPendingExtractionTenants, markPendingProcessedUpTo } from './graph.js';
|
|
@@ -425,6 +427,13 @@ function recallFrom(ctx, opts, windowSize, all) {
|
|
|
425
427
|
}));
|
|
426
428
|
}
|
|
427
429
|
}
|
|
430
|
+
if (!opts.keepHeldCopies) {
|
|
431
|
+
const shownIds = new Set(dropHeldCopies([...baseScored.map((r) => r.entry), ...substituted.map((s) => s.entry)], (e) => e).map((e) => e.id));
|
|
432
|
+
droppedPreRankCount += baseScored.filter((r) => !shownIds.has(r.entry.id)).length;
|
|
433
|
+
baseScored = baseScored.filter((r) => shownIds.has(r.entry.id));
|
|
434
|
+
baseSlice = baseScored.map((r) => r.entry);
|
|
435
|
+
substituted = substituted.filter((s) => shownIds.has(s.entry.id));
|
|
436
|
+
}
|
|
428
437
|
// v1.12.13 / C5 — WYSIATI summary_substitutions_added counter.
|
|
429
438
|
summarySubstitutionsCount = substituted.length;
|
|
430
439
|
// v1.7.4 -- baseScored carries the (possibly boosted) per-row scores. When
|
|
@@ -498,9 +507,11 @@ function recallFrom(ctx, opts, windowSize, all) {
|
|
|
498
507
|
...baseRanked.map((r) => r.id),
|
|
499
508
|
...summaryRanked.map((r) => r.id),
|
|
500
509
|
]);
|
|
510
|
+
const shownKeys = storedTextKeys(opts.keepHeldCopies ? [] : [...baseSlice, ...substituted.map((s) => s.entry)]);
|
|
501
511
|
for (const m of recentScoped) {
|
|
502
|
-
if (seenIds.has(m.id))
|
|
512
|
+
if (seenIds.has(m.id) || shownKeys.has(duplicateKey(m.content)))
|
|
503
513
|
continue;
|
|
514
|
+
shownKeys.add(duplicateKey(m.content));
|
|
504
515
|
const item = {
|
|
505
516
|
id: m.id,
|
|
506
517
|
content: m.content,
|
|
@@ -626,10 +637,7 @@ function recallFrom(ctx, opts, windowSize, all) {
|
|
|
626
637
|
// per-pipeline). opts.actor threads through to the inner
|
|
627
638
|
// computePredictionBaserate call so MCP/HTTP-originated hints attribute
|
|
628
639
|
// correctly instead of defaulting to 'cli'. Disabled by HIPPO_AUTODEBIAS=off.
|
|
629
|
-
//
|
|
630
|
-
// computePlanningFallacyOutput so the no-class-match / tiebreak
|
|
631
|
-
// watching variant can also reach the caller surface. The two
|
|
632
|
-
// outputs are mutually exclusive; we splat both as optional fields.
|
|
640
|
+
// The hint and the no-class-match / tiebreak watching variant are mutually exclusive; both go out as optional fields.
|
|
633
641
|
const planningFallacyOutput = computePlanningFallacyOutput(ctx.hippoRoot, ctx.tenantId, opts.query, { actor: ctx.actor.subject });
|
|
634
642
|
const planningFallacyHint = planningFallacyOutput.hint ?? null;
|
|
635
643
|
const planningFallacyWatching = planningFallacyOutput.watching ?? null;
|
|
@@ -869,9 +877,11 @@ export function assemble(ctx, sessionId, opts = {}) {
|
|
|
869
877
|
olderItems.sort((a, b) => cmpIso(a.createdAt, b.createdAt));
|
|
870
878
|
tailItems.sort((a, b) => cmpIso(a.createdAt, b.createdAt));
|
|
871
879
|
let items = [...olderItems, ...tailItems];
|
|
872
|
-
|
|
880
|
+
const itemCost = opts.cost?.item ?? ((it) => estimateTokens(it.content));
|
|
881
|
+
const room = budget - (opts.cost?.fixed(Math.max(budget, totalRaw)) ?? 0);
|
|
882
|
+
let tokens = items.reduce((acc, it) => acc + itemCost(it), 0);
|
|
873
883
|
let evicted = 0;
|
|
874
|
-
while (tokens >
|
|
884
|
+
while (tokens > room && items.length > 0) {
|
|
875
885
|
let worstIdx = -1;
|
|
876
886
|
let worstStrength = Infinity;
|
|
877
887
|
for (let i = 0; i < items.length; i++) {
|
|
@@ -884,7 +894,7 @@ export function assemble(ctx, sessionId, opts = {}) {
|
|
|
884
894
|
}
|
|
885
895
|
if (worstIdx === -1)
|
|
886
896
|
break;
|
|
887
|
-
const cost =
|
|
897
|
+
const cost = itemCost(items[worstIdx]);
|
|
888
898
|
items = items.filter((_, i) => i !== worstIdx);
|
|
889
899
|
tokens -= cost;
|
|
890
900
|
evicted++;
|
|
@@ -961,15 +971,32 @@ export function drillDown(ctx, summaryId, opts = {}) {
|
|
|
961
971
|
break;
|
|
962
972
|
frontier = nextFrontier;
|
|
963
973
|
}
|
|
974
|
+
const summaryOut = {
|
|
975
|
+
id: summary.id,
|
|
976
|
+
content: summary.content,
|
|
977
|
+
// v0.30 / E5: the STORED direct-child count; the legacy fallback counts
|
|
978
|
+
// level-0 children, never the BFS-depth-N total (independent-review MED #4).
|
|
979
|
+
descendantCount: summary.descendant_count ?? level0DirectCount,
|
|
980
|
+
earliestAt: summary.earliest_at ?? null,
|
|
981
|
+
latestAt: summary.latest_at ?? null,
|
|
982
|
+
};
|
|
983
|
+
const all = collected.map((c) => ({
|
|
984
|
+
id: c.id,
|
|
985
|
+
content: c.content,
|
|
986
|
+
layer: c.layer,
|
|
987
|
+
dagLevel: c.dag_level ?? 0,
|
|
988
|
+
created: c.created,
|
|
989
|
+
}));
|
|
964
990
|
// Apply global cumulative token budget + limit cap on collected.
|
|
965
|
-
let children =
|
|
991
|
+
let children = all;
|
|
966
992
|
let truncated = false;
|
|
967
993
|
if (opts.budget !== undefined) {
|
|
968
994
|
const out = [];
|
|
969
995
|
let used = 0;
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
996
|
+
const room = opts.budget - (opts.cost?.fixed(summaryOut, all.length) ?? 0);
|
|
997
|
+
for (const c of all) {
|
|
998
|
+
const t = opts.cost ? opts.cost.child(c) : estimateTokens(c.content);
|
|
999
|
+
if (out.length > 0 && used + t > room) {
|
|
973
1000
|
truncated = true;
|
|
974
1001
|
break;
|
|
975
1002
|
}
|
|
@@ -983,25 +1010,8 @@ export function drillDown(ctx, summaryId, opts = {}) {
|
|
|
983
1010
|
truncated = true;
|
|
984
1011
|
}
|
|
985
1012
|
return {
|
|
986
|
-
summary:
|
|
987
|
-
|
|
988
|
-
content: summary.content,
|
|
989
|
-
// v0.30 / E5: descendant_count stays the summary's STORED value
|
|
990
|
-
// (direct children at creation time). totalChildren below reflects
|
|
991
|
-
// the full BFS collection at the requested depth.
|
|
992
|
-
// independent-review MED #4 fold: legacy fallback uses level-0 direct
|
|
993
|
-
// count (NOT collected.length which is BFS-depth-N total).
|
|
994
|
-
descendantCount: summary.descendant_count ?? level0DirectCount,
|
|
995
|
-
earliestAt: summary.earliest_at ?? null,
|
|
996
|
-
latestAt: summary.latest_at ?? null,
|
|
997
|
-
},
|
|
998
|
-
children: children.map((c) => ({
|
|
999
|
-
id: c.id,
|
|
1000
|
-
content: c.content,
|
|
1001
|
-
layer: c.layer,
|
|
1002
|
-
dagLevel: c.dag_level ?? 0,
|
|
1003
|
-
created: c.created,
|
|
1004
|
-
})),
|
|
1013
|
+
summary: summaryOut,
|
|
1014
|
+
children,
|
|
1005
1015
|
// v0.30 / E5: totalChildren = BFS-collected count (depth-aware). For
|
|
1006
1016
|
// depth=1 this equals the eligible direct-children count (backward
|
|
1007
1017
|
// compat). For depth>1 it is the cumulative count across levels.
|
|
@@ -1192,14 +1202,8 @@ export function supersede(ctx, oldId, newContent) {
|
|
|
1192
1202
|
if (old.superseded_by) {
|
|
1193
1203
|
throw new Error(`Memory ${oldId} is already superseded by ${old.superseded_by}. Supersede that one instead.`);
|
|
1194
1204
|
}
|
|
1195
|
-
const newEntry =
|
|
1196
|
-
layer: old.layer ?? Layer.Episodic,
|
|
1197
|
-
tags: [...old.tags],
|
|
1198
|
-
pinned: old.pinned,
|
|
1199
|
-
source: old.source,
|
|
1200
|
-
confidence: 'verified',
|
|
1205
|
+
const newEntry = createSuccessor(old, newContent, {
|
|
1201
1206
|
tenantId: ctx.tenantId,
|
|
1202
|
-
scope: old.scope,
|
|
1203
1207
|
baseHalfLifeDays: loadConfig(ctx.hippoRoot).defaultHalfLifeDays,
|
|
1204
1208
|
});
|
|
1205
1209
|
// Race-safe transition: open a fresh db handle, BEGIN IMMEDIATE, run all
|
|
@@ -1539,13 +1543,13 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1539
1543
|
if (budget <= 0) {
|
|
1540
1544
|
return { entries: [], tokens: 0 };
|
|
1541
1545
|
}
|
|
1542
|
-
//
|
|
1543
|
-
// UserPromptSubmit hook can run in directories without a .hippo). Non-pinned
|
|
1544
|
-
// path requires an initialised local store; callers should check first.
|
|
1546
|
+
// Global memories do not establish a project boundary for task state.
|
|
1545
1547
|
const hasLocal = isInitialized(ctx.hippoRoot);
|
|
1546
1548
|
const query = (opts.q ?? '').trim() || '*';
|
|
1547
1549
|
const globalRoot = getGlobalRoot();
|
|
1548
1550
|
const hasGlobal = isInitialized(globalRoot);
|
|
1551
|
+
const primaryIsGlobal = isGlobalStoreRoot(ctx.hippoRoot);
|
|
1552
|
+
const hasLocalTaskState = hasLocal && !primaryIsGlobal;
|
|
1549
1553
|
// v39 memory scope isolation (docs/plans/2026-07-01-memory-scope-isolation.md).
|
|
1550
1554
|
// S2: envelope-filter parity with api.recall for AMBIENT context - private
|
|
1551
1555
|
// scopes and quarantine buckets never inject. `requested` is deliberately
|
|
@@ -1557,11 +1561,6 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1557
1561
|
const isolationEnabled = config.contextProjectIsolation !== false;
|
|
1558
1562
|
const currentProjectName = opts.currentProject ?? resolveProjectIdentity(process.cwd()).name;
|
|
1559
1563
|
const includeCrossProject = opts.crossProject === true || !isolationEnabled;
|
|
1560
|
-
const ambientAdmit = (e) => ambientAdmitEntry(e, currentProjectName, includeCrossProject);
|
|
1561
|
-
// Superseded rows never inject, and ambientAdmitEntry regex-scans content for
|
|
1562
|
-
// secrets, so WHICH rows reach this predicate is what loadAmbientEntries cares
|
|
1563
|
-
// about below.
|
|
1564
|
-
const admit = (e) => !e.superseded_by && ambientAdmit(e);
|
|
1565
1564
|
// Z1: decided before the ambient loads so the FTS candidate query below (pinned-only
|
|
1566
1565
|
// branch) can piggyback on that connection instead of opening its own.
|
|
1567
1566
|
const promptRecallPending = pinnedOnly && Boolean(opts.prompt?.trim()) && config.pinnedInject.promptRecall === true;
|
|
@@ -1571,18 +1570,21 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1571
1570
|
const recallRequest = promptRecallTerms.length > 0
|
|
1572
1571
|
? { terms: promptRecallTerms, limit: Math.floor(finiteOr(config.pinnedInject.promptRecallCandidates, 100, 1)) }
|
|
1573
1572
|
: undefined;
|
|
1574
|
-
|
|
1575
|
-
const
|
|
1576
|
-
?
|
|
1577
|
-
:
|
|
1578
|
-
const
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1573
|
+
const cost = opts.cost;
|
|
1574
|
+
const price = (entry, isGlobal, promptRecall) => cost
|
|
1575
|
+
? cost.entry({ entry, isGlobal, promptRecall, origin: entry.origin_project ?? null, category: classifyOriginProject(entry.origin_project, currentProjectName) })
|
|
1576
|
+
: estimateTokens(entry.content);
|
|
1577
|
+
const blockBudget = pinnedOnly && opts.budget === undefined ? config.pinnedInject.budget : budget;
|
|
1578
|
+
let left = cost
|
|
1579
|
+
? Math.max(0, blockBudget - cost.fixed(blockBudget, { cross: includeCrossProject, promptRecall: promptRecallPending, ambient: !pinnedOnly && config.ambient.enabled }))
|
|
1580
|
+
: blockBudget;
|
|
1581
|
+
// Sections print ahead of the memories, so they are paid first; one that does not fit is dropped, as an oversize entry is.
|
|
1582
|
+
const pays = (tokens) => {
|
|
1583
|
+
if (tokens > left)
|
|
1584
|
+
return false;
|
|
1585
|
+
left -= tokens;
|
|
1586
|
+
return true;
|
|
1587
|
+
};
|
|
1586
1588
|
// DF1 T2: bounded read — an orphaned snapshot (no later pre-compact
|
|
1587
1589
|
// superseded it, no session-end closed it) must age out of this ambient
|
|
1588
1590
|
// surface instead of injecting into every future prompt forever. Owner
|
|
@@ -1590,7 +1592,7 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1590
1592
|
// unbounded; see loadFreshActiveTaskSnapshot's own doc comment for the
|
|
1591
1593
|
// exact null/empty-id matching rules.
|
|
1592
1594
|
const rowScope = (r) => r?.scope ?? null;
|
|
1593
|
-
const rawActiveSnapshot =
|
|
1595
|
+
const rawActiveSnapshot = hasLocalTaskState
|
|
1594
1596
|
? loadFreshActiveTaskSnapshot(ctx.hippoRoot, ctx.tenantId, {
|
|
1595
1597
|
sessionId: opts.currentSessionId,
|
|
1596
1598
|
})
|
|
@@ -1601,7 +1603,7 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1601
1603
|
? rawActiveSnapshot
|
|
1602
1604
|
: null;
|
|
1603
1605
|
// Key on the RAW snapshot: a scope-hidden active session must not fall through to another session's ambient handoff.
|
|
1604
|
-
const rawSessionHandoff = !
|
|
1606
|
+
const rawSessionHandoff = !hasLocalTaskState
|
|
1605
1607
|
? null
|
|
1606
1608
|
: rawActiveSnapshot?.session_id
|
|
1607
1609
|
? loadLatestHandoff(ctx.hippoRoot, ctx.tenantId, rawActiveSnapshot.session_id)
|
|
@@ -1615,18 +1617,49 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1615
1617
|
? rawSessionHandoff
|
|
1616
1618
|
: null;
|
|
1617
1619
|
// Raw session id here too: each event is admitted on its own scope, same as recall and the CLI.
|
|
1618
|
-
const recentSessionEvents =
|
|
1620
|
+
const recentSessionEvents = hasLocalTaskState && rawActiveSnapshot?.session_id
|
|
1619
1621
|
? listSessionEvents(ctx.hippoRoot, ctx.tenantId, {
|
|
1620
1622
|
session_id: rawActiveSnapshot.session_id,
|
|
1621
1623
|
limit: 5,
|
|
1622
1624
|
}).filter((e) => passesScopeFilterForRecall(rowScope(e), undefined))
|
|
1623
1625
|
: [];
|
|
1626
|
+
const shownSnapshot = activeSnapshot && (!cost || pays(cost.snapshot(activeSnapshot))) ? activeSnapshot : null;
|
|
1627
|
+
const shownHandoff = sessionHandoff && (!cost || pays(cost.handoff(sessionHandoff))) ? sessionHandoff : null;
|
|
1628
|
+
const shownEvents = recentSessionEvents.length > 0 && (!cost || pays(cost.trail(recentSessionEvents))) ? recentSessionEvents : [];
|
|
1629
|
+
const transcriptHandoffSession = shownHandoff?.evidence?.derivedFrom === 'transcript' ? shownHandoff.sessionId : null;
|
|
1630
|
+
let digestHiddenForHandoff = false;
|
|
1631
|
+
const ambientAdmit = (e) => {
|
|
1632
|
+
// A printed handoff already carries the session's closing message, which its digest would print a second time.
|
|
1633
|
+
if (transcriptHandoffSession !== null && e.source_session_id === transcriptHandoffSession && isSessionDigestRow(e)) {
|
|
1634
|
+
digestHiddenForHandoff = true;
|
|
1635
|
+
return false;
|
|
1636
|
+
}
|
|
1637
|
+
return ambientAdmitEntry(e, currentProjectName, includeCrossProject);
|
|
1638
|
+
};
|
|
1639
|
+
const ownSessionId = opts.currentSessionId || '';
|
|
1640
|
+
// Inside admit, not after the load, so the loader's window widens past a session's own items.
|
|
1641
|
+
const isOwnCompactionItem = (e) => ownSessionId !== '' &&
|
|
1642
|
+
e.source_session_id === ownSessionId &&
|
|
1643
|
+
e.tags.includes(COMPACTION_MEMORY_TAG);
|
|
1644
|
+
// Superseded rows never inject; which rows reach ambientAdmitEntry matters because it regex-scans content for secrets.
|
|
1645
|
+
const admit = (e) => !e.superseded_by && !isOwnCompactionItem(e) && ambientAdmit(e);
|
|
1646
|
+
// Tenant-scoped loads (v1.11.1 lesson: NEVER resolveTenantId({}) here).
|
|
1647
|
+
const localLoad = hasLocal
|
|
1648
|
+
? loadAmbientEntries(ctx.hippoRoot, ctx.tenantId, pinnedOnly, includeRecent, admit, recallRequest)
|
|
1649
|
+
: { entries: [] };
|
|
1650
|
+
const globalLoad = hasGlobal && !primaryIsGlobal
|
|
1651
|
+
? loadAmbientEntries(globalRoot, ctx.tenantId, pinnedOnly, includeRecent, admit, recallRequest)
|
|
1652
|
+
: { entries: [] };
|
|
1653
|
+
let localEntries = localLoad.entries;
|
|
1654
|
+
let globalEntries = globalLoad.entries;
|
|
1655
|
+
// Computed after markRetrieved runs, so avgStrength reflects post-retrieval strengths.
|
|
1656
|
+
let ambientState;
|
|
1624
1657
|
if (!promptRecallPending &&
|
|
1625
1658
|
localEntries.length === 0 &&
|
|
1626
1659
|
globalEntries.length === 0 &&
|
|
1627
|
-
!
|
|
1628
|
-
!
|
|
1629
|
-
|
|
1660
|
+
!shownSnapshot &&
|
|
1661
|
+
!shownHandoff &&
|
|
1662
|
+
shownEvents.length === 0) {
|
|
1630
1663
|
return { entries: [], tokens: 0 };
|
|
1631
1664
|
}
|
|
1632
1665
|
let selectedItems = [];
|
|
@@ -1637,8 +1670,8 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1637
1670
|
if (!pinnedCfg.pinnedInject.enabled) {
|
|
1638
1671
|
return { entries: [], tokens: 0 };
|
|
1639
1672
|
}
|
|
1640
|
-
// Effective budget: explicit opts.budget wins over config.
|
|
1641
|
-
const effBudget =
|
|
1673
|
+
// Effective budget: explicit opts.budget wins over config, less what the sections took.
|
|
1674
|
+
const effBudget = left;
|
|
1642
1675
|
const nowP = evalNow(); // honors HIPPO_FAKE_NOW (eval-only; see ablation.ts)
|
|
1643
1676
|
const [localPool, globalPool] = oneCopyPerMemory(localEntries, globalEntries, nowP);
|
|
1644
1677
|
const selectedIds = new Set();
|
|
@@ -1649,7 +1682,7 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1649
1682
|
const pinnedLocal = localPool.filter((e) => e.pinned);
|
|
1650
1683
|
const pinnedGlobal = globalPool.filter((e) => e.pinned);
|
|
1651
1684
|
const rankedPinned = [
|
|
1652
|
-
...pinnedLocal.map((e) => ({ entry: e, isGlobal:
|
|
1685
|
+
...pinnedLocal.map((e) => ({ entry: e, isGlobal: primaryIsGlobal })),
|
|
1653
1686
|
...pinnedGlobal.map((e) => ({ entry: e, isGlobal: true })),
|
|
1654
1687
|
]
|
|
1655
1688
|
.map(({ entry, isGlobal }) => {
|
|
@@ -1658,7 +1691,7 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1658
1691
|
return {
|
|
1659
1692
|
entry,
|
|
1660
1693
|
score: calculateStrength(entry, nowP) * (isGlobal ? 1 / 1.2 : 1) * sBst,
|
|
1661
|
-
tokens:
|
|
1694
|
+
tokens: price(entry, isGlobal),
|
|
1662
1695
|
isGlobal,
|
|
1663
1696
|
};
|
|
1664
1697
|
})
|
|
@@ -1715,7 +1748,7 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1715
1748
|
if (seenCandidateIds.has(e.id))
|
|
1716
1749
|
continue;
|
|
1717
1750
|
seenCandidateIds.add(e.id);
|
|
1718
|
-
candidateItems.push({ id: e.id, tokens: contentTokens(e.content), entry: e, isGlobal:
|
|
1751
|
+
candidateItems.push({ id: e.id, tokens: contentTokens(e.content), entry: e, isGlobal: primaryIsGlobal });
|
|
1719
1752
|
}
|
|
1720
1753
|
for (const e of globalCandidates) {
|
|
1721
1754
|
if (seenCandidateIds.has(e.id))
|
|
@@ -1727,7 +1760,7 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1727
1760
|
for (const g of gated) {
|
|
1728
1761
|
if (selectedIds.has(g.item.id))
|
|
1729
1762
|
continue;
|
|
1730
|
-
const tokens =
|
|
1763
|
+
const tokens = price(g.item.entry, g.item.isGlobal, true);
|
|
1731
1764
|
if (usedP + tokens > recentBudget)
|
|
1732
1765
|
continue;
|
|
1733
1766
|
selectedItems.push({ entry: g.item.entry, score: g.score, tokens, isGlobal: g.item.isGlobal, promptRecall: true });
|
|
@@ -1738,7 +1771,7 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1738
1771
|
}
|
|
1739
1772
|
else if (includeRecent > 0) {
|
|
1740
1773
|
const recent = [
|
|
1741
|
-
...localPool.map((entry) => ({ entry, isGlobal:
|
|
1774
|
+
...localPool.map((entry) => ({ entry, isGlobal: primaryIsGlobal })),
|
|
1742
1775
|
...globalPool.map((entry) => ({ entry, isGlobal: true })),
|
|
1743
1776
|
]
|
|
1744
1777
|
// T2 (src/compare.ts) note: this already carries an explicit
|
|
@@ -1775,7 +1808,7 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1775
1808
|
.map(({ entry, isGlobal }) => ({
|
|
1776
1809
|
entry,
|
|
1777
1810
|
score: calculateStrength(entry, nowP) * (isGlobal ? 1 / 1.2 : 1),
|
|
1778
|
-
tokens:
|
|
1811
|
+
tokens: price(entry, isGlobal),
|
|
1779
1812
|
isGlobal,
|
|
1780
1813
|
}));
|
|
1781
1814
|
for (const r of recent) {
|
|
@@ -1790,7 +1823,8 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1790
1823
|
}
|
|
1791
1824
|
if (pinnedLocal.length === 0 &&
|
|
1792
1825
|
pinnedGlobal.length === 0 &&
|
|
1793
|
-
selectedItems.length === 0
|
|
1826
|
+
selectedItems.length === 0 &&
|
|
1827
|
+
!digestHiddenForHandoff) {
|
|
1794
1828
|
return { entries: [], tokens: 0 };
|
|
1795
1829
|
}
|
|
1796
1830
|
for (const r of rankedPinned) {
|
|
@@ -1812,22 +1846,22 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1812
1846
|
.map((e) => ({
|
|
1813
1847
|
entry: e,
|
|
1814
1848
|
score: calculateStrength(e, now),
|
|
1815
|
-
tokens:
|
|
1816
|
-
isGlobal:
|
|
1849
|
+
tokens: price(e, primaryIsGlobal),
|
|
1850
|
+
isGlobal: primaryIsGlobal,
|
|
1817
1851
|
}))
|
|
1818
1852
|
.sort(compareScoredResults);
|
|
1819
1853
|
const globalRanked = globalPool
|
|
1820
1854
|
.map((e) => ({
|
|
1821
1855
|
entry: e,
|
|
1822
1856
|
score: calculateStrength(e, now) * (1 / 1.2),
|
|
1823
|
-
tokens:
|
|
1857
|
+
tokens: price(e, true),
|
|
1824
1858
|
isGlobal: true,
|
|
1825
1859
|
}))
|
|
1826
1860
|
.sort(compareScoredResults);
|
|
1827
1861
|
const combined = [...localRanked, ...globalRanked].sort(compareScoredResults);
|
|
1828
1862
|
let used = 0;
|
|
1829
1863
|
for (const r of combined) {
|
|
1830
|
-
if (used + r.tokens >
|
|
1864
|
+
if (used + r.tokens > left)
|
|
1831
1865
|
continue;
|
|
1832
1866
|
selectedItems.push(r);
|
|
1833
1867
|
used += r.tokens;
|
|
@@ -1837,7 +1871,8 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1837
1871
|
else {
|
|
1838
1872
|
// Real query: hybrid search (global + local) or physics+hybrid (local only).
|
|
1839
1873
|
let results;
|
|
1840
|
-
|
|
1874
|
+
const minResults = cost ? 0 : undefined; // a priced block skips an oversize top hit too, so the budget bounds it
|
|
1875
|
+
if (hasGlobal && !primaryIsGlobal) {
|
|
1841
1876
|
// searchBothHybrid loads from the store roots itself, so the ambient
|
|
1842
1877
|
// filter above never saw its candidates. Admission runs INSIDE the
|
|
1843
1878
|
// search via the opt-in entryFilter, BEFORE ranking, cross-store
|
|
@@ -1845,40 +1880,48 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1845
1880
|
// excluded row saturate the budget (codex rounds 1+3) or shadow its
|
|
1846
1881
|
// admitted duplicate in the dedupe pass (codex round 4). Recall paths
|
|
1847
1882
|
// never set entryFilter, so their behavior is unchanged.
|
|
1883
|
+
const localIndex = loadIndex(ctx.hippoRoot);
|
|
1884
|
+
const isGlobalHit = (e) => !localIndex.entries[e.id];
|
|
1848
1885
|
const merged = await searchBothHybrid(query, ctx.hippoRoot, globalRoot, {
|
|
1849
|
-
budget,
|
|
1886
|
+
budget: left,
|
|
1887
|
+
minResults,
|
|
1888
|
+
cost: cost && ((r) => price(r.entry, isGlobalHit(r.entry))),
|
|
1850
1889
|
scope: activeScope,
|
|
1851
1890
|
tenantId: ctx.tenantId,
|
|
1852
1891
|
entryFilter: ambientAdmit,
|
|
1853
1892
|
});
|
|
1854
|
-
const localIndex = loadIndex(ctx.hippoRoot);
|
|
1855
1893
|
results = merged.map((r) => ({
|
|
1856
1894
|
entry: r.entry,
|
|
1857
1895
|
score: r.score,
|
|
1858
|
-
tokens: r.
|
|
1859
|
-
isGlobal:
|
|
1896
|
+
tokens: price(r.entry, isGlobalHit(r.entry)),
|
|
1897
|
+
isGlobal: isGlobalHit(r.entry),
|
|
1860
1898
|
}));
|
|
1861
1899
|
}
|
|
1862
1900
|
else {
|
|
1863
1901
|
const ctxConfig = loadConfig(ctx.hippoRoot);
|
|
1864
1902
|
const usePhysicsCtx = ctxConfig.physics?.enabled !== false;
|
|
1903
|
+
const localCost = cost && ((r) => price(r.entry, primaryIsGlobal));
|
|
1865
1904
|
const ctxResults = usePhysicsCtx
|
|
1866
1905
|
? await physicsSearch(query, localEntries, {
|
|
1867
|
-
budget,
|
|
1906
|
+
budget: left,
|
|
1907
|
+
minResults,
|
|
1908
|
+
cost: localCost,
|
|
1868
1909
|
hippoRoot: ctx.hippoRoot,
|
|
1869
1910
|
physicsConfig: ctxConfig.physics,
|
|
1870
1911
|
scope: activeScope,
|
|
1871
1912
|
})
|
|
1872
1913
|
: await hybridSearch(query, localEntries, {
|
|
1873
|
-
budget,
|
|
1914
|
+
budget: left,
|
|
1915
|
+
minResults,
|
|
1916
|
+
cost: localCost,
|
|
1874
1917
|
hippoRoot: ctx.hippoRoot,
|
|
1875
1918
|
scope: activeScope,
|
|
1876
1919
|
});
|
|
1877
1920
|
results = ctxResults.map((r) => ({
|
|
1878
1921
|
entry: r.entry,
|
|
1879
1922
|
score: r.score,
|
|
1880
|
-
tokens: r.
|
|
1881
|
-
isGlobal:
|
|
1923
|
+
tokens: price(r.entry, primaryIsGlobal),
|
|
1924
|
+
isGlobal: primaryIsGlobal,
|
|
1882
1925
|
}));
|
|
1883
1926
|
}
|
|
1884
1927
|
selectedItems = results;
|
|
@@ -1905,7 +1948,7 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1905
1948
|
closeHippoDb(localDb);
|
|
1906
1949
|
}
|
|
1907
1950
|
}
|
|
1908
|
-
if (hasGlobal) {
|
|
1951
|
+
if (hasGlobal && !primaryIsGlobal) {
|
|
1909
1952
|
const globalDb = openHippoDb(globalRoot);
|
|
1910
1953
|
try {
|
|
1911
1954
|
appendAuditEvent(globalDb, {
|
|
@@ -1922,8 +1965,9 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1922
1965
|
}
|
|
1923
1966
|
if (limit < selectedItems.length) {
|
|
1924
1967
|
selectedItems = selectedItems.slice(0, limit);
|
|
1925
|
-
totalTokens = selectedItems.reduce((sum, r) => sum + r.tokens, 0);
|
|
1926
1968
|
}
|
|
1969
|
+
selectedItems = dropHeldCopies(selectedItems, (r) => r.entry); // after the last cut, so a merged row that was cut hides nothing
|
|
1970
|
+
totalTokens = selectedItems.reduce((sum, r) => sum + r.tokens, 0);
|
|
1927
1971
|
// v39: annotate every returned entry with its origin and how it relates to
|
|
1928
1972
|
// the active project, so renderers can demarcate cross-project inclusions.
|
|
1929
1973
|
selectedItems = selectedItems.map((r) => ({
|
|
@@ -1932,9 +1976,9 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1932
1976
|
category: classifyOriginProject(r.entry.origin_project, currentProjectName),
|
|
1933
1977
|
}));
|
|
1934
1978
|
if (selectedItems.length === 0 &&
|
|
1935
|
-
!
|
|
1936
|
-
!
|
|
1937
|
-
|
|
1979
|
+
!shownSnapshot &&
|
|
1980
|
+
!shownHandoff &&
|
|
1981
|
+
shownEvents.length === 0) {
|
|
1938
1982
|
// LC1 F5 fix: this bare early-return used to skip tracing entirely — a
|
|
1939
1983
|
// query that found nothing is exactly the coverage-gap signal Track LC
|
|
1940
1984
|
// needs. Write an empty trace (result_count 0, no result rows) so it
|
|
@@ -2013,9 +2057,9 @@ export async function getContext(ctx, opts = {}) {
|
|
|
2013
2057
|
return {
|
|
2014
2058
|
entries: selectedItems,
|
|
2015
2059
|
tokens: totalTokens,
|
|
2016
|
-
activeSnapshot:
|
|
2017
|
-
sessionHandoff:
|
|
2018
|
-
recentEvents:
|
|
2060
|
+
activeSnapshot: shownSnapshot ?? undefined,
|
|
2061
|
+
sessionHandoff: shownHandoff ?? undefined,
|
|
2062
|
+
recentEvents: shownEvents.length > 0 ? shownEvents : undefined,
|
|
2019
2063
|
ambientState,
|
|
2020
2064
|
};
|
|
2021
2065
|
}
|
|
@@ -2047,8 +2091,8 @@ export function recordTokens(ctx, surface, use) {
|
|
|
2047
2091
|
}
|
|
2048
2092
|
/**
|
|
2049
2093
|
* Token ledger totals for the tenant over the last `days` days (default 30):
|
|
2050
|
-
* tokens sent
|
|
2051
|
-
*
|
|
2094
|
+
* tokens sent, skipped as unchanged and re-read by later model calls, per
|
|
2095
|
+
* surface, with session counts and mean tokens per session.
|
|
2052
2096
|
*/
|
|
2053
2097
|
export function tokenSummary(ctx, opts = {}) {
|
|
2054
2098
|
const db = openHippoDb(ctx.hippoRoot);
|
package/dist/audit.d.ts
CHANGED
|
@@ -17,7 +17,8 @@ export declare const STOP_WORDS: Set<string>;
|
|
|
17
17
|
export declare function auditMemory(entry: MemoryEntry): AuditIssue | null;
|
|
18
18
|
export declare function auditMemories(entries: MemoryEntry[]): AuditResult;
|
|
19
19
|
export declare function isContentWorthStoring(content: string): boolean;
|
|
20
|
-
export
|
|
20
|
+
export declare const AUDIT_OPS: readonly ["remember", "recall", "promote", "supersede", "forget", "archive_raw", "auth_revoke", "auth_create", "outcome", "consolidate", "audit_prune", "summary_marked_dirty", "summary_marked_clean", "summary_rebuilt", "predict_create", "predict_close", "predict_baserate", "recall_autodebias_hint", "recall_autodebias_hint_no_class_match", "recall_autodebias_hint_tiebreak", "recall_anchor_detected_query_repeat", "recall_anchor_detected_memory_dominance", "recall_anchor_skipped_no_session", "recall_availability_detected", "decision_create", "decision_supersede", "decision_close", "incident_open", "incident_resolve", "incident_close", "process_create", "process_supersede", "process_close", "policy_create", "policy_supersede", "policy_close", "skill_create", "skill_supersede", "skill_close", "project_brief_create", "project_brief_supersede", "project_brief_close", "customer_note_create", "customer_note_supersede", "customer_note_close", "mv_rescue", "reject_value", "reject_refusal", "unreject_value", "conflict_resolve", "half_life_migrate", "dormant_restore", "auth_grant", "auth_ungrant", "quarantine", "quarantine_approve", "quarantine_reject", "agent_memory_restore", "agent_memory_set_aside"];
|
|
21
|
+
export type AuditOp = (typeof AUDIT_OPS)[number];
|
|
21
22
|
export interface AppendAuditOpts {
|
|
22
23
|
tenantId: string;
|
|
23
24
|
actor: string;
|
package/dist/audit.js
CHANGED
|
@@ -111,9 +111,10 @@ function hasNoSpecificity(text) {
|
|
|
111
111
|
}
|
|
112
112
|
export function auditMemory(entry) {
|
|
113
113
|
const issue = classifyMemory(entry);
|
|
114
|
-
// Error means "auto-remove"; a pinned or
|
|
114
|
+
// Error means "auto-remove"; a pinned, raw or kept-for-good row is never auto-removed, so it can only warn.
|
|
115
115
|
if (issue?.severity === 'error' && !canAutoDelete(entry)) {
|
|
116
|
-
|
|
116
|
+
const why = entry.pinned ? 'pinned' : entry.kind === 'raw' ? 'raw' : 'keep rule';
|
|
117
|
+
return { ...issue, severity: 'warning', reason: `${issue.reason} (${why}, kept)` };
|
|
117
118
|
}
|
|
118
119
|
return issue;
|
|
119
120
|
}
|
|
@@ -167,6 +168,71 @@ export function isContentWorthStoring(content) {
|
|
|
167
168
|
return false;
|
|
168
169
|
return true;
|
|
169
170
|
}
|
|
171
|
+
// ---------------------------------------------------------------------------
|
|
172
|
+
// A5 audit log primitives (append-only mutation trail)
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
// The one list of audit ops: the AuditOp type, `hippo audit list --op` and GET /v1/audit?op= all read it.
|
|
175
|
+
export const AUDIT_OPS = [
|
|
176
|
+
'remember',
|
|
177
|
+
'recall',
|
|
178
|
+
'promote',
|
|
179
|
+
'supersede',
|
|
180
|
+
'forget',
|
|
181
|
+
'archive_raw',
|
|
182
|
+
'auth_revoke',
|
|
183
|
+
'auth_create', // emitted by api.authCreate
|
|
184
|
+
'outcome',
|
|
185
|
+
'consolidate', // emitted once per api.sleep invocation
|
|
186
|
+
'audit_prune', // emitted by pruneAuditLog after each retention prune
|
|
187
|
+
'summary_marked_dirty', // emitted by markSummaryDirty on the 0->1 transition
|
|
188
|
+
'summary_marked_clean', // emitted by clearSummaryDirtyAfterBuild after the buildDag child-link loop
|
|
189
|
+
'summary_rebuilt', // emitted by applyRebuildResult on a successful sleep-cycle rebuild
|
|
190
|
+
'predict_create', // emitted by savePrediction
|
|
191
|
+
'predict_close', // emitted by closePrediction
|
|
192
|
+
'predict_baserate', // emitted by computePredictionBaserate
|
|
193
|
+
'recall_autodebias_hint', // emitted by computePlanningFallacyOutput on success
|
|
194
|
+
'recall_autodebias_hint_no_class_match', // telemetry: forward-claim detected, no class scored
|
|
195
|
+
'recall_autodebias_hint_tiebreak', // telemetry: forward-claim detected, two or more classes tied
|
|
196
|
+
'recall_anchor_detected_query_repeat', // emitted by the anchoring detector when the same query returns the same top-1
|
|
197
|
+
'recall_anchor_detected_memory_dominance', // emitted by the anchoring detector when one memory wins top-1 across distinct queries
|
|
198
|
+
'recall_anchor_skipped_no_session', // telemetry: no sessionId, so ring tracking was skipped
|
|
199
|
+
'recall_availability_detected', // emitted when the availability/recency-bias hint fires
|
|
200
|
+
'decision_create', // emitted by saveDecision
|
|
201
|
+
'decision_supersede', // emitted by saveDecision when --supersedes resolves to an active decision
|
|
202
|
+
'decision_close', // emitted by closeDecision
|
|
203
|
+
'incident_open', // emitted by saveIncident
|
|
204
|
+
'incident_resolve', // emitted by resolveIncident
|
|
205
|
+
'incident_close', // emitted by closeIncident
|
|
206
|
+
'process_create', // emitted by saveProcess
|
|
207
|
+
'process_supersede', // emitted by saveProcess on a supersession
|
|
208
|
+
'process_close', // emitted by closeProcess
|
|
209
|
+
'policy_create', // emitted by savePolicy
|
|
210
|
+
'policy_supersede', // emitted by savePolicy on a supersession
|
|
211
|
+
'policy_close', // emitted by closePolicy
|
|
212
|
+
'skill_create', // emitted by saveSkill
|
|
213
|
+
'skill_supersede', // emitted by saveSkill on a supersession
|
|
214
|
+
'skill_close', // emitted by closeSkill
|
|
215
|
+
'project_brief_create', // emitted by saveProjectBrief
|
|
216
|
+
'project_brief_supersede', // emitted by saveProjectBrief on a supersession, including a refresh
|
|
217
|
+
'project_brief_close', // emitted by closeProjectBrief
|
|
218
|
+
'customer_note_create', // emitted by saveCustomerNote
|
|
219
|
+
'customer_note_supersede', // emitted by saveCustomerNote on a supersession
|
|
220
|
+
'customer_note_close', // emitted by closeCustomerNote
|
|
221
|
+
'mv_rescue', // emitted by consolidate() per rescued entry when config.memoryValue.enabled
|
|
222
|
+
'reject_value', // emitted by the `hippo reject` verb
|
|
223
|
+
'reject_refusal', // emitted when the rejection guard refuses a write
|
|
224
|
+
'unreject_value', // emitted by the `hippo unreject` verb
|
|
225
|
+
'conflict_resolve', // emitted by resolveConflict on every resolution path
|
|
226
|
+
'half_life_migrate', // emitted by migrateDefaultHalfLife with the rescaled ids
|
|
227
|
+
'dormant_restore', // emitted by api.restoreDormant
|
|
228
|
+
'auth_grant', // emitted by api.authGrant
|
|
229
|
+
'auth_ungrant', // emitted by api.authUngrant
|
|
230
|
+
'quarantine', // emitted by recordQuarantine inside remember's write transaction
|
|
231
|
+
'quarantine_approve', // emitted by api.quarantineApprove
|
|
232
|
+
'quarantine_reject', // emitted by api.quarantineReject
|
|
233
|
+
'agent_memory_restore', // emitted by the agent memory sync when a deleted note comes back
|
|
234
|
+
'agent_memory_set_aside', // emitted by the agent memory sync when a note is deleted or refused
|
|
235
|
+
];
|
|
170
236
|
function isBigIntValue(value) {
|
|
171
237
|
return typeof value === 'bigint';
|
|
172
238
|
}
|