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
package/dist/api/learn.js
CHANGED
|
@@ -2,23 +2,20 @@
|
|
|
2
2
|
import { ForbiddenError } from '../core/api-errors.js';
|
|
3
3
|
import { fetchGitLog, extractLessons, partitionLessons, isGitRepo } from '../learn/autolearn.js';
|
|
4
4
|
import { loadConfig } from '../core/config.js';
|
|
5
|
-
import {
|
|
5
|
+
import { runWithRequestStores } from '../db/request-stores.js';
|
|
6
6
|
import { embedMemory } from '../search/embed-memories.js';
|
|
7
7
|
import { extractInvalidationTarget, invalidateMatching, invalidateMatchingAmong, } from '../learn/invalidation.js';
|
|
8
8
|
import { createMemory, Layer, schemaFitFrom, schemaFitSourceOf } from '../core/memory.js';
|
|
9
9
|
import { extractPathTags } from '../search/path-context.js';
|
|
10
10
|
import { canReadScope, personalScopeOf } from '../core/recall-scope.js';
|
|
11
|
-
import { touchableScopeSql } from '../store/rule-sql.js';
|
|
12
11
|
import { duplicateKey, longestWord, storedTextKeys } from '../util/same-text.js';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { writeEntriesSkippingRejected } from '../store/entry-writes.js';
|
|
16
|
-
import { updateStats } from '../store/index-and-stats.js';
|
|
12
|
+
import { requireGroup } from '../store/index.js';
|
|
13
|
+
import { storeFor } from '../store/sqlite/store.js';
|
|
17
14
|
// SHORTCUT: two profiles keep each surface's rows as they were; fold MCP onto the CLI profile once it may invalidate.
|
|
18
15
|
export const CLI_LEARN = Object.freeze({ tags: Object.freeze(['error', 'git-learned']), source: 'git-learn', full: true });
|
|
19
16
|
export const MCP_LEARN = Object.freeze({ tags: Object.freeze(['git-learned']), source: 'git', full: false });
|
|
20
17
|
/** Learn from `repoPath`'s last `days` of commits into the caller's tenant. Host admin only. */
|
|
21
|
-
export function learn(ctx, opts) {
|
|
18
|
+
export async function learn(ctx, opts) {
|
|
22
19
|
// It reads the host's own git history, which belongs to the host, not to the caller's tenant.
|
|
23
20
|
if (!ctx.actor.hostAdmin)
|
|
24
21
|
throw new ForbiddenError('hippo_learn requires a host admin');
|
|
@@ -34,67 +31,71 @@ export function learn(ctx, opts) {
|
|
|
34
31
|
return { status: 'no-lessons', ...none };
|
|
35
32
|
// The gate filters the loop input, so a dropped lesson neither stores nor invalidates; docs/decisions git-learn-gate-placement says why.
|
|
36
33
|
const { kept, dropped } = partitionLessons(parsed);
|
|
37
|
-
return { status: 'scanned', lowInfo: dropped.length, ...writeLessons(ctx, kept, opts, config) };
|
|
34
|
+
return { status: 'scanned', lowInfo: dropped.length, ...await writeLessons(ctx, kept, opts, config) };
|
|
38
35
|
}
|
|
39
|
-
/** Writes `
|
|
40
|
-
function landLessons(
|
|
41
|
-
if (
|
|
36
|
+
/** Writes `queued` in one transaction; a refused lesson is dropped from `live` and `keys` and must not abort the rest of the git-log scan. */
|
|
37
|
+
async function landLessons(batch, queued) {
|
|
38
|
+
if (queued.length === 0)
|
|
42
39
|
return;
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
const { ctx, writes, live, keys, counts } = batch;
|
|
41
|
+
const written = new Set(await writes.writeSkippingRejected(queued.map((q) => q.entry), ctx.actor.subject));
|
|
45
42
|
const landed = [];
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
counts.rejected++;
|
|
53
|
-
keys.delete(key);
|
|
54
|
-
const at = live.indexOf(entry);
|
|
55
|
-
if (at !== -1)
|
|
56
|
-
live.splice(at, 1);
|
|
43
|
+
for (const { entry, key } of queued) {
|
|
44
|
+
if (written.has(entry.id)) {
|
|
45
|
+
landed.push(entry);
|
|
46
|
+
continue;
|
|
57
47
|
}
|
|
58
|
-
|
|
48
|
+
counts.rejected++;
|
|
49
|
+
keys.delete(key);
|
|
50
|
+
const at = live.indexOf(entry);
|
|
51
|
+
if (at !== -1)
|
|
52
|
+
live.splice(at, 1);
|
|
53
|
+
}
|
|
59
54
|
counts.added += landed.length;
|
|
60
|
-
if (!full)
|
|
55
|
+
if (!batch.full)
|
|
61
56
|
return;
|
|
62
57
|
if (landed.length > 0)
|
|
63
|
-
|
|
58
|
+
await writes.bumpRemembered(landed.length);
|
|
64
59
|
for (const entry of landed)
|
|
65
|
-
void embedMemory(ctx.hippoRoot, entry);
|
|
60
|
+
void embedMemory(ctx.hippoRoot, entry, undefined, ctx.store);
|
|
61
|
+
}
|
|
62
|
+
/** The duplicate keys a batch skips: every readable row's with `existing`, else only the rows holding a lesson's longest word. */
|
|
63
|
+
async function knownKeys(ctx, writes, lessons, existing) {
|
|
64
|
+
return storedTextKeys(existing ?? await writes.textsHoldingWords(ctx.tenantId, lessons.map(longestWord), personalScopeOf(ctx.actor)));
|
|
66
65
|
}
|
|
67
|
-
function writeLessons(ctx, lessons, opts, config) {
|
|
68
|
-
const {
|
|
66
|
+
async function writeLessons(ctx, lessons, opts, config) {
|
|
67
|
+
const { tenantId } = ctx;
|
|
69
68
|
const { profile } = opts;
|
|
69
|
+
const store = storeFor(ctx);
|
|
70
|
+
const writes = requireGroup(store, 'lessonWrites');
|
|
70
71
|
// `hippo init` calls this outside any request, so the batch brings its own scope: every read and write below shares one handle.
|
|
71
|
-
return
|
|
72
|
+
return runWithRequestStores(async () => {
|
|
72
73
|
// One read serves the batch: invalidation matches against `live`, which follows every write below, as a reload per lesson did.
|
|
73
|
-
const live = profile.full ?
|
|
74
|
+
const live = profile.full ? await writes.tenantEntries(tenantId) : [];
|
|
74
75
|
// Schema fit needs every row; without it, only rows holding a lesson's longest word can be its copy. Learn is host-admin only, so
|
|
75
76
|
// both read what an admin can.
|
|
76
77
|
const existing = profile.full ? live.filter((e) => e.scope == null || canReadScope(ctx.actor, e.scope)) : undefined;
|
|
77
78
|
// Each lesson is scored against `existing` as it was before the batch, so its tag counts and texts are read once.
|
|
78
79
|
const fitSource = existing && schemaFitSourceOf(existing);
|
|
79
|
-
const
|
|
80
|
-
|
|
80
|
+
const batch = {
|
|
81
|
+
ctx, writes, live, keys: await knownKeys(ctx, writes, lessons, existing), full: profile.full,
|
|
82
|
+
counts: { added: 0, skipped: 0, rejected: 0, invalidations: [] },
|
|
83
|
+
};
|
|
81
84
|
const pathTags = profile.full ? extractPathTags(opts.repoPath) : [];
|
|
82
|
-
const counts = { added: 0, skipped: 0, rejected: 0, invalidations: [] };
|
|
83
85
|
// Lessons queue and land in one transaction; an invalidation reads the store, so it flushes the queue first.
|
|
84
86
|
const queue = [];
|
|
85
|
-
const flush = () => landLessons(ctx, queue.splice(0), live, keys, counts, profile.full);
|
|
86
87
|
for (const lesson of lessons) {
|
|
87
88
|
const key = duplicateKey(lesson);
|
|
88
|
-
if (keys.has(key)) {
|
|
89
|
-
counts.skipped++;
|
|
89
|
+
if (batch.keys.has(key)) {
|
|
90
|
+
batch.counts.skipped++;
|
|
90
91
|
continue;
|
|
91
92
|
}
|
|
92
93
|
const target = profile.full ? extractInvalidationTarget(lesson) : null;
|
|
93
94
|
if (target) {
|
|
94
|
-
|
|
95
|
-
const { invalidated } = invalidateMatchingAmong(
|
|
95
|
+
await landLessons(batch, queue.splice(0));
|
|
96
|
+
const { invalidated } = await invalidateMatchingAmong(store, live, target, tenantId);
|
|
96
97
|
if (invalidated > 0)
|
|
97
|
-
counts.invalidations.push({ from: target.from, count: invalidated });
|
|
98
|
+
batch.counts.invalidations.push({ from: target.from, count: invalidated });
|
|
98
99
|
}
|
|
99
100
|
const entry = createMemory(lesson, {
|
|
100
101
|
layer: Layer.Episodic,
|
|
@@ -111,10 +112,10 @@ function writeLessons(ctx, lessons, opts, config) {
|
|
|
111
112
|
queue.push({ entry, key });
|
|
112
113
|
if (profile.full)
|
|
113
114
|
live.push(entry);
|
|
114
|
-
keys.add(key);
|
|
115
|
+
batch.keys.add(key);
|
|
115
116
|
}
|
|
116
|
-
|
|
117
|
-
return counts;
|
|
117
|
+
await landLessons(batch, queue.splice(0));
|
|
118
|
+
return batch.counts;
|
|
118
119
|
});
|
|
119
120
|
}
|
|
120
121
|
/** `hippo invalidate`: weakens the caller's tenant's memories that reference `target.from`, or only the one `options.onlyId` names. */
|
package/dist/api/memories.d.ts
CHANGED
|
@@ -14,8 +14,10 @@ export declare function getMemoryStatus(ctx: Context, now: Date, atRiskBelow: nu
|
|
|
14
14
|
export interface TenantReach {
|
|
15
15
|
everyTenant?: boolean;
|
|
16
16
|
}
|
|
17
|
-
/** Every memory row in reach, oldest first. */
|
|
18
|
-
export declare function listMemories(ctx: Context, reach?: TenantReach
|
|
17
|
+
/** Every memory row in reach, oldest first, read `pageSize` rows per query so no single read grows with the store. */
|
|
18
|
+
export declare function listMemories(ctx: Context, reach?: TenantReach, opts?: {
|
|
19
|
+
pageSize?: number;
|
|
20
|
+
}): MemoryEntry[];
|
|
19
21
|
/** How many memory rows the whole store holds, counted in SQL. */
|
|
20
22
|
export declare function countAllMemories(ctx: Context): number;
|
|
21
23
|
/** The rows in reach among `ids`; a missing id or one out of reach is absent. */
|
package/dist/api/memories.js
CHANGED
|
@@ -2,8 +2,8 @@ import { loadStrengthTallies } from '../store/candidates.js';
|
|
|
2
2
|
import { countOpenConflicts } from '../store/conflicts.js';
|
|
3
3
|
import { NotFoundError } from '../core/api-errors.js';
|
|
4
4
|
import { canTouchScope } from '../core/recall-scope.js';
|
|
5
|
-
import { storeFor } from '../store/
|
|
6
|
-
import { countAllEntries,
|
|
5
|
+
import { storeFor } from '../store/sqlite/store.js';
|
|
6
|
+
import { countAllEntries, loadEntriesByIds, loadEntriesPage } from '../store/entry-reads.js';
|
|
7
7
|
import { requireHostAdmin } from './types.js';
|
|
8
8
|
/** The row `id` in the caller's tenant, or null when it is missing or another tenant's. */
|
|
9
9
|
export async function getMemory(ctx, id) {
|
|
@@ -30,9 +30,20 @@ function tenantFilter(ctx, reach) {
|
|
|
30
30
|
requireHostAdmin(ctx, 'Reading every tenant');
|
|
31
31
|
return undefined;
|
|
32
32
|
}
|
|
33
|
-
/**
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
/** Rows a listing reads per query by default; the verbs still receive every row. */
|
|
34
|
+
const LIST_PAGE_SIZE = 1000;
|
|
35
|
+
/** Every memory row in reach, oldest first, read `pageSize` rows per query so no single read grows with the store. */
|
|
36
|
+
export function listMemories(ctx, reach = {}, opts = {}) {
|
|
37
|
+
const tenant = tenantFilter(ctx, reach);
|
|
38
|
+
const limit = Math.max(1, Math.trunc(opts.pageSize ?? LIST_PAGE_SIZE));
|
|
39
|
+
const all = [];
|
|
40
|
+
let after;
|
|
41
|
+
do {
|
|
42
|
+
const page = loadEntriesPage(ctx.hippoRoot, tenant, { limit, after });
|
|
43
|
+
all.push(...page.entries);
|
|
44
|
+
after = page.next ?? undefined;
|
|
45
|
+
} while (after);
|
|
46
|
+
return all;
|
|
36
47
|
}
|
|
37
48
|
/** How many memory rows the whole store holds, counted in SQL. */
|
|
38
49
|
export function countAllMemories(ctx) {
|
package/dist/api/objects.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { saveObject } from '../objects/lifecycle.js';
|
|
2
|
-
import { requireGroup
|
|
2
|
+
import { requireGroup } from '../store/index.js';
|
|
3
|
+
import { storeFor } from '../store/sqlite/store.js';
|
|
3
4
|
/** The request store's object group; the dispatcher has already answered 501 for a store without it. */
|
|
4
5
|
export function objectsOf(ctx) {
|
|
5
6
|
return requireGroup(storeFor(ctx), 'objects');
|
package/dist/api/outcome.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Context
|
|
1
|
+
import type { Context } from './types.js';
|
|
2
2
|
/** An id outside ctx.tenantId, or in someone else's personal scope, is skipped, so a stale id never fails the call.
|
|
3
3
|
* `appliedIds` holds only the ids applied: a caller that answers across tenants returns it, never the ids it was sent. */
|
|
4
4
|
export interface OutcomeResult {
|
|
@@ -6,14 +6,14 @@ export interface OutcomeResult {
|
|
|
6
6
|
appliedIds: string[];
|
|
7
7
|
}
|
|
8
8
|
/** `opts.traceId` links the outcome to the recall trace it judges. Only outcomeForLastRecall sends one: explicit ids may follow no recall at all. */
|
|
9
|
-
export declare function outcome
|
|
9
|
+
export declare function outcome(ctx: Context, ids: ReadonlyArray<string>, good: boolean, opts?: {
|
|
10
10
|
traceId?: number;
|
|
11
|
-
}):
|
|
12
|
-
/** The last recall's ids and its trace sit
|
|
11
|
+
}): Promise<OutcomeResult>;
|
|
12
|
+
/** The last recall's ids and its trace sit with the store's lastRecall group, which only the CLI and context write; a store without it is refused.
|
|
13
13
|
* `ids` holds only the ids applied in ctx.tenantId: the index is not tenant-scoped, so the ids read from it are never returned. */
|
|
14
14
|
export interface OutcomeForLastRecallResult {
|
|
15
15
|
applied: number;
|
|
16
16
|
ids: string[];
|
|
17
17
|
}
|
|
18
|
-
export declare function outcomeForLastRecall
|
|
18
|
+
export declare function outcomeForLastRecall(ctx: Context, good: boolean): Promise<OutcomeForLastRecallResult>;
|
|
19
19
|
//# sourceMappingURL=outcome.d.ts.map
|
package/dist/api/outcome.js
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
// Outcome feedback on recalled memories.
|
|
2
2
|
import { personalScopeOf } from '../core/recall-scope.js';
|
|
3
|
-
import {
|
|
3
|
+
import { requireGroup } from '../store/index.js';
|
|
4
|
+
import { storeFor } from '../store/sqlite/store.js';
|
|
4
5
|
/** `opts.traceId` links the outcome to the recall trace it judges. Only outcomeForLastRecall sends one: explicit ids may follow no recall at all. */
|
|
5
|
-
export function outcome(ctx, ids, good, opts) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
export async function outcome(ctx, ids, good, opts) {
|
|
7
|
+
const store = storeFor(ctx);
|
|
8
|
+
const entryWrites = requireGroup(store, 'entryWrites');
|
|
9
|
+
const write = { tenantId: ctx.tenantId, actor: ctx.actor.subject, ownScope: personalScopeOf(ctx.actor), ids, good };
|
|
10
|
+
const traceId = opts?.traceId;
|
|
11
|
+
// Recall traces live beside the last recall, so only a store with the lastRecall group can link one.
|
|
12
|
+
const appliedIds = traceId === undefined
|
|
13
|
+
? await entryWrites.applyOutcome(write)
|
|
14
|
+
: await requireGroup(store, 'lastRecall').applyTracedOutcome(write, traceId);
|
|
15
|
+
return { applied: appliedIds.length, appliedIds };
|
|
14
16
|
}
|
|
15
|
-
export function outcomeForLastRecall(ctx, good) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
});
|
|
17
|
+
export async function outcomeForLastRecall(ctx, good) {
|
|
18
|
+
const lastRecall = requireGroup(storeFor(ctx), 'lastRecall');
|
|
19
|
+
const ids = await lastRecall.applyOutcomeToLast({ tenantId: ctx.tenantId, actor: ctx.actor.subject, ownScope: personalScopeOf(ctx.actor) }, good);
|
|
20
|
+
return { applied: ids.length, ids };
|
|
20
21
|
}
|
|
21
22
|
//# sourceMappingURL=outcome.js.map
|
package/dist/api/predictions.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { predictionMirror } from '../store/predictions.js';
|
|
2
2
|
import { loadConfig } from '../core/config.js';
|
|
3
|
-
import { requireGroup
|
|
3
|
+
import { requireGroup } from '../store/index.js';
|
|
4
|
+
import { storeFor } from '../store/sqlite/store.js';
|
|
4
5
|
const predictionsOf = (ctx) => requireGroup(storeFor(ctx), 'predictions');
|
|
5
6
|
export async function savePrediction(ctx, input) {
|
|
6
7
|
return predictionsOf(ctx).savePrediction(ctx.tenantId, input, ctx.actor.subject);
|
package/dist/api/promote.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ConnectorEvent } from '../store/port.js';
|
|
2
2
|
import { type Layer } from '../core/memory.js';
|
|
3
|
-
import type { Context
|
|
3
|
+
import type { Context } from './types.js';
|
|
4
4
|
/** Copies a local memory into the global store; the inner writeEntry emits 'remember' there, and we add a 'promote' audit event so intent stays distinct.
|
|
5
5
|
* promoteToGlobal reads via `readEntry` with no tenant filter and keeps the entry's existing tenantId on the global side. */
|
|
6
6
|
export interface PromoteResult {
|
|
@@ -21,7 +21,7 @@ export interface SupersedeOverrides {
|
|
|
21
21
|
tags?: string[];
|
|
22
22
|
pinned?: boolean;
|
|
23
23
|
}
|
|
24
|
-
export declare function supersede
|
|
24
|
+
export declare function supersede(ctx: Context, oldId: string, newContent: string, overrides?: SupersedeOverrides): Promise<SupersedeResult>;
|
|
25
25
|
/** Archive a kind='raw' memory: its metadata moves to raw_archive and the row is
|
|
26
26
|
* deleted. The store writes the one archive_raw audit row, under the caller's subject. */
|
|
27
27
|
export interface ArchiveRawOpts {
|
|
@@ -32,5 +32,5 @@ export interface ArchiveRawResult {
|
|
|
32
32
|
ok: true;
|
|
33
33
|
archivedAt: string;
|
|
34
34
|
}
|
|
35
|
-
export declare function archiveRaw
|
|
35
|
+
export declare function archiveRaw(ctx: Context, id: string, reason: string, opts?: ArchiveRawOpts): Promise<ArchiveRawResult>;
|
|
36
36
|
//# sourceMappingURL=promote.d.ts.map
|
package/dist/api/promote.js
CHANGED
|
@@ -4,7 +4,8 @@ import { stampOriginProject } from '../store/entry-row.js';
|
|
|
4
4
|
import { createSuccessor } from '../core/memory.js';
|
|
5
5
|
import { promoteToGlobal } from '../sharing/global-store.js';
|
|
6
6
|
import { loadConfig } from '../core/config.js';
|
|
7
|
-
import {
|
|
7
|
+
import { requireGroup } from '../store/index.js';
|
|
8
|
+
import { storeFor } from '../store/sqlite/store.js';
|
|
8
9
|
import { memoryReach } from '../store/tenant-lookup.js';
|
|
9
10
|
import { canTouchScope, personalScopeOf } from '../core/recall-scope.js';
|
|
10
11
|
export function promote(ctx, id) {
|
|
@@ -22,23 +23,21 @@ export function promote(ctx, id) {
|
|
|
22
23
|
});
|
|
23
24
|
return { ok: true, sourceId: id, globalId: globalEntry.id };
|
|
24
25
|
}
|
|
25
|
-
export function supersede(ctx, oldId, newContent, overrides = {}) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
});
|
|
36
|
-
// A store writes origin_project as given, so the served folder's fallback is stamped here.
|
|
37
|
-
const successor = stampOriginProject(ctx.hippoRoot, newEntry);
|
|
38
|
-
const write = { tenantId: ctx.tenantId, actor: ctx.actor.subject, ownScope: personalScopeOf(ctx.actor), oldId, successor };
|
|
39
|
-
return andThen(entryWrites.supersede(write), () => ({ ok: true, oldId, newId: successor.id }));
|
|
40
|
-
});
|
|
26
|
+
export async function supersede(ctx, oldId, newContent, overrides = {}) {
|
|
27
|
+
const store = storeFor(ctx);
|
|
28
|
+
const entryWrites = requireGroup(store, 'entryWrites');
|
|
29
|
+
const [old] = await store.entriesByIds([oldId], ctx.tenantId);
|
|
30
|
+
const newEntry = createSuccessor(assertSupersedable(ctx, oldId, old ?? null), newContent, {
|
|
31
|
+
tenantId: ctx.tenantId,
|
|
32
|
+
baseHalfLifeDays: loadConfig(ctx.hippoRoot).defaultHalfLifeDays,
|
|
33
|
+
layer: overrides.layer,
|
|
34
|
+
tags: overrides.tags,
|
|
35
|
+
pinned: overrides.pinned,
|
|
41
36
|
});
|
|
37
|
+
// A store writes origin_project as given, so the served folder's fallback is stamped here.
|
|
38
|
+
const successor = stampOriginProject(ctx.hippoRoot, newEntry);
|
|
39
|
+
await entryWrites.supersede({ tenantId: ctx.tenantId, actor: ctx.actor.subject, ownScope: personalScopeOf(ctx.actor), oldId, successor });
|
|
40
|
+
return { ok: true, oldId, newId: successor.id };
|
|
42
41
|
}
|
|
43
42
|
/** The tenant-scoped row to supersede; another tenant's id, or someone else's personal row, reads as not found. */
|
|
44
43
|
function assertSupersedable(ctx, oldId, old) {
|
|
@@ -51,15 +50,14 @@ function assertSupersedable(ctx, oldId, old) {
|
|
|
51
50
|
}
|
|
52
51
|
return old;
|
|
53
52
|
}
|
|
54
|
-
export function archiveRaw(ctx, id, reason, opts = {}) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
});
|
|
53
|
+
export async function archiveRaw(ctx, id, reason, opts = {}) {
|
|
54
|
+
const store = storeFor(ctx);
|
|
55
|
+
const entryWrites = requireGroup(store, 'entryWrites');
|
|
56
|
+
const archive = { tenantId: ctx.tenantId, actor: ctx.actor.subject, ownScope: personalScopeOf(ctx.actor), id, reason };
|
|
57
|
+
const { event } = opts;
|
|
58
|
+
const archivedAt = event
|
|
59
|
+
? await requireGroup(store, 'connectorWrites').archiveConnectorEntry({ ...archive, event })
|
|
60
|
+
: await entryWrites.archiveRaw(archive);
|
|
61
|
+
return { ok: true, archivedAt };
|
|
64
62
|
}
|
|
65
63
|
//# sourceMappingURL=promote.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Context, type ContextResult, type ContextResultEntry } from './index.js';
|
|
2
|
-
import { type DeliveryEventType, type DeliveryRecorder } from '../store/delivery-recorder.js';
|
|
2
|
+
import { type DeliveryEventType, type DeliveryFault, type DeliveryRecorder } from '../store/delivery-recorder.js';
|
|
3
3
|
import type { HookRuntime } from '../core/capture-contract.js';
|
|
4
4
|
import { type JsonValue } from '../util/json.js';
|
|
5
5
|
import type { MemoryEntry } from '../core/memory.js';
|
|
@@ -12,6 +12,8 @@ export interface DeliveryStart {
|
|
|
12
12
|
eventType?: DeliveryEventType;
|
|
13
13
|
/** The host's own session id from its environment, used when the payload names none. */
|
|
14
14
|
envSessionId: string | undefined;
|
|
15
|
+
/** Passed through to the recorder; only tests set it. */
|
|
16
|
+
fault?: DeliveryFault;
|
|
15
17
|
}
|
|
16
18
|
/** A delivery recorder for a hook on `ctx.hippoRoot` when the ledger store enables one, else null; never throws. */
|
|
17
19
|
export declare function startDeliveryRecorder(ctx: Context, start: DeliveryStart): DeliveryRecorder | null;
|
package/dist/api/prompt-hook.js
CHANGED
package/dist/api/quarantine.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Quarantine review: list, approve and reject held memories.
|
|
2
2
|
import { ConflictError, ForbiddenError, NotFoundError } from '../core/api-errors.js';
|
|
3
|
-
import { requireGroup
|
|
3
|
+
import { requireGroup } from '../store/index.js';
|
|
4
|
+
import { storeFor } from '../store/sqlite/store.js';
|
|
4
5
|
const QUARANTINE_PREVIEW_CHARS = 200;
|
|
5
6
|
/** A tenant's quarantined memories, newest first. Default `status` is 'pending' (the review queue). */
|
|
6
7
|
export async function quarantineList(ctx, opts = {}) {
|
package/dist/api/recall-core.js
CHANGED
|
@@ -5,7 +5,7 @@ import { decidePlanningFallacy, detectPlanningClaim } from '../predictions/plann
|
|
|
5
5
|
import { rankRecall } from './recall-pipeline.js';
|
|
6
6
|
import { writeRecallTraceAtRoot } from '../store/recall-trace.js';
|
|
7
7
|
import { saveIndex } from '../store/index-and-stats.js';
|
|
8
|
-
import {
|
|
8
|
+
import { storeFor } from '../store/sqlite/store.js';
|
|
9
9
|
import { estimateTokens } from '../util/token-text.js';
|
|
10
10
|
import { callerOf, recallAuditMetadata, recallAuditRow, strengthenOf } from './recall-record.js';
|
|
11
11
|
import { RECALL_RECORDING, recordShownRecall } from './recall-finish.js';
|
|
@@ -58,7 +58,7 @@ async function recordShown(ctx, store, opts, core, done) {
|
|
|
58
58
|
const { ranking, shown, planningAudit } = done;
|
|
59
59
|
const ids = shown.results.map((r) => r.entry.id);
|
|
60
60
|
const recallRow = recallAuditRow(callerOf(ctx), 'recall', undefined, recallAuditMetadata(opts.query, ids.length));
|
|
61
|
-
const second =
|
|
61
|
+
const second = secondStore(ctx, core);
|
|
62
62
|
const heldElsewhere = second !== undefined ? await idsNotHeld(store, ids, ctx.tenantId) : [];
|
|
63
63
|
const audited = await finishOrReport(store, {
|
|
64
64
|
goalLog: ranking.goalRecallLog,
|
|
@@ -68,7 +68,7 @@ async function recordShown(ctx, store, opts, core, done) {
|
|
|
68
68
|
if (audited) {
|
|
69
69
|
// The second store logs that it was searched, and strengthens only the rows the first does not hold.
|
|
70
70
|
if (second !== undefined)
|
|
71
|
-
await finishOrReport(
|
|
71
|
+
await finishOrReport(second, { goalLog: [], audit: [recallRow], strengthen: strengthenOf(ctx, heldElsewhere) });
|
|
72
72
|
traceAndMark(ctx, opts, core, shown.results);
|
|
73
73
|
}
|
|
74
74
|
// The caller prints whether or not the record was written, so the ring and the ledger still take this list; `written` holds back the count.
|
|
@@ -83,6 +83,11 @@ async function recordShown(ctx, store, opts, core, done) {
|
|
|
83
83
|
written: audited,
|
|
84
84
|
});
|
|
85
85
|
}
|
|
86
|
+
/** The second store's writes go where the first's do: the store named for its root, else hippo.db under that root. */
|
|
87
|
+
function secondStore(ctx, core) {
|
|
88
|
+
const root = core.sources?.globalRoot;
|
|
89
|
+
return root !== undefined && root !== ctx.hippoRoot ? storeFor({ hippoRoot: root, store: core.sources?.globalStore }) : undefined;
|
|
90
|
+
}
|
|
86
91
|
// The caller has ranked and is about to print, so a failed audit write is logged and counted, never thrown.
|
|
87
92
|
async function finishOrReport(store, writes) {
|
|
88
93
|
try {
|
|
@@ -13,10 +13,10 @@ import { multihopSearch } from '../search/multihop.js';
|
|
|
13
13
|
import { passesCliRecallScopeFilter } from '../core/recall-scope.js';
|
|
14
14
|
import { currentEntries } from '../search/as-of.js';
|
|
15
15
|
import { STRENGTH_RANK_FLOOR, STRENGTH_RANK_SPAN } from '../search/boosts.js';
|
|
16
|
-
import { hybridSearch } from '../search/hybrid.js';
|
|
17
|
-
import {
|
|
16
|
+
import { hybridSearch, vectorSourceOf } from '../search/hybrid.js';
|
|
17
|
+
import { searchByEngine } from '../search/engine.js';
|
|
18
18
|
import { rankBothStores } from '../sharing/search-both.js';
|
|
19
|
-
import {
|
|
19
|
+
import { loadRecallEntries, recallScopeFilter } from '../store/search-rows.js';
|
|
20
20
|
import { isJsonNumber } from '../util/json.js';
|
|
21
21
|
import { textOverlap, tokenize as tokenizeQuery } from '../util/tokenize.js';
|
|
22
22
|
/** Ranks memories for a query as `hippo recall` does, through the `limit` slice. Reads stores, embeddings and physics state; writes and prints nothing
|
|
@@ -65,10 +65,9 @@ function loadRecallPool(ctx, opts) {
|
|
|
65
65
|
// An explicit --scope unlocks that envelope scope; the regex-only `<source>:private:*` deny is the JS half below.
|
|
66
66
|
const requested = opts.explicitScope || undefined;
|
|
67
67
|
const loadSuperseded = opts.includeSuperseded || Boolean(opts.asOf);
|
|
68
|
-
|
|
69
|
-
let
|
|
70
|
-
|
|
71
|
-
: [];
|
|
68
|
+
const load = { tenantId: ctx.tenantId, requestedScope: requested, explicitScopeMode: 'additive', includeSuperseded: loadSuperseded };
|
|
69
|
+
let local = loadRecallEntries(ctx.hippoRoot, opts.query, load);
|
|
70
|
+
let global = ctx.globalRoot ? loadRecallEntries(ctx.globalRoot, opts.query, load) : [];
|
|
72
71
|
// SQL-excluded rows are pre-candidate, so the total is taken before the JS filters, which normally drop nothing.
|
|
73
72
|
const total = local.length + global.length;
|
|
74
73
|
const passes = (e) => passesCliRecallScopeFilter(e.scope ?? null, requested);
|
|
@@ -109,13 +108,6 @@ async function searchPool(ctx, opts, pool) {
|
|
|
109
108
|
includeSuperseded: includeSuperseded || Boolean(asOf),
|
|
110
109
|
admit: (e) => passesCliRecallScopeFilter(e.scope ?? null, requested),
|
|
111
110
|
};
|
|
112
|
-
if (search.usePhysics && !globalRoot) {
|
|
113
|
-
// Explain leaves minResults and includeSuperseded out to hold its output steady; asOf must reach physics or later rows leak.
|
|
114
|
-
const temporal = explain ? { asOf } : { minResults, includeSuperseded, asOf };
|
|
115
|
-
return physicsSearch(query, pool.local, {
|
|
116
|
-
budget, cost, hippoRoot: ctx.hippoRoot, physicsConfig: search.physicsConfig, scope, explain, vectorCandidates, ...temporal
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
111
|
if (globalRoot) {
|
|
120
112
|
// The pool already holds searchBothHybrid's additive-scope load for each store, so rank it rather than read both stores again.
|
|
121
113
|
return rankBothStores(query, { local: ctx.hippoRoot, global: globalRoot }, { local: pool.local, global: pool.global }, vectorCandidates, {
|
|
@@ -123,9 +115,17 @@ async function searchPool(ctx, opts, pool) {
|
|
|
123
115
|
includeSuperseded, asOf,
|
|
124
116
|
});
|
|
125
117
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
118
|
+
const physics = search.usePhysics ? search.physicsConfig : null;
|
|
119
|
+
// Physics takes no MMR; explain drops minResults and includeSuperseded to hold its output steady; asOf must reach physics or later rows leak.
|
|
120
|
+
const tuning = !physics ? { mmr, mmrLambda, minResults, includeSuperseded, asOf } : explain ? { asOf } : { minResults, includeSuperseded, asOf };
|
|
121
|
+
return searchByEngine(hippoDbSource(ctx.hippoRoot), query, pool.local, physics, { budget, cost, scope, explain, vectorCandidates, ...tuning });
|
|
122
|
+
}
|
|
123
|
+
/** The CLI core holds no store: it ranks on hippo.db under its root, as its loads above do. */
|
|
124
|
+
function hippoDbSource(hippoRoot) {
|
|
125
|
+
const source = vectorSourceOf({ hippoRoot });
|
|
126
|
+
if (!source)
|
|
127
|
+
throw new Error('The CLI ranking core needs a hippo root to rank on');
|
|
128
|
+
return source;
|
|
129
129
|
}
|
|
130
130
|
/** Adds memories reached by walking the entity graph out from the lexical seeds. */
|
|
131
131
|
function expandGraph(ctx, opts, graph, state) {
|
|
@@ -5,6 +5,7 @@ import type { PlanningFallacyHint, PlanningFallacyOutput, PlanningFallacyWatchin
|
|
|
5
5
|
import type { AnchoringHint, RecallHistorySnapshot } from './recall-history.js';
|
|
6
6
|
import type { AvailabilityHint } from './availability.js';
|
|
7
7
|
import type { AppendAuditOpts } from '../store/audit.js';
|
|
8
|
+
import type { HippoStore } from '../store/port.js';
|
|
8
9
|
import type { CallerProject } from './prompt-hook.js';
|
|
9
10
|
import type { RankRecallOpts, RankRecallResult } from './recall-pipeline.js';
|
|
10
11
|
import type { RecallRecording } from './recall-finish.js';
|
|
@@ -60,9 +61,10 @@ export interface RecallOpts {
|
|
|
60
61
|
}
|
|
61
62
|
interface CliCoreRanking {
|
|
62
63
|
rank: Omit<RankRecallOpts, 'query' | 'sessionId' | 'goalTag'>;
|
|
63
|
-
/** A second store searched beside `ctx.hippoRoot`. */
|
|
64
|
+
/** A second store searched beside `ctx.hippoRoot`; its recall rows go to `globalStore`, else hippo.db under `globalRoot`. */
|
|
64
65
|
sources?: {
|
|
65
66
|
globalRoot?: string;
|
|
67
|
+
globalStore?: HippoStore;
|
|
66
68
|
};
|
|
67
69
|
/** Receives each operator note when ranking reaches it. */
|
|
68
70
|
note?: (line: string) => void;
|