hippo-memory 1.33.0 → 1.34.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/dist/api.d.ts +9 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +10 -2
- package/dist/api.js.map +1 -1
- package/dist/capture.d.ts +9 -0
- package/dist/capture.d.ts.map +1 -1
- package/dist/capture.js +4 -1
- package/dist/capture.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +99 -7
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +6 -2
- package/dist/mcp/server.js.map +1 -1
- package/dist/src/api.js +10 -2
- package/dist/src/api.js.map +1 -1
- package/dist/src/capture.js +4 -1
- package/dist/src/capture.js.map +1 -1
- package/dist/src/cli.js +99 -7
- package/dist/src/cli.js.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp/server.js +6 -2
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/store.js +73 -0
- package/dist/src/store.js.map +1 -1
- package/dist/src/version.js +1 -1
- package/dist/store.d.ts +45 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +73 -0
- package/dist/store.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
- package/extensions/openclaw-plugin/package.json +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -38,7 +38,7 @@ import { installJsonHooks, uninstallJsonHooks, resolveJsonHookPaths, detectInsta
|
|
|
38
38
|
import { createMemory, calculateStrength, calculateRewardFactor, deriveHalfLife, resolveConfidence, computeSchemaFit, Layer, } from './memory.js';
|
|
39
39
|
import { resolveProjectIdentity } from './project-identity.js';
|
|
40
40
|
import { detectSecret } from './secret-detect.js';
|
|
41
|
-
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';
|
|
41
|
+
import { getHippoRoot, isInitialized, initStore, writeEntry, readEntry, deleteEntry, loadAllEntries, loadSearchEntries, loadRecallSearchEntries, loadIndex, saveIndex, loadStats, updateStats, saveActiveTaskSnapshot, loadActiveTaskSnapshot, closeTaskSnapshotsForSession, clearActiveTaskSnapshot, appendSessionEvent, listSessionEvents, listMemoryConflicts, resolveConflict, saveSessionHandoff, loadLatestHandoff, loadHandoffById, RECALL_DEFAULT_DENY_SCOPES, } from './store.js';
|
|
42
42
|
import { rejectValue, unrejectValue, listRejectionsForTenant } from './reject-flow.js';
|
|
43
43
|
import { RejectedValueError } from './rejection.js';
|
|
44
44
|
import { markRetrieved, hybridSearch, physicsSearch, explainMatch, textOverlap, tokenize as tokenizeQuery } from './search.js';
|
|
@@ -61,7 +61,7 @@ import { detectScope } from './scope.js';
|
|
|
61
61
|
import { getGlobalRoot, initGlobal, shareMemory, listPeers, autoShare, transferScore, searchBothHybrid, syncGlobalToLocal, } from './shared.js';
|
|
62
62
|
import { DAILY_TASK_NAME, buildDailyRunnerCommand, listRegisteredWorkspaces, registerWorkspace, runDailyMaintenance, } from './scheduler.js';
|
|
63
63
|
import { importChatGPT, importClaude, importCursor, importGenericFile, importMarkdown, importVault, } from './importers.js';
|
|
64
|
-
import { cmdCapture, cmdPreCompact, truncateCodePointSafe } from './capture.js';
|
|
64
|
+
import { cmdCapture, cmdPreCompact, truncateCodePointSafe, sanitizeLogMessage } from './capture.js';
|
|
65
65
|
import { auditMemories, appendAuditEvent, } from './audit.js';
|
|
66
66
|
import { listApiKeys, revokeApiKey } from './auth.js';
|
|
67
67
|
import { buildProvenanceCoverage } from './provenance-coverage.js';
|
|
@@ -2805,7 +2805,11 @@ function cmdSessionEnd(hippoRoot, flags) {
|
|
|
2805
2805
|
// Read stdin synchronously. The SessionEnd hook payload carries
|
|
2806
2806
|
// `transcript_path` as JSON; we extract it here and pass it to the worker
|
|
2807
2807
|
// via argv so the detached child doesn't need to inherit stdin.
|
|
2808
|
+
// DF1 T3 (docs/plans/2026-08-23-df1-snapshot-lifecycle.md): `session_id`
|
|
2809
|
+
// is extracted the same way, so the worker can close the ending session's
|
|
2810
|
+
// own active task snapshot after sleep+capture finish.
|
|
2808
2811
|
let transcriptPath = null;
|
|
2812
|
+
let sessionId = null;
|
|
2809
2813
|
try {
|
|
2810
2814
|
const stdinText = fs.readFileSync(0, 'utf8');
|
|
2811
2815
|
if (stdinText && stdinText.trim().startsWith('{')) {
|
|
@@ -2813,17 +2817,22 @@ function cmdSessionEnd(hippoRoot, flags) {
|
|
|
2813
2817
|
if (typeof payload.transcript_path === 'string') {
|
|
2814
2818
|
transcriptPath = payload.transcript_path;
|
|
2815
2819
|
}
|
|
2820
|
+
if (typeof payload.session_id === 'string') {
|
|
2821
|
+
sessionId = payload.session_id;
|
|
2822
|
+
}
|
|
2816
2823
|
}
|
|
2817
2824
|
}
|
|
2818
2825
|
catch {
|
|
2819
2826
|
// No stdin, not JSON, or read failure — capture will fall back to
|
|
2820
|
-
// transcript auto-discovery.
|
|
2827
|
+
// transcript auto-discovery; the snapshot close below will no-op.
|
|
2821
2828
|
}
|
|
2822
2829
|
const workerArgs = [process.argv[1], '__session-end-worker'];
|
|
2823
2830
|
if (logFile)
|
|
2824
2831
|
workerArgs.push('--log-file', logFile);
|
|
2825
2832
|
if (transcriptPath)
|
|
2826
2833
|
workerArgs.push('--transcript', transcriptPath);
|
|
2834
|
+
if (sessionId)
|
|
2835
|
+
workerArgs.push('--session-id', sessionId);
|
|
2827
2836
|
try {
|
|
2828
2837
|
const child = spawn(process.execPath, workerArgs, {
|
|
2829
2838
|
detached: true,
|
|
@@ -2834,7 +2843,10 @@ function cmdSessionEnd(hippoRoot, flags) {
|
|
|
2834
2843
|
}
|
|
2835
2844
|
catch (err) {
|
|
2836
2845
|
// If spawn fails, run inline as a last resort — better late output than
|
|
2837
|
-
// no consolidation at all.
|
|
2846
|
+
// no consolidation at all. NOTE: `flags` carries neither --transcript nor
|
|
2847
|
+
// --session-id (both are stdin-derived, argv-only for the child), so in
|
|
2848
|
+
// this fallback capture auto-discovers the transcript and the DF1
|
|
2849
|
+
// snapshot close no-ops — the ambient freshness bound is the backstop.
|
|
2838
2850
|
cmdSessionEndWorker(hippoRoot, flags);
|
|
2839
2851
|
return;
|
|
2840
2852
|
}
|
|
@@ -2870,6 +2882,49 @@ function cmdSessionEndWorker(hippoRoot, flags) {
|
|
|
2870
2882
|
catch {
|
|
2871
2883
|
// Same treatment — the failure line is already in the log.
|
|
2872
2884
|
}
|
|
2885
|
+
// DF1 T3: close the ending session's own active task snapshot AFTER
|
|
2886
|
+
// sleep+capture complete — neither producer (runPreCompact,
|
|
2887
|
+
// `hippo snapshot save`) runs inside session-end, so this can never
|
|
2888
|
+
// destroy same-run work. Scoped to `--session-id`: a concurrent session's
|
|
2889
|
+
// active snapshot is untouched (closeTaskSnapshotsForSession's own WHERE
|
|
2890
|
+
// clause). Absent session id -> no-op plus one log line; session-end is
|
|
2891
|
+
// not guaranteed to fire at all (crash, kill -9), so the freshness bound
|
|
2892
|
+
// in loadFreshActiveTaskSnapshot is the backstop layer, not this close.
|
|
2893
|
+
const closeLogFile = typeof flags['log-file'] === 'string' ? flags['log-file'] : null;
|
|
2894
|
+
const closeSessionId = typeof flags['session-id'] === 'string' ? flags['session-id'] : null;
|
|
2895
|
+
try {
|
|
2896
|
+
if (closeSessionId) {
|
|
2897
|
+
const closed = closeTaskSnapshotsForSession(hippoRoot, resolveTenantId({}), closeSessionId);
|
|
2898
|
+
appendSessionEndCloseLog(closeLogFile, `closed ${closed} active snapshot(s) for session ${closeSessionId}`);
|
|
2899
|
+
}
|
|
2900
|
+
else {
|
|
2901
|
+
appendSessionEndCloseLog(closeLogFile, 'skip: no session_id in SessionEnd payload, active snapshot left untouched');
|
|
2902
|
+
}
|
|
2903
|
+
}
|
|
2904
|
+
catch (err) {
|
|
2905
|
+
appendSessionEndCloseLog(closeLogFile, `snapshot close failed: ${err.message}`);
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
/**
|
|
2909
|
+
* Best-effort log line for the DF1 T3 snapshot-close step in
|
|
2910
|
+
* `cmdSessionEndWorker`. `cmdSleep`/`cmdCapture` each tee console output to
|
|
2911
|
+
* `logFile` only for their own duration (the tee is restored before this
|
|
2912
|
+
* runs), so a plain `console.log` here would be silently discarded under
|
|
2913
|
+
* the detached worker's `stdio: 'ignore'` — write straight to the file
|
|
2914
|
+
* instead, matching capture.ts's `appendPreCompactLog` convention.
|
|
2915
|
+
*/
|
|
2916
|
+
function appendSessionEndCloseLog(logFile, message) {
|
|
2917
|
+
if (!logFile)
|
|
2918
|
+
return;
|
|
2919
|
+
try {
|
|
2920
|
+
fs.mkdirSync(path.dirname(logFile), { recursive: true });
|
|
2921
|
+
// sanitizeLogMessage: `message` interpolates the payload-controlled
|
|
2922
|
+
// session_id — same log-forgery guard appendPreCompactLog applies.
|
|
2923
|
+
fs.appendFileSync(logFile, `[hippo] ${new Date().toISOString()} ${sanitizeLogMessage(message)}\n`, 'utf8');
|
|
2924
|
+
}
|
|
2925
|
+
catch {
|
|
2926
|
+
// Best-effort only — never let a log-write failure surface as an error.
|
|
2927
|
+
}
|
|
2873
2928
|
}
|
|
2874
2929
|
function loadCodexWrapperMetadata() {
|
|
2875
2930
|
const { metadataPath } = resolveCodexWrapperPaths();
|
|
@@ -5316,7 +5371,7 @@ function cmdCurrent(hippoRoot, args, flags) {
|
|
|
5316
5371
|
console.error('Usage: hippo current <show>');
|
|
5317
5372
|
process.exit(1);
|
|
5318
5373
|
}
|
|
5319
|
-
async function cmdContext(hippoRoot, args, flags) {
|
|
5374
|
+
async function cmdContext(hippoRoot, args, flags, stdinText) {
|
|
5320
5375
|
// --pinned-only fires on every UserPromptSubmit — including in directories
|
|
5321
5376
|
// that don't have a local .hippo. Skip requireInit for that path and fall
|
|
5322
5377
|
// back to global-only inside api.getContext. The non-pinned path still
|
|
@@ -5347,6 +5402,24 @@ async function cmdContext(hippoRoot, args, flags) {
|
|
|
5347
5402
|
// v39 memory scope isolation: --cross-project re-includes other-project
|
|
5348
5403
|
// memories (rendered under a demarcated section below).
|
|
5349
5404
|
const crossProject = flags['cross-project'] === true;
|
|
5405
|
+
// DF1 T2: resolve the calling session's id for the bounded active-task-
|
|
5406
|
+
// snapshot read (api.getContext -> loadFreshActiveTaskSnapshot). Stdin
|
|
5407
|
+
// payload (the UserPromptSubmit hook JSON) wins; falls back to
|
|
5408
|
+
// HIPPO_SESSION_ID; absent both, undefined -- api.getContext then applies
|
|
5409
|
+
// the pure freshness bound with no owner-match short-circuit.
|
|
5410
|
+
let payloadSessionId;
|
|
5411
|
+
if (stdinText && stdinText.trim() !== '') {
|
|
5412
|
+
try {
|
|
5413
|
+
const payload = JSON.parse(stdinText.trim());
|
|
5414
|
+
if (payload && typeof payload === 'object' && typeof payload.session_id === 'string') {
|
|
5415
|
+
payloadSessionId = payload.session_id;
|
|
5416
|
+
}
|
|
5417
|
+
}
|
|
5418
|
+
catch {
|
|
5419
|
+
// Malformed/non-JSON stdin: fall through to the env fallback below.
|
|
5420
|
+
}
|
|
5421
|
+
}
|
|
5422
|
+
const currentSessionId = payloadSessionId ?? (process.env.HIPPO_SESSION_ID || undefined);
|
|
5350
5423
|
const opts = {
|
|
5351
5424
|
q: query,
|
|
5352
5425
|
budget,
|
|
@@ -5355,6 +5428,7 @@ async function cmdContext(hippoRoot, args, flags) {
|
|
|
5355
5428
|
scope: ctxActiveScope ?? undefined,
|
|
5356
5429
|
includeRecent: parseCountFlag(flags['include-recent']),
|
|
5357
5430
|
crossProject,
|
|
5431
|
+
currentSessionId,
|
|
5358
5432
|
};
|
|
5359
5433
|
const result = await api.getContext(ctx, opts);
|
|
5360
5434
|
// Early exit when there's nothing to render (matches pre-extraction behavior).
|
|
@@ -8301,9 +8375,27 @@ async function main() {
|
|
|
8301
8375
|
cmdInspect(hippoRoot, id);
|
|
8302
8376
|
break;
|
|
8303
8377
|
}
|
|
8304
|
-
case 'context':
|
|
8305
|
-
|
|
8378
|
+
case 'context': {
|
|
8379
|
+
// DF1 T2 (docs/plans/2026-08-23-df1-snapshot-lifecycle.md): same TTY
|
|
8380
|
+
// guard as `pre-compact` / `compact-resume` above — skip reading stdin
|
|
8381
|
+
// when it's an interactive terminal so a manual invocation never
|
|
8382
|
+
// hangs. `hippo context` is both the hot UserPromptSubmit path
|
|
8383
|
+
// (non-TTY, stdin carries the hook JSON with `session_id`) and a
|
|
8384
|
+
// manually-invocable command (TTY, no payload) — the guardless read in
|
|
8385
|
+
// cmdSessionEnd is the wrong sibling to copy here; it only runs under
|
|
8386
|
+
// a hook that always supplies stdin.
|
|
8387
|
+
let stdinText;
|
|
8388
|
+
if (!process.stdin.isTTY) {
|
|
8389
|
+
try {
|
|
8390
|
+
stdinText = fs.readFileSync(0, 'utf8');
|
|
8391
|
+
}
|
|
8392
|
+
catch {
|
|
8393
|
+
stdinText = undefined;
|
|
8394
|
+
}
|
|
8395
|
+
}
|
|
8396
|
+
await cmdContext(hippoRoot, args, flags, stdinText);
|
|
8306
8397
|
break;
|
|
8398
|
+
}
|
|
8307
8399
|
case 'hook':
|
|
8308
8400
|
cmdHook(args, flags);
|
|
8309
8401
|
break;
|