hippo-memory 1.72.0 → 2.0.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 +6 -4
- package/dist/agent-memories/apply.js +5 -5
- package/dist/api/assemble.js +2 -1
- package/dist/api/audit.d.ts +7 -7
- package/dist/api/audit.js +19 -17
- package/dist/api/auth.d.ts +5 -6
- package/dist/api/auth.js +28 -33
- package/dist/api/conflicts.js +1 -1
- package/dist/api/context-auto.js +4 -2
- package/dist/api/context-select.js +8 -20
- package/dist/api/context.js +12 -10
- package/dist/api/drill-down.js +2 -1
- package/dist/api/forget.d.ts +2 -2
- package/dist/api/forget.js +6 -7
- package/dist/api/graph.js +2 -1
- package/dist/api/learn.d.ts +1 -1
- package/dist/api/learn.js +45 -44
- package/dist/api/memories.d.ts +4 -2
- package/dist/api/memories.js +16 -5
- package/dist/api/objects.js +2 -1
- package/dist/api/outcome.d.ts +5 -5
- package/dist/api/outcome.js +16 -15
- package/dist/api/predictions.js +2 -1
- package/dist/api/promote.d.ts +3 -3
- package/dist/api/promote.js +25 -27
- package/dist/api/prompt-hook.d.ts +3 -1
- package/dist/api/prompt-hook.js +1 -0
- package/dist/api/quarantine.js +2 -1
- package/dist/api/recall-core.js +8 -3
- package/dist/api/recall-pipeline.js +17 -17
- package/dist/api/recall-types.d.ts +3 -1
- package/dist/api/recall.js +32 -29
- package/dist/api/remember-local.d.ts +5 -2
- package/dist/api/remember-local.js +14 -6
- package/dist/api/remember.d.ts +5 -2
- package/dist/api/remember.js +16 -14
- package/dist/api/session-end.d.ts +24 -0
- package/dist/api/session-end.js +31 -1
- package/dist/api/sleep-run.js +5 -4
- package/dist/api/sleep.js +4 -3
- package/dist/api/tokens.js +1 -1
- package/dist/api/types.d.ts +1 -7
- package/dist/capture/command.js +1 -1
- package/dist/capture/compaction-spool.d.ts +3 -5
- package/dist/capture/compaction-spool.js +41 -46
- package/dist/capture/session-end-caller.js +1 -1
- package/dist/capture/session-worker.js +19 -6
- package/dist/cli/audit.js +10 -10
- package/dist/cli/auth.js +4 -4
- package/dist/cli/client.d.ts +1 -2
- package/dist/cli/client.js +13 -13
- package/dist/cli/context.js +1 -1
- package/dist/cli/curate.d.ts +1 -1
- package/dist/cli/curate.js +9 -9
- package/dist/cli/decisions.js +4 -5
- package/dist/cli/hook-runtime.d.ts +3 -1
- package/dist/cli/hook-runtime.js +2 -2
- package/dist/cli/init.d.ts +1 -1
- package/dist/cli/init.js +9 -9
- package/dist/cli/object-verbs.d.ts +2 -2
- package/dist/cli/object-verbs.js +4 -4
- package/dist/cli/predictions.js +2 -3
- package/dist/cli/quality-repair-once.d.ts +1 -1
- package/dist/cli/quality-repair-once.js +2 -2
- package/dist/cli/refine-llm.js +2 -1
- package/dist/cli/remember.js +5 -5
- package/dist/cli/serve.js +9 -4
- package/dist/cli/session-hooks.d.ts +0 -4
- package/dist/cli/session-hooks.js +57 -66
- package/dist/cli/setup.d.ts +1 -1
- package/dist/cli/setup.js +2 -2
- package/dist/cli/shared.d.ts +1 -1
- package/dist/cli/shared.js +2 -2
- package/dist/cli/sleep.js +2 -2
- package/dist/cli/transfer.d.ts +3 -3
- package/dist/cli/transfer.js +34 -15
- package/dist/cli/usage.d.ts +1 -1
- package/dist/cli/usage.js +1 -1
- package/dist/cli/verb-row.d.ts +2 -0
- package/dist/cli/verbs/health.js +2 -0
- package/dist/cli/verbs/memory.js +43 -51
- package/dist/cli/verbs/objects.js +6 -3
- package/dist/cli/verbs/sessions.js +4 -2
- package/dist/cli.js +1 -2
- package/dist/connectors/dlq.js +2 -1
- package/dist/connectors/github/deletion.js +2 -1
- package/dist/connectors/github/ingest.js +2 -1
- package/dist/connectors/github/tenant-routing.js +2 -1
- package/dist/connectors/github/webhook.js +4 -2
- package/dist/connectors/ingest.js +2 -1
- package/dist/connectors/slack/deletion.js +2 -1
- package/dist/connectors/slack/ingest.js +2 -1
- package/dist/connectors/slack/tenant-routing.js +2 -1
- package/dist/consolidate/conflicts.js +1 -1
- package/dist/consolidate/dag.js +14 -9
- package/dist/consolidate/dedupe.js +1 -1
- package/dist/consolidate/memory-value.js +1 -1
- package/dist/consolidate/merge.js +6 -6
- package/dist/consolidate/sleep.js +1 -1
- package/dist/consolidate/traces.js +2 -2
- package/dist/core/config.js +15 -16
- package/dist/core/markdown.js +2 -2
- package/dist/core/memory.d.ts +4 -4
- package/dist/core/memory.js +10 -5
- package/dist/core/physics.d.ts +3 -1
- package/dist/core/physics.js +17 -2
- package/dist/core/project-identity.js +2 -1
- package/dist/core/search-types.d.ts +1 -1
- package/dist/dashboard/dashboard-actions.d.ts +2 -2
- package/dist/dashboard/dashboard-actions.js +5 -5
- package/dist/dashboard/dashboard-queries.js +2 -7
- package/dist/dashboard/dashboard-snapshot.js +1 -3
- package/dist/dashboard/dashboard.js +16 -5
- package/dist/db/connect.d.ts +2 -1
- package/dist/db/connect.js +2 -1
- package/dist/db/migrate.js +14 -7
- package/dist/db/migrations/v11.js +1 -0
- package/dist/db/migrations/v14.js +1 -0
- package/dist/db/physics-state.d.ts +0 -4
- package/dist/db/physics-state.js +5 -31
- package/dist/db/request-stores.d.ts +1 -0
- package/dist/db/request-stores.js +2 -1
- package/dist/db/vector-store.js +2 -1
- package/dist/doctor.d.ts +3 -0
- package/dist/doctor.js +7 -7
- package/dist/embeddings/local.js +2 -2
- package/dist/embeddings/provider.js +1 -10
- package/dist/entry/store-kit.d.ts +37 -0
- package/dist/entry/store-kit.js +25 -0
- package/dist/entry/testing.d.ts +6 -0
- package/dist/entry/testing.js +7 -0
- package/dist/eval/judgment.js +1 -1
- package/dist/hooks/json-hooks.js +6 -2
- package/dist/importers/core.js +3 -3
- package/dist/importers/vault.d.ts +1 -1
- package/dist/importers/vault.js +18 -18
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/learn/autolearn.d.ts +6 -2
- package/dist/learn/autolearn.js +8 -6
- package/dist/learn/extract.js +1 -1
- package/dist/learn/invalidation.d.ts +2 -1
- package/dist/learn/invalidation.js +10 -5
- package/dist/mcp/memory-tools.d.ts +1 -1
- package/dist/mcp/memory-tools.js +6 -9
- package/dist/mcp/request.js +20 -19
- package/dist/mcp/stdio.js +13 -8
- package/dist/rerankers/cross-encoder.js +10 -3
- package/dist/search/embed-memories.d.ts +5 -2
- package/dist/search/embed-memories.js +40 -28
- package/dist/search/engine.d.ts +8 -0
- package/dist/search/engine.js +10 -0
- package/dist/search/vector.js +5 -5
- package/dist/server/auth.d.ts +1 -1
- package/dist/server/auth.js +7 -8
- package/dist/server/boot.d.ts +2 -1
- package/dist/server/boot.js +28 -19
- package/dist/server/client-ip.d.ts +9 -4
- package/dist/server/client-ip.js +35 -17
- package/dist/server/route-table.js +5 -6
- package/dist/server/routes/memories.js +4 -2
- package/dist/server/sleep-offload.js +3 -3
- package/dist/server/tls.d.ts +7 -2
- package/dist/server/tls.js +17 -5
- package/dist/server/types.d.ts +7 -2
- package/dist/server.d.ts +4 -41
- package/dist/server.js +3 -30
- package/dist/sharing/global-store.d.ts +4 -5
- package/dist/sharing/global-store.js +6 -11
- package/dist/sharing/global-sync.js +1 -1
- package/dist/sharing/search-both.js +9 -2
- package/dist/sharing/share.js +2 -4
- package/dist/store/ambient.js +2 -1
- package/dist/store/candidates.js +18 -8
- package/dist/store/cards.js +5 -4
- package/dist/store/compactions-record.js +8 -7
- package/dist/store/conflicts.d.ts +7 -1
- package/dist/store/conflicts.js +6 -5
- package/dist/store/connectors/dlq.js +2 -1
- package/dist/store/connectors/event-log.d.ts +13 -0
- package/dist/store/connectors/event-log.js +24 -0
- package/dist/store/connectors/github.js +6 -8
- package/dist/store/connectors/slack.js +6 -10
- package/dist/store/dashboard-reads.js +1 -0
- package/dist/store/delete-and-batch.js +5 -4
- package/dist/store/delivery-recorder.d.ts +2 -2
- package/dist/store/delivery-recorder.js +1 -6
- package/dist/store/descendant-page.js +2 -1
- package/dist/store/dormant.js +7 -3
- package/dist/store/entry-reads.d.ts +25 -3
- package/dist/store/entry-reads.js +43 -12
- package/dist/store/entry-row.js +9 -2
- package/dist/store/entry-writes.d.ts +10 -7
- package/dist/store/entry-writes.js +23 -11
- package/dist/store/goals.js +3 -2
- package/dist/store/graph-reads.js +5 -4
- package/dist/store/graph-view-rows.js +2 -1
- package/dist/store/half-life.js +2 -2
- package/dist/store/handoffs.d.ts +9 -3
- package/dist/store/handoffs.js +4 -3
- package/dist/store/index.d.ts +2 -2
- package/dist/store/index.js +1 -1
- package/dist/store/mirrors.js +9 -4
- package/dist/store/open.d.ts +2 -1
- package/dist/store/open.js +2 -1
- package/dist/store/port.d.ts +59 -3
- package/dist/store/quality-repair.js +1 -1
- package/dist/store/recall-trace.js +2 -1
- package/dist/store/rejected-values.js +14 -9
- package/dist/store/rows.js +2 -2
- package/dist/store/rule-sql.d.ts +2 -0
- package/dist/store/rule-sql.js +12 -6
- package/dist/store/search-rows.d.ts +5 -5
- package/dist/store/search-rows.js +9 -12
- package/dist/store/set-aside.js +1 -1
- package/dist/store/sqlite/connector-writes-group.js +3 -1
- package/dist/store/sqlite/entry-writes-group.d.ts +3 -3
- package/dist/store/sqlite/entry-writes-group.js +1 -1
- package/dist/store/sqlite/held-connection.d.ts +2 -0
- package/dist/store/sqlite/held-connection.js +1 -1
- package/dist/store/sqlite/last-recall-group.d.ts +13 -0
- package/dist/store/sqlite/last-recall-group.js +57 -0
- package/dist/store/sqlite/local.d.ts +0 -17
- package/dist/store/sqlite/local.js +0 -31
- package/dist/store/sqlite/maintenance-groups.d.ts +6 -0
- package/dist/store/sqlite/maintenance-groups.js +60 -0
- package/dist/store/sqlite/objects-group.js +14 -6
- package/dist/store/sqlite/store.d.ts +1 -8
- package/dist/store/sqlite/store.js +13 -24
- package/dist/store/summaries.d.ts +7 -0
- package/dist/store/summaries.js +49 -7
- package/dist/store/token-ledger-rows.js +3 -2
- package/dist/store/vector-index.js +5 -2
- package/dist/util/anthropic-messages.d.ts +3 -1
- package/dist/util/anthropic-messages.js +4 -2
- package/dist/util/atomic-write.d.ts +5 -2
- package/dist/util/atomic-write.js +12 -8
- package/dist/util/env.d.ts +10 -1
- package/dist/util/env.js +10 -1
- package/dist/util/format.d.ts +2 -0
- package/dist/util/format.js +4 -0
- package/dist/util/request-scope.d.ts +6 -2
- package/dist/util/request-scope.js +7 -3
- package/dist/util/spawn-argv.d.ts +9 -0
- package/dist/util/spawn-argv.js +57 -0
- package/dist/util/sql.d.ts +3 -0
- package/dist/util/sql.js +6 -0
- package/dist/util/vector-codec.d.ts +4 -0
- package/dist/util/vector-codec.js +15 -0
- package/dist/util/version.d.ts +1 -1
- package/dist/util/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 +3 -1
- package/dist/api/on-store.d.ts +0 -16
- package/dist/api/on-store.js +0 -38
- package/dist/cli/lenient-id.d.ts +0 -2
- package/dist/cli/lenient-id.js +0 -13
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// One store handle per store for each request (HTTP call, MCP tool call, scoped CLI command), opened lazily and closed with it.
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import { connectWithFacts, getHippoDbPath } from './connect.js';
|
|
4
|
-
import { currentDeadline, currentRequestId, requestScopes } from '../util/request-scope.js';
|
|
4
|
+
import { currentDeadline, currentRequestId, currentSignal, requestScopes } from '../util/request-scope.js';
|
|
5
5
|
// Handles a scope owns, so closeHippoDb leaves them open for the rest of the request.
|
|
6
6
|
const scopedHandles = new WeakSet();
|
|
7
7
|
// process.exit skips every finally, so one exit listener closes the handles of scopes still open.
|
|
@@ -24,6 +24,7 @@ export class RequestStores {
|
|
|
24
24
|
// Taken from the scope this one opens inside, so the request's log lines keep their id.
|
|
25
25
|
requestId = currentRequestId();
|
|
26
26
|
deadline = currentDeadline();
|
|
27
|
+
signal = currentSignal();
|
|
27
28
|
setupDoneFor;
|
|
28
29
|
#failFastWhenBusy;
|
|
29
30
|
#handles = new Map();
|
package/dist/db/vector-store.js
CHANGED
|
@@ -3,6 +3,7 @@ import * as fs from 'fs';
|
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
import { withWriteScope } from './busy.js';
|
|
5
5
|
import { errorCode, errorFields, log } from '../util/log.js';
|
|
6
|
+
import { placeholders } from '../util/sql.js';
|
|
6
7
|
/** Legacy whole-file index; imported once into `memory_vectors`, then kept beside the store as a renamed backup. */
|
|
7
8
|
const LEGACY_EMBEDDINGS_FILE = 'embeddings.json';
|
|
8
9
|
export const EMBEDDING_MODEL_META_KEY = 'embedding_model';
|
|
@@ -66,7 +67,7 @@ function eachStoredVector(db, ids, take) {
|
|
|
66
67
|
const unique = [...new Set(ids)];
|
|
67
68
|
for (let i = 0; i < unique.length; i += ID_CHUNK) {
|
|
68
69
|
const chunk = unique.slice(i, i + ID_CHUNK);
|
|
69
|
-
read(db.prepare(`SELECT memory_id, vector FROM memory_vectors WHERE memory_id IN (${chunk.
|
|
70
|
+
read(db.prepare(`SELECT memory_id, vector FROM memory_vectors WHERE memory_id IN (${placeholders(chunk.length)})`).all(...chunk));
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
/** Vectors for `ids`, or every stored vector when `ids` is omitted. */
|
package/dist/doctor.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type SpoolFs } from './capture/compaction-spool.js';
|
|
1
2
|
/** Outcome of one check. `fail` makes `hippo doctor` exit 1. */
|
|
2
3
|
export type DoctorStatus = 'pass' | 'warn' | 'fail' | 'info';
|
|
3
4
|
/** One check in a {@link DoctorReport}. */
|
|
@@ -24,6 +25,8 @@ export interface DoctorOpts {
|
|
|
24
25
|
version: string;
|
|
25
26
|
nodeVersion?: string;
|
|
26
27
|
now?: Date;
|
|
28
|
+
/** Stands in for the fs the spool check reads; a test makes one call fail. */
|
|
29
|
+
spoolFs?: SpoolFs;
|
|
27
30
|
}
|
|
28
31
|
/** Minimum Node.js version hippo supports (package.json engines). */
|
|
29
32
|
export declare const MIN_NODE = "22.16.0";
|
package/dist/doctor.js
CHANGED
|
@@ -105,16 +105,16 @@ function sleepCheck(lastSleep, now) {
|
|
|
105
105
|
: { id: 'sleep', status: 'pass', detail: `last sleep ${days === 0 ? 'today' : `${days} day${days === 1 ? '' : 's'} ago`}` };
|
|
106
106
|
}
|
|
107
107
|
/** Spool counts in their own try, so a spool that cannot be read never hides the database counts. */
|
|
108
|
-
function readSpool(store, now) {
|
|
108
|
+
function readSpool(store, now, spoolFs) {
|
|
109
109
|
try {
|
|
110
|
-
return { counts: spoolCounts(store, now, REPLAY_AFTER_MS), unread: '' };
|
|
110
|
+
return { counts: spoolCounts(store, now, REPLAY_AFTER_MS, spoolFs), unread: '' };
|
|
111
111
|
}
|
|
112
112
|
catch (err) {
|
|
113
113
|
return { counts: { waiting: 0, stale: 0, bad: 0 }, unread: `; spool not read: ${errorMessage(err)}` };
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
/** Compaction records the PostCompact hook left unfinished and spool files a replay has not finished, which `hippo sleep` replays. */
|
|
117
|
-
function compactionsCheck(compactions, store, now) {
|
|
117
|
+
function compactionsCheck(compactions, store, now, spoolFs) {
|
|
118
118
|
if (!compactions.ok) {
|
|
119
119
|
return compactions.message.includes('no such table')
|
|
120
120
|
? { id: 'compactions', status: 'info', detail: 'no compaction records yet (hippo creates them on the next write)' }
|
|
@@ -122,7 +122,7 @@ function compactionsCheck(compactions, store, now) {
|
|
|
122
122
|
}
|
|
123
123
|
const { total, summarised, started } = compactions.value;
|
|
124
124
|
const stuck = summarised + started;
|
|
125
|
-
const { counts: spool, unread } = readSpool(store, now);
|
|
125
|
+
const { counts: spool, unread } = readSpool(store, now, spoolFs);
|
|
126
126
|
const spooled = spool.waiting + spool.stale + spool.bad > 0;
|
|
127
127
|
const recorded = `${total} compaction${total === 1 ? '' : 's'} recorded, none stuck`;
|
|
128
128
|
if (stuck === 0 && !spooled && unread === '')
|
|
@@ -260,7 +260,7 @@ function tokensCheck(tokens) {
|
|
|
260
260
|
};
|
|
261
261
|
}
|
|
262
262
|
/** Checks that read the store's database; a store that cannot be opened becomes one failed schema check. */
|
|
263
|
-
function databaseChecks(store, now) {
|
|
263
|
+
function databaseChecks(store, now, spoolFs) {
|
|
264
264
|
let health;
|
|
265
265
|
try {
|
|
266
266
|
health = readStoreHealth(store, {
|
|
@@ -284,7 +284,7 @@ function databaseChecks(store, now) {
|
|
|
284
284
|
tokensCheck(health.tokens),
|
|
285
285
|
failuresCheck(health.failures, health.schemaVersion),
|
|
286
286
|
sleepCheck(health.lastSleep, now),
|
|
287
|
-
compactionsCheck(health.compactions, store, now),
|
|
287
|
+
compactionsCheck(health.compactions, store, now, spoolFs),
|
|
288
288
|
];
|
|
289
289
|
}
|
|
290
290
|
// Each hook and what it does, so a partial install says what is missing.
|
|
@@ -334,7 +334,7 @@ export function runDoctor(opts) {
|
|
|
334
334
|
const { store, check } = storeCheck(local, globalRoot, hasGlobal);
|
|
335
335
|
checks.push(check);
|
|
336
336
|
if (store !== null)
|
|
337
|
-
checks.push(...databaseChecks(store, now));
|
|
337
|
+
checks.push(...databaseChecks(store, now, opts.spoolFs));
|
|
338
338
|
const holdoutRateBp = store === null ? 0 : loadConfig(store).pilot.holdoutRateBp;
|
|
339
339
|
if (holdoutRateBp > 0) {
|
|
340
340
|
checks.push({
|
package/dist/embeddings/local.js
CHANGED
|
@@ -44,8 +44,8 @@ export function createLocalEmbedder(transformers = INSTALLED_TRANSFORMERS) {
|
|
|
44
44
|
return Array.from(output.data);
|
|
45
45
|
}
|
|
46
46
|
catch (err) {
|
|
47
|
-
// The caller sees `[]` and names the memory;
|
|
48
|
-
log.
|
|
47
|
+
// The caller sees `[]` and names the memory; the first failure per model is told, the repeats go to debug.
|
|
48
|
+
log.warnThenDebug(`local-embed:${model}`, `local embedding with ${model} failed: ${errorMessage(err)}`, errorFields(err));
|
|
49
49
|
return [];
|
|
50
50
|
}
|
|
51
51
|
},
|
|
@@ -229,15 +229,6 @@ class ApiEmbeddingProvider {
|
|
|
229
229
|
}
|
|
230
230
|
}
|
|
231
231
|
}
|
|
232
|
-
function readEmbeddingsConfig(hippoRoot) {
|
|
233
|
-
try {
|
|
234
|
-
return loadConfig(hippoRoot).embeddings;
|
|
235
|
-
}
|
|
236
|
-
catch {
|
|
237
|
-
// An unreadable config falls back to provider defaults; loadConfig warns on a bad parse.
|
|
238
|
-
return {};
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
232
|
/** Validate a user-supplied API base URL: HTTPS only (or explicit localhost). */
|
|
242
233
|
function validateBaseUrl(url, fallback) {
|
|
243
234
|
const candidate = url?.trim();
|
|
@@ -260,7 +251,7 @@ function validateBaseUrl(url, fallback) {
|
|
|
260
251
|
/** Build the active embedding provider from config (or an explicit override). A missing key never throws here (it surfaces via `isAvailable()`);
|
|
261
252
|
* only an invalid config (e.g. insecure apiBaseUrl) throws, deliberately loud, so hot-path callers wrap it. */
|
|
262
253
|
export function resolveEmbeddingProvider(hippoRoot, opts = {}) {
|
|
263
|
-
const cfg =
|
|
254
|
+
const cfg = loadConfig(hippoRoot).embeddings;
|
|
264
255
|
const requested = opts.provider ?? cfg.provider ?? 'local';
|
|
265
256
|
// An explicit embeddings.enabled=false hard-disables embedding for BOTH local
|
|
266
257
|
// and API providers — for API this prevents unwanted paid off-box calls.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export { hasGroup, sqliteStore, type AmbientCandidateRequest, type ContextReads, type EntryRemoval, type EntryTarget, type EntryWrite, type EntryWrites, type HippoStore, type KeyAudit, type KeyListQuery, type KeyMint, type KeyRevoke, type KeyWrites, type LastRecall, type LastRecallWrites, type OutcomeWrite, type RawArchive, type RecallSearchArgs, type RecallWrites, type SelfKeyMint, type StoreGroup, type StoreGroups, type SupersedeWrite, type VectorReads, type VectorBackfillQuery, type VectorRowWrite, type VectorWrite, type VectorWriteResult, type VectorWrites, } from '../store/index.js';
|
|
2
|
+
export { entryAfterOutcome } from '../core/memory.js';
|
|
3
|
+
export { rejectionDigest } from '../store/rejection.js';
|
|
4
|
+
export { RejectedValueError } from '../core/api-errors.js';
|
|
5
|
+
export { ownScopeTouches } from '../core/recall-scope.js';
|
|
6
|
+
export type { HippoDbContext } from '../api/types.js';
|
|
7
|
+
export type { ApiKeyListItem, ApiKeyListRow, ApiKeyRecord, ListApiKeysOpts, NewApiKey } from '../store/auth.js';
|
|
8
|
+
export type { KeysetPosition } from '../util/keyset.js';
|
|
9
|
+
export type { AppendAuditOpts, AuditEvent, ListAuditAfterOpts } from '../store/audit.js';
|
|
10
|
+
export type { ContinuityBlock } from '../api/recall-types.js';
|
|
11
|
+
export type { ActiveGoals, GetActiveGoalsOpts, Goal, GoalRecallLogRow, RetrievalPolicy } from '../store/goals.js';
|
|
12
|
+
export type { MemoryEntry } from '../core/memory.js';
|
|
13
|
+
export type { ClassResolution, PlanningFallacyEvidence } from '../store/planning-fallacy-evidence.js';
|
|
14
|
+
export type { PredictionBaserate } from '../store/predictions.js';
|
|
15
|
+
export type { RecallTraceInput } from '../store/recall-trace.js';
|
|
16
|
+
export type { StrengthenOptions } from '../store/entry-writes.js';
|
|
17
|
+
export type { OriginFilter, RecallScopeFilter, VectorCandidateSpec } from '../store/search-rows.js';
|
|
18
|
+
export type { ContinuityKey } from '../store/sessions.js';
|
|
19
|
+
export type { SessionEvent, TaskSnapshot } from '../store/rows.js';
|
|
20
|
+
export type { SessionHandoff } from '../core/handoff.js';
|
|
21
|
+
export type { AmbientLoadResult, AmbientRecallRequest, ContextCandidateFilter, RecentOrigins } from '../store/candidates.js';
|
|
22
|
+
export type { AmbientStoreFilter } from '../store/ambient.js';
|
|
23
|
+
export type { AmbientTallies } from '../core/ambient.js';
|
|
24
|
+
export type { TokenUse } from '../store/token-ledger.js';
|
|
25
|
+
export type { EmbeddingIndexState } from '../store/vector-index.js';
|
|
26
|
+
export type { PhysicsParticle } from '../core/physics.js';
|
|
27
|
+
export { StoreBusyError } from '../store/port.js';
|
|
28
|
+
export { decodeVector, EMBEDDING_MODEL_META_KEY, encodeVector, rankVectorRows, type VectorMatch, type VectorRow } from '../db/vector-store.js';
|
|
29
|
+
export { bufferToFloat32, float32ToBuffer } from '../util/vector-codec.js';
|
|
30
|
+
export { replacesIndex } from '../store/vector-index.js';
|
|
31
|
+
export { passesScopeFilterForRecall, RECALL_DEFAULT_DENY_SCOPES } from '../core/recall-scope.js';
|
|
32
|
+
export { SECRET_TAGS } from '../util/secret-detect.js';
|
|
33
|
+
export { tallyAmbientEntries } from '../core/ambient.js';
|
|
34
|
+
export { ftsTermParts, rarestFtsQuery } from '../core/prompt-recall.js';
|
|
35
|
+
export { OTHER_STORE_MARKER } from '../db/index.js';
|
|
36
|
+
export { isSharedStore } from '../core/config.js';
|
|
37
|
+
//# sourceMappingURL=store-kit.d.ts.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// The hippo-memory/store-kit subpath: the contract for a second HippoStore (port types, row types, hippo.db's rule helpers),
|
|
2
|
+
// so a store built on it applies hippo.db's rules with the same code.
|
|
3
|
+
// An add-on serves from another database by passing serve() its own HippoStore.
|
|
4
|
+
export { hasGroup, sqliteStore, } from '../store/index.js';
|
|
5
|
+
// An add-on store's entry writes apply an outcome, guard tombstones and check reach exactly as hippo.db does.
|
|
6
|
+
export { entryAfterOutcome } from '../core/memory.js';
|
|
7
|
+
export { rejectionDigest } from '../store/rejection.js';
|
|
8
|
+
export { RejectedValueError } from '../core/api-errors.js';
|
|
9
|
+
export { ownScopeTouches } from '../core/recall-scope.js';
|
|
10
|
+
export { StoreBusyError } from '../store/port.js';
|
|
11
|
+
// An add-on store encodes, decodes and ranks vectors and particles with hippo.db's own code, and drops the index by its rule,
|
|
12
|
+
// so both stores keep the same bytes and return the same ids in the same order.
|
|
13
|
+
export { decodeVector, EMBEDDING_MODEL_META_KEY, encodeVector, rankVectorRows } from '../db/vector-store.js';
|
|
14
|
+
export { bufferToFloat32, float32ToBuffer } from '../util/vector-codec.js';
|
|
15
|
+
export { replacesIndex } from '../store/vector-index.js';
|
|
16
|
+
// An add-on's ContextReads applies hippo.db's scope, secret, tally and rarest-term rules with the same code.
|
|
17
|
+
export { passesScopeFilterForRecall, RECALL_DEFAULT_DENY_SCOPES } from '../core/recall-scope.js';
|
|
18
|
+
export { SECRET_TAGS } from '../util/secret-detect.js';
|
|
19
|
+
export { tallyAmbientEntries } from '../core/ambient.js';
|
|
20
|
+
export { ftsTermParts, rarestFtsQuery } from '../core/prompt-recall.js';
|
|
21
|
+
// store copy --db writes the marker into the other store's folder.
|
|
22
|
+
export { OTHER_STORE_MARKER } from '../db/index.js';
|
|
23
|
+
// An add-on that serves a team store checks the flag before it starts.
|
|
24
|
+
export { isSharedStore } from '../core/config.js';
|
|
25
|
+
//# sourceMappingURL=store-kit.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { _resetAblationCacheForTests } from '../core/ablation.js';
|
|
2
|
+
export { _resetSharedStoreCacheForTests } from '../core/config.js';
|
|
3
|
+
export { _resetLossAversionRatioCacheForTests } from '../core/memory.js';
|
|
4
|
+
export { _resetSessionRecallHistoryMcpForTests } from '../mcp/session-state.js';
|
|
5
|
+
export { __resetSessionRecallHistoryHttp } from '../server/routes/recall.js';
|
|
6
|
+
//# sourceMappingURL=testing.d.ts.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Test seams for add-ons and hippo's own suite. Not for production code; no semver guarantee beyond the major version.
|
|
2
|
+
export { _resetAblationCacheForTests } from '../core/ablation.js';
|
|
3
|
+
export { _resetSharedStoreCacheForTests } from '../core/config.js';
|
|
4
|
+
export { _resetLossAversionRatioCacheForTests } from '../core/memory.js';
|
|
5
|
+
export { _resetSessionRecallHistoryMcpForTests } from '../mcp/session-state.js';
|
|
6
|
+
export { __resetSessionRecallHistoryHttp } from '../server/routes/recall.js';
|
|
7
|
+
//# sourceMappingURL=testing.js.map
|
package/dist/eval/judgment.js
CHANGED
|
@@ -99,7 +99,7 @@ async function judge(content, opts) {
|
|
|
99
99
|
data = await readCappedJson(res, JUDGE_MAX_REPLY_BYTES);
|
|
100
100
|
}
|
|
101
101
|
catch (err) {
|
|
102
|
-
log.
|
|
102
|
+
log.warnThenDebug('judge-unreadable', `judge: unreadable response: ${errorMessage(err)}`, errorFields(err));
|
|
103
103
|
return null;
|
|
104
104
|
}
|
|
105
105
|
const durable = answerNamed(data, 'durable').noul;
|
package/dist/hooks/json-hooks.js
CHANGED
|
@@ -7,6 +7,7 @@ import { claudeConfigDir, codexHomeDir, homeDir } from '../util/agent-homes.js';
|
|
|
7
7
|
import { isJsonString, readJsonFile, isJsonObjectLiteral } from '../util/json.js';
|
|
8
8
|
import { escapeRegex } from '../util/escape.js';
|
|
9
9
|
import { writeFileAtomic } from '../util/atomic-write.js';
|
|
10
|
+
import { errorMessage, log } from '../util/log.js';
|
|
10
11
|
/** The paths for `target` under the current home directory; reads and writes nothing. */
|
|
11
12
|
export function resolveJsonHookPaths(target) {
|
|
12
13
|
const home = homeDir();
|
|
@@ -366,8 +367,11 @@ export function uninstallJsonHooks(target) {
|
|
|
366
367
|
try {
|
|
367
368
|
settings = readJsonFile(settingsPath);
|
|
368
369
|
}
|
|
369
|
-
catch {
|
|
370
|
-
// Never rewrite a settings file we cannot parse;
|
|
370
|
+
catch (err) {
|
|
371
|
+
// Never rewrite a settings file we cannot parse; a read failure (permissions, a directory) is the caller's to see.
|
|
372
|
+
if (!(err instanceof SyntaxError))
|
|
373
|
+
throw new Error(`could not read ${settingsPath}: ${errorMessage(err)}`, { cause: err });
|
|
374
|
+
log.warn(`${settingsPath} is not valid JSON, so no hippo hooks were removed from it: ${err.message}`);
|
|
371
375
|
return false;
|
|
372
376
|
}
|
|
373
377
|
if (!isJsonObjectLiteral(settings) || !isJsonObjectLiteral(settings.hooks))
|
package/dist/importers/core.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { DEFAULT_TENANT_ID } from '../util/env.js';
|
|
4
4
|
import { createMemory, Layer } from '../core/memory.js';
|
|
5
5
|
import { writeEntry } from '../store/entry-writes.js';
|
|
6
|
-
import {
|
|
6
|
+
import { loadEntryTextKeys } from '../store/entry-reads.js';
|
|
7
7
|
import { duplicateKey, storedTextKeys } from '../util/same-text.js';
|
|
8
8
|
import { getGlobalRoot, initGlobal } from '../sharing/global-store.js';
|
|
9
9
|
import { withRequestStoresSync } from '../db/request-stores.js';
|
|
@@ -58,7 +58,7 @@ function openImportTarget(options) {
|
|
|
58
58
|
if (options.global) {
|
|
59
59
|
initGlobal();
|
|
60
60
|
}
|
|
61
|
-
const keys = storedTextKeys(
|
|
61
|
+
const keys = storedTextKeys(loadEntryTextKeys(targetRoot, options.global ? undefined : options.tenantId));
|
|
62
62
|
return { targetRoot, keys };
|
|
63
63
|
}
|
|
64
64
|
function skipReason(chunk, keys) {
|
|
@@ -95,7 +95,7 @@ function createImportEntry(chunk, source, allTags, options, baseHalfLifeDays) {
|
|
|
95
95
|
/** Writes the entry, or on a dry run only probes the guard; false when a rejected value refuses it. */
|
|
96
96
|
function writeOrProbeImport(targetRoot, entry, options) {
|
|
97
97
|
if (options.dryRun)
|
|
98
|
-
return !rejectionGuardRefuses(targetRoot, entry.
|
|
98
|
+
return !rejectionGuardRefuses(targetRoot, entry.tenant_id ?? DEFAULT_TENANT_ID, entry.id, entry.content);
|
|
99
99
|
// A rejection refuses one chunk, not the whole import, so siblings still land.
|
|
100
100
|
try {
|
|
101
101
|
writeEntry(targetRoot, entry);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ImportResult, type ImportOptions } from './core.js';
|
|
2
2
|
/** Import a markdown vault FOLDER as `kind='raw'` memories.
|
|
3
3
|
* NOT re-entrant: idempotency rests on the in-memory `existing` Map loaded once, so two concurrent runs over one vault can double-insert. */
|
|
4
|
-
export declare function importVault(folderPath: string, options: ImportOptions): ImportResult
|
|
4
|
+
export declare function importVault(folderPath: string, options: ImportOptions): Promise<ImportResult>;
|
|
5
5
|
//# sourceMappingURL=vault.d.ts.map
|
package/dist/importers/vault.js
CHANGED
|
@@ -8,7 +8,7 @@ import { remember, archiveRaw, isPrivateScope } from '../api/index.js';
|
|
|
8
8
|
import { assertClientScope } from '../core/recall-scope.js';
|
|
9
9
|
import { RejectedValueError } from '../core/api-errors.js';
|
|
10
10
|
import { rejectionGuardRefuses } from '../store/rejected-values.js';
|
|
11
|
-
import {
|
|
11
|
+
import { runWithRequestStores } from '../db/request-stores.js';
|
|
12
12
|
import { loadConfig } from '../core/config.js';
|
|
13
13
|
import { vetSecrets } from '../util/secret-detect.js';
|
|
14
14
|
import { errorMessage, log } from '../util/log.js';
|
|
@@ -18,7 +18,7 @@ import { isJsonString } from '../util/json.js';
|
|
|
18
18
|
import { escapeLike } from '../util/escape.js';
|
|
19
19
|
/** Import a markdown vault FOLDER as `kind='raw'` memories.
|
|
20
20
|
* NOT re-entrant: idempotency rests on the in-memory `existing` Map loaded once, so two concurrent runs over one vault can double-insert. */
|
|
21
|
-
export function importVault(folderPath, options) {
|
|
21
|
+
export async function importVault(folderPath, options) {
|
|
22
22
|
const hippoRoot = options.hippoRoot;
|
|
23
23
|
const identity = vaultIdentityOrThrow(options);
|
|
24
24
|
if (options.global) {
|
|
@@ -34,7 +34,7 @@ export function importVault(folderPath, options) {
|
|
|
34
34
|
}
|
|
35
35
|
return syncVaultFolder(folderPath, options, identity);
|
|
36
36
|
}
|
|
37
|
-
function syncVaultFolder(folderPath, options, { vaultName, scope }) {
|
|
37
|
+
async function syncVaultFolder(folderPath, options, { vaultName, scope }) {
|
|
38
38
|
const hippoRoot = options.hippoRoot;
|
|
39
39
|
const tenantId = options.tenantId ?? DEFAULT_TENANT_ID;
|
|
40
40
|
const extraTags = options.extraTags ?? [];
|
|
@@ -54,15 +54,15 @@ function syncVaultFolder(folderPath, options, { vaultName, scope }) {
|
|
|
54
54
|
const run = {
|
|
55
55
|
ctx, folderPath, vaultName, scope, extraTags, dryRun, existing, seen, baseHalfLifeDays, tally,
|
|
56
56
|
};
|
|
57
|
-
const importNotes = () => {
|
|
57
|
+
const importNotes = async () => {
|
|
58
58
|
for (const relpath of relpaths)
|
|
59
|
-
importVaultNote(run, relpath);
|
|
59
|
+
await importVaultNote(run, relpath);
|
|
60
60
|
};
|
|
61
61
|
// A dry run probes the rejection guard once per changed note; one scope lets every probe share a handle.
|
|
62
62
|
if (dryRun)
|
|
63
|
-
|
|
63
|
+
await runWithRequestStores(importNotes);
|
|
64
64
|
else
|
|
65
|
-
importNotes();
|
|
65
|
+
await importNotes();
|
|
66
66
|
// Deletion-sync: an artifactRef in the Map but not seen this run vanished from the source, so archive its raw row.
|
|
67
67
|
// Per-file archiveRaw, no outer SAVEPOINT: there is no cross-file idempotency row to commit atomically.
|
|
68
68
|
for (const [artifactRef, rows] of existing) {
|
|
@@ -71,7 +71,7 @@ function syncVaultFolder(folderPath, options, { vaultName, scope }) {
|
|
|
71
71
|
for (const row of rows) {
|
|
72
72
|
tally.archived++; // count even in dryRun so the preview reflects destructive deletes
|
|
73
73
|
if (!dryRun)
|
|
74
|
-
archiveRaw(ctx, row.id, `source_deleted:${artifactRef}`);
|
|
74
|
+
await archiveRaw(ctx, row.id, `source_deleted:${artifactRef}`);
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
const { total, imported, skipped, rejected, archived, redacted, entries } = tally;
|
|
@@ -120,7 +120,7 @@ function loadVaultRows(hippoRoot, tenantId, vaultName) {
|
|
|
120
120
|
}
|
|
121
121
|
return existing;
|
|
122
122
|
}
|
|
123
|
-
function importVaultNote(run, relpath) {
|
|
123
|
+
async function importVaultNote(run, relpath) {
|
|
124
124
|
const { tally } = run;
|
|
125
125
|
tally.total++;
|
|
126
126
|
const artifactRef = `vault:${run.vaultName}:${relpath}`;
|
|
@@ -137,7 +137,7 @@ function importVaultNote(run, relpath) {
|
|
|
137
137
|
// Empty or frontmatter-only note: nothing storable, and the content was deleted at source, so archive prior rows (the old body must not stay searchable)
|
|
138
138
|
// and skip the write. Safe because remember() is skipped, so there is no archive-then-throw hazard; the note stays in `seen` so deletion-sync skips it.
|
|
139
139
|
if (body.trim().length < 3) {
|
|
140
|
-
archivePriors(run, priors, `emptied:${artifactRef}`);
|
|
140
|
+
await archivePriors(run, priors, `emptied:${artifactRef}`);
|
|
141
141
|
tally.skipped++;
|
|
142
142
|
return;
|
|
143
143
|
}
|
|
@@ -147,13 +147,13 @@ function importVaultNote(run, relpath) {
|
|
|
147
147
|
tally.skipped++;
|
|
148
148
|
return;
|
|
149
149
|
}
|
|
150
|
-
writeChangedNote(run, artifactRef, priors, body, tags);
|
|
150
|
+
await writeChangedNote(run, artifactRef, priors, body, tags);
|
|
151
151
|
}
|
|
152
|
-
function writeChangedNote(run, artifactRef, priors, body, tags) {
|
|
152
|
+
async function writeChangedNote(run, artifactRef, priors, body, tags) {
|
|
153
153
|
const { ctx, tally } = run;
|
|
154
154
|
// Changed file: archive EVERY old raw row for this ref (>1 only after a concurrent double-insert), then append the new one; NEVER supersede.
|
|
155
155
|
// archiveRaw commits before remember() runs, so no double-live row; a crash between them self-heals on the next import.
|
|
156
|
-
archivePriors(run, priors, `changed:${artifactRef}`);
|
|
156
|
+
await archivePriors(run, priors, `changed:${artifactRef}`);
|
|
157
157
|
// remember() owns the write; the `echo` from createMemory only fills the ImportResult, with its id reconciled to the row that landed.
|
|
158
158
|
const content = vetSecrets(body, tags, true).content;
|
|
159
159
|
const echo = createMemory(content, {
|
|
@@ -165,7 +165,7 @@ function writeChangedNote(run, artifactRef, priors, body, tags) {
|
|
|
165
165
|
tenantId: ctx.tenantId,
|
|
166
166
|
baseHalfLifeDays: run.baseHalfLifeDays,
|
|
167
167
|
});
|
|
168
|
-
if (!storeVaultNote(run, echo, content, tags, artifactRef)) {
|
|
168
|
+
if (!(await storeVaultNote(run, echo, content, tags, artifactRef))) {
|
|
169
169
|
tally.rejected++;
|
|
170
170
|
return;
|
|
171
171
|
}
|
|
@@ -185,11 +185,11 @@ function readVaultNote(run, relpath) {
|
|
|
185
185
|
return null;
|
|
186
186
|
}
|
|
187
187
|
}
|
|
188
|
-
function archivePriors(run, priors, reason) {
|
|
188
|
+
async function archivePriors(run, priors, reason) {
|
|
189
189
|
for (const p of priors) {
|
|
190
190
|
run.tally.archived++; // count the would-be archive even in dryRun (true preview)
|
|
191
191
|
if (!run.dryRun)
|
|
192
|
-
archiveRaw(run.ctx, p.id, reason);
|
|
192
|
+
await archiveRaw(run.ctx, p.id, reason);
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
195
|
function vaultNoteTags(run, hashTag, fm, body) {
|
|
@@ -221,14 +221,14 @@ function vaultEnvelopeUnchanged(priors, tags, scope) {
|
|
|
221
221
|
}));
|
|
222
222
|
}
|
|
223
223
|
/** Returns false when the rejection guard refuses the note; any other error propagates. */
|
|
224
|
-
function storeVaultNote(run, echo, content, tags, artifactRef) {
|
|
224
|
+
async function storeVaultNote(run, echo, content, tags, artifactRef) {
|
|
225
225
|
// A dry run writes nothing; it asks the guard remember() uses, so its `rejected` count matches what a real run would refuse.
|
|
226
226
|
if (run.dryRun)
|
|
227
227
|
return !rejectionGuardRefuses(run.ctx.hippoRoot, run.ctx.tenantId, echo.id, echo.content);
|
|
228
228
|
try {
|
|
229
229
|
// A rejected note must not stop the scan or the deletion sync. Its priors are archived by now, so a rerun meets
|
|
230
230
|
// the same refusal and counts it again.
|
|
231
|
-
const result = remember(run.ctx, {
|
|
231
|
+
const result = await remember(run.ctx, {
|
|
232
232
|
content,
|
|
233
233
|
kind: 'raw',
|
|
234
234
|
artifactRef,
|
package/dist/index.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export { initStore } from './store/open.js';
|
|
|
20
20
|
export { writeEntry } from './store/entry-writes.js';
|
|
21
21
|
export { loadAllEntries, readEntry } from './store/entry-reads.js';
|
|
22
22
|
export { deleteEntry } from './store/delete-and-batch.js';
|
|
23
|
-
export { loadSearchEntries,
|
|
23
|
+
export { loadSearchEntries, loadRecallEntries, type RecallSearchEntriesOptions } from './store/search-rows.js';
|
|
24
24
|
export { loadIndex, rebuildIndex, loadSessionDecayContext, SessionDecayContext } from './store/index-and-stats.js';
|
|
25
25
|
export { saveActiveTaskSnapshot, loadActiveTaskSnapshot, loadFreshActiveTaskSnapshot, SNAPSHOT_AMBIENT_MAX_AGE_MS, closeTaskSnapshotsForSession, clearActiveTaskSnapshot, appendSessionEvent, listSessionEvents, type ContinuityKey, } from './store/sessions.js';
|
|
26
26
|
export { listMemoryConflicts, replaceDetectedConflicts, resolveConflict } from './store/conflicts.js';
|
|
@@ -36,7 +36,7 @@ export { isEmbeddingAvailable, getEmbedding } from './embeddings/local.js';
|
|
|
36
36
|
export { cosineSimilarity } from './store/embeddings/index.js';
|
|
37
37
|
export { embedMemory, embedAll } from './search/embed-memories.js';
|
|
38
38
|
export { loadEmbeddingIndex, saveEmbeddingIndex } from './store/vector-index.js';
|
|
39
|
-
export { captureError, extractLessons, partitionLessons, deduplicateLesson, runWatched, fetchGitLog, } from './learn/autolearn.js';
|
|
39
|
+
export { captureError, extractLessons, partitionLessons, deduplicateLesson, runWatched, type RunWatchedOptions, fetchGitLog, } from './learn/autolearn.js';
|
|
40
40
|
export { getGlobalRoot, initGlobal, promoteToGlobal } from './sharing/global-store.js';
|
|
41
41
|
export { searchBoth, searchBothHybrid, HybridSearchOptions } from './sharing/search-both.js';
|
|
42
42
|
export { syncGlobalToLocal } from './sharing/global-sync.js';
|
package/dist/index.js
CHANGED
|
@@ -21,7 +21,7 @@ export { initStore } from './store/open.js';
|
|
|
21
21
|
export { writeEntry } from './store/entry-writes.js';
|
|
22
22
|
export { loadAllEntries, readEntry } from './store/entry-reads.js';
|
|
23
23
|
export { deleteEntry } from './store/delete-and-batch.js';
|
|
24
|
-
export { loadSearchEntries,
|
|
24
|
+
export { loadSearchEntries, loadRecallEntries } from './store/search-rows.js';
|
|
25
25
|
export { loadIndex, rebuildIndex, loadSessionDecayContext } from './store/index-and-stats.js';
|
|
26
26
|
export { saveActiveTaskSnapshot, loadActiveTaskSnapshot, loadFreshActiveTaskSnapshot, SNAPSHOT_AMBIENT_MAX_AGE_MS, closeTaskSnapshotsForSession, clearActiveTaskSnapshot, appendSessionEvent, listSessionEvents, } from './store/sessions.js';
|
|
27
27
|
export { listMemoryConflicts, replaceDetectedConflicts, resolveConflict } from './store/conflicts.js';
|
|
@@ -23,9 +23,13 @@ export declare class RunWatchedTimeoutError extends Error {
|
|
|
23
23
|
readonly stderr: string;
|
|
24
24
|
constructor(deadlineMs: number, stderr: string);
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
export interface RunWatchedOptions {
|
|
27
|
+
/** Join argv with spaces and run it as one shell command line; only for text the caller wrote itself. */
|
|
28
|
+
shell?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** Run argv[0] with the rest as its arguments and no shell, so `;`, `&` and `$()` reach it literally; stderr streams live.
|
|
27
31
|
* Returns: { exitCode, stderr } with only the last 64 KiB of stderr; rejects with RunWatchedTimeoutError past `deadlineMs`. */
|
|
28
|
-
export declare function runWatched(
|
|
32
|
+
export declare function runWatched(argv: readonly string[], deadlineMs?: number, opts?: RunWatchedOptions): Promise<{
|
|
29
33
|
exitCode: number;
|
|
30
34
|
stderr: string;
|
|
31
35
|
}>;
|
package/dist/learn/autolearn.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** Auto-learn from errors and git history.
|
|
2
2
|
* Agents learn from failures without explicit hippo remember calls. */
|
|
3
3
|
import { execFileSync, spawn } from 'child_process';
|
|
4
|
+
import { spawnArgv } from '../util/spawn-argv.js';
|
|
4
5
|
import { createMemory, Layer, DEFAULT_HALF_LIFE_DAYS } from '../core/memory.js';
|
|
5
6
|
import { loadAllEntries } from '../store/entry-reads.js';
|
|
6
7
|
import { textOverlap } from '../util/tokenize.js';
|
|
@@ -112,20 +113,21 @@ export class RunWatchedTimeoutError extends Error {
|
|
|
112
113
|
this.name = 'RunWatchedTimeoutError';
|
|
113
114
|
}
|
|
114
115
|
}
|
|
115
|
-
/** Run
|
|
116
|
+
/** Run argv[0] with the rest as its arguments and no shell, so `;`, `&` and `$()` reach it literally; stderr streams live.
|
|
116
117
|
* Returns: { exitCode, stderr } with only the last 64 KiB of stderr; rejects with RunWatchedTimeoutError past `deadlineMs`. */
|
|
117
|
-
export function runWatched(
|
|
118
|
+
export function runWatched(argv, deadlineMs = RUN_WATCHED_DEADLINE_MS, opts = {}) {
|
|
118
119
|
return new Promise((resolve, reject) => {
|
|
119
|
-
|
|
120
|
-
const child = spawn(
|
|
120
|
+
const options = { stdio: ['inherit', 'inherit', 'pipe'], windowsHide: true };
|
|
121
|
+
const child = opts.shell ? spawn(argv.join(' '), { ...options, windowsHide: true, shell: true }) : spawnArgv(argv, options);
|
|
121
122
|
let tail = Buffer.alloc(0);
|
|
122
|
-
child.stderr
|
|
123
|
+
child.stderr?.on('data', (chunk) => {
|
|
123
124
|
tail = Buffer.concat([tail, chunk]);
|
|
124
125
|
if (tail.length > STDERR_TAIL_BYTES)
|
|
125
126
|
tail = tail.subarray(tail.length - STDERR_TAIL_BYTES);
|
|
126
127
|
process.stderr.write(chunk);
|
|
127
128
|
});
|
|
128
|
-
// SHORTCUT: kills the shell
|
|
129
|
+
// SHORTCUT: kills the direct child only (the shell or cmd.exe when there is one); on Windows a grandchild can outlive it.
|
|
130
|
+
// A process-tree kill is the upgrade path.
|
|
129
131
|
const timer = setTimeout(() => {
|
|
130
132
|
child.kill('SIGKILL');
|
|
131
133
|
reject(new RunWatchedTimeoutError(deadlineMs, tail.toString('utf8')));
|
package/dist/learn/extract.js
CHANGED
|
@@ -118,7 +118,7 @@ function buildExtractedEntry(fact, source, inheritedTags, baseHalfLifeDays) {
|
|
|
118
118
|
scope: source.scope,
|
|
119
119
|
// Without it createMemory stamps 'default', and extracted facts leave
|
|
120
120
|
// the tenant of the episodic memory they were extracted from.
|
|
121
|
-
tenantId: source.
|
|
121
|
+
tenantId: source.tenant_id,
|
|
122
122
|
baseHalfLifeDays,
|
|
123
123
|
}), origin_project: source.origin_project };
|
|
124
124
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type HippoStore } from '../store/port.js';
|
|
1
2
|
import { type MemoryEntry } from '../core/memory.js';
|
|
2
3
|
export interface InvalidationTarget {
|
|
3
4
|
from: string;
|
|
@@ -28,7 +29,7 @@ export declare function extractInvalidationTarget(message: string): Invalidation
|
|
|
28
29
|
export declare function invalidateMatching(hippoRoot: string, target: InvalidationTarget, tenantId?: string, options?: InvalidationOptions): InvalidationResult;
|
|
29
30
|
/** invalidateMatching for a batch: matches against `live`, the caller's list of the tenant's rows, and keeps it current.
|
|
30
31
|
* Each match is read again before it is weakened, so a row another writer changed since the list was read is never written back stale. */
|
|
31
|
-
export declare function invalidateMatchingAmong(
|
|
32
|
+
export declare function invalidateMatchingAmong(store: HippoStore, live: MemoryEntry[], target: InvalidationTarget, tenantId?: string): Promise<InvalidationResult>;
|
|
32
33
|
export interface ChurnRefs {
|
|
33
34
|
paths: string[];
|
|
34
35
|
symbols: string[];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { addTagToEntries, writeEntriesTogether } from '../store/entry-writes.js';
|
|
2
2
|
import { confirmedOutcomeTimes } from '../store/audit.js';
|
|
3
|
-
import { loadAllEntries,
|
|
3
|
+
import { loadAllEntries, loadEntriesMentioning, readEntry } from '../store/entry-reads.js';
|
|
4
|
+
import { requireGroup } from '../store/port.js';
|
|
4
5
|
import { chunked } from '../util/chunked.js';
|
|
5
6
|
import { CHURN_STALE_TAG } from '../core/memory.js';
|
|
6
7
|
import { GitReadError, gitLsFilesAtHead, fetchChurnWindowLog, gitGrepPresence, resolveCommitBefore, packageScriptsAt, } from './churn-git.js';
|
|
@@ -57,13 +58,17 @@ export function invalidateMatching(hippoRoot, target, tenantId, options) {
|
|
|
57
58
|
}
|
|
58
59
|
/** invalidateMatching for a batch: matches against `live`, the caller's list of the tenant's rows, and keeps it current.
|
|
59
60
|
* Each match is read again before it is weakened, so a row another writer changed since the list was read is never written back stale. */
|
|
60
|
-
export function invalidateMatchingAmong(
|
|
61
|
+
export async function invalidateMatchingAmong(store, live, target, tenantId) {
|
|
61
62
|
const seen = weakenMatches(live, target, { dryRun: true }).result;
|
|
62
63
|
const ids = [...seen.targets, ...seen.skippedPinned];
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
const fresh = new Map();
|
|
65
|
+
// The port reads at most 500 ids a call.
|
|
66
|
+
for (const chunk of chunked(ids, 500))
|
|
67
|
+
for (const entry of await store.entriesByIds(chunk, tenantId))
|
|
68
|
+
fresh.set(entry.id, entry);
|
|
65
69
|
const { result, weakened } = weakenMatches(ids.flatMap((id) => fresh.get(id) ?? []), target);
|
|
66
|
-
|
|
70
|
+
if (weakened.length > 0)
|
|
71
|
+
await requireGroup(store, 'lessonWrites').writeTogether(weakened);
|
|
67
72
|
const byId = new Map(weakened.map((entry) => [entry.id, entry]));
|
|
68
73
|
for (let i = 0; i < live.length; i++)
|
|
69
74
|
live[i] = byId.get(live[i].id) ?? live[i];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ToolCall } from './protocol.js';
|
|
2
2
|
export declare function runRememberTool({ args, ctx, hippoRoot, config, tenantId }: ToolCall): Promise<string>;
|
|
3
3
|
export declare function runOutcomeTool({ args, ctx, hippoRoot, tenantId }: ToolCall): Promise<string>;
|
|
4
|
-
export declare function runLearnTool({ args, ctx, hippoRoot, tenantId }: ToolCall): string
|
|
4
|
+
export declare function runLearnTool({ args, ctx, hippoRoot, tenantId }: ToolCall): Promise<string>;
|
|
5
5
|
//# sourceMappingURL=memory-tools.d.ts.map
|
package/dist/mcp/memory-tools.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Write-side tool handlers: hippo_remember, hippo_outcome and hippo_learn.
|
|
2
|
-
import {
|
|
2
|
+
import { hasGroup } from '../store/index.js';
|
|
3
3
|
import { startAutoSleepIfDue } from '../api/auto-sleep.js';
|
|
4
4
|
import { remember as apiRemember, outcome as apiOutcome, learn as apiLearn, MCP_LEARN } from '../api/index.js';
|
|
5
5
|
import { mcpActor } from './protocol.js';
|
|
@@ -14,7 +14,6 @@ export async function runRememberTool({ args, ctx, hippoRoot, config, tenantId }
|
|
|
14
14
|
if (args.tag)
|
|
15
15
|
tags.push(String(args.tag));
|
|
16
16
|
// Route through api/index.ts so audit_log captures the caller identity like CLI/REST: the auth-resolved ctx.actor under HTTP-MCP, 'mcp' for stdio.
|
|
17
|
-
// The entry is re-read to surface the half-life used in the MCP response.
|
|
18
17
|
const apiCtx = {
|
|
19
18
|
hippoRoot,
|
|
20
19
|
tenantId,
|
|
@@ -27,14 +26,12 @@ export async function runRememberTool({ args, ctx, hippoRoot, config, tenantId }
|
|
|
27
26
|
personal: args.personal === true,
|
|
28
27
|
project: ctx?.project,
|
|
29
28
|
});
|
|
30
|
-
const [entry] = await storeFor(apiCtx).entriesByIds([result.id], tenantId);
|
|
31
29
|
// Auto-sleep runs on hippo.db, so another store skips it.
|
|
32
|
-
const allowed = (ctx?.store === undefined || ctx.store
|
|
30
|
+
const allowed = (ctx?.store === undefined || hasGroup(ctx.store, 'localFiles')) && ctx?.autoSleep !== false;
|
|
33
31
|
startAutoSleepIfDue(hippoRoot, tenantId, config.autoSleep, allowed, ctx?.autoSleepRunner);
|
|
34
|
-
const
|
|
35
|
-
const tagStr = entry?.tags.join(', ') || tags.join(', ') || 'none';
|
|
32
|
+
const tagStr = result.tags.join(', ') || 'none';
|
|
36
33
|
const warnings = (result.warnings ?? []).map((w) => `\nWarning: ${w}`).join('');
|
|
37
|
-
return `Remembered [${result.id}] (half-life: ${
|
|
34
|
+
return `Remembered [${result.id}] (half-life: ${result.halfLifeDays}d, tags: ${tagStr})${warnings}`;
|
|
38
35
|
}
|
|
39
36
|
export async function runOutcomeTool({ args, ctx, hippoRoot, tenantId }) {
|
|
40
37
|
const good = Boolean(args.good);
|
|
@@ -53,10 +50,10 @@ export async function runOutcomeTool({ args, ctx, hippoRoot, tenantId }) {
|
|
|
53
50
|
const { applied } = await apiOutcome(apiCtx, ids, good);
|
|
54
51
|
return `Applied ${good ? 'positive' : 'negative'} outcome to ${applied} memories`;
|
|
55
52
|
}
|
|
56
|
-
export function runLearnTool({ args, ctx, hippoRoot, tenantId }) {
|
|
53
|
+
export async function runLearnTool({ args, ctx, hippoRoot, tenantId }) {
|
|
57
54
|
const days = Number(args.days) || 7;
|
|
58
55
|
// process.cwd() is the server's repo, not a remote caller's, so api learn refuses all but stdio and a host admin (mcpActor fails closed).
|
|
59
|
-
const result = apiLearn({ hippoRoot, tenantId, actor: mcpActor(ctx), store: ctx?.store }, { repoPath: process.cwd(), days, profile: MCP_LEARN });
|
|
56
|
+
const result = await apiLearn({ hippoRoot, tenantId, actor: mcpActor(ctx), store: ctx?.store }, { repoPath: process.cwd(), days, profile: MCP_LEARN });
|
|
60
57
|
if (result.status === 'not-a-repo')
|
|
61
58
|
return 'No git history found.';
|
|
62
59
|
if (result.status === 'no-commits')
|