hippo-memory 1.16.0 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/hippo.js +2 -2
- package/dist/api.d.ts +28 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +31 -7
- package/dist/api.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +107 -12
- package/dist/cli.js.map +1 -1
- package/dist/connectors/github/backfill.js +4 -4
- package/dist/connectors/github/cli-impl.js +6 -6
- package/dist/connectors/github/dlq.js +14 -14
- package/dist/connectors/slack/backfill.js +1 -1
- package/dist/connectors/slack/dlq.js +10 -10
- package/dist/connectors/slack/workspaces.js +4 -4
- package/dist/dag.js +6 -6
- package/dist/dashboard.js +7 -7
- package/dist/goals.d.ts +11 -0
- package/dist/goals.d.ts.map +1 -1
- package/dist/goals.js +61 -49
- package/dist/goals.js.map +1 -1
- package/dist/graph-extract.d.ts +28 -12
- package/dist/graph-extract.d.ts.map +1 -1
- package/dist/graph-extract.js +138 -20
- package/dist/graph-extract.js.map +1 -1
- package/dist/hooks.js +24 -24
- package/dist/physics-state.js +27 -27
- package/dist/predictions.js +67 -67
- package/dist/refine-llm.js +13 -13
- package/dist/search.d.ts +33 -0
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +7 -0
- package/dist/server.js.map +1 -1
- package/dist/src/api.js +31 -7
- package/dist/src/api.js.map +1 -1
- package/dist/src/cli.js +107 -12
- package/dist/src/cli.js.map +1 -1
- package/dist/src/connectors/github/backfill.js +4 -4
- package/dist/src/connectors/github/cli-impl.js +6 -6
- package/dist/src/connectors/github/dlq.js +14 -14
- package/dist/src/connectors/slack/backfill.js +1 -1
- package/dist/src/connectors/slack/dlq.js +10 -10
- package/dist/src/connectors/slack/workspaces.js +4 -4
- package/dist/src/dag.js +6 -6
- package/dist/src/dashboard.js +7 -7
- package/dist/src/goals.js +61 -49
- package/dist/src/goals.js.map +1 -1
- package/dist/src/graph-extract.js +138 -20
- package/dist/src/graph-extract.js.map +1 -1
- package/dist/src/hooks.js +24 -24
- package/dist/src/physics-state.js +27 -27
- package/dist/src/predictions.js +67 -67
- package/dist/src/refine-llm.js +13 -13
- package/dist/src/search.js.map +1 -1
- package/dist/src/server.js +7 -0
- package/dist/src/server.js.map +1 -1
- package/dist/src/store.js +260 -260
- package/dist/src/version.js +1 -1
- package/dist/src/working-memory.js +19 -19
- package/dist/store.js +260 -260
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/working-memory.js +19 -19
- package/dist-ui/index.html +12 -12
- package/extensions/openclaw-plugin/index.ts +650 -650
- package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
- package/extensions/openclaw-plugin/package.json +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/dist/benchmarks/e1.3/scenarios.json +0 -2587
package/dist/graph-extract.js
CHANGED
|
@@ -3,19 +3,22 @@
|
|
|
3
3
|
* (docs/plans/2026-06-01-e3-deterministic-extraction.md).
|
|
4
4
|
*
|
|
5
5
|
* Populates the E3 graph from the already-structured consolidated E2-object tables -
|
|
6
|
-
* NO NLP, no precision gate. The graph is a pure derived
|
|
7
|
-
* state, so `extractGraph` is an idempotent REBUILD: clear
|
|
8
|
-
* re-derive entities + `supersedes` relations from decisions /
|
|
9
|
-
* customer_notes / project_briefs (the four E2 types whose kind maps to the
|
|
10
|
-
* `entity_type` enum). All writes go through the src/graph.ts consolidated-source
|
|
11
|
-
*
|
|
6
|
+
* NO NLP, no precision gate for entities + supersedes. The graph is a pure derived
|
|
7
|
+
* function of the current E2 state, so `extractGraph` is an idempotent REBUILD: clear
|
|
8
|
+
* the tenant's graph, then re-derive entities + `supersedes` relations from decisions /
|
|
9
|
+
* policies / customer_notes / project_briefs (the four E2 types whose kind maps to the
|
|
10
|
+
* `entity_type` enum). All writes go through the src/graph.ts consolidated-source guard
|
|
11
|
+
* (insertEntity / insertRelation / clearGraph); this module issues no raw SQL.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
13
|
+
* Pass 3 (E3 cross-object, docs/plans/2026-06-02-e3-cross-object-references.md) adds the
|
|
14
|
+
* first CROSS-OBJECT relations: a deterministic NAME-MATCH heuristic that emits a
|
|
15
|
+
* `references` edge when one consolidated object's text contains another entity's name.
|
|
16
|
+
* It is conservative (word-boundary, length-bounded, ambiguity-guarded, per-source
|
|
17
|
+
* capped); its precision is measured + reported, not assumed.
|
|
18
|
+
*
|
|
19
|
+
* Deferred (follow-ups): NLP prose-extraction (semantic depends-on / blocked-by / owns);
|
|
20
|
+
* skill/incident/process entities (not in the entity_type enum - needs a migration); the
|
|
21
|
+
* `hippo sleep` enqueue-hook.
|
|
19
22
|
*/
|
|
20
23
|
import { clearGraph, insertEntity, insertRelation, MAX_ENTITY_NAME_LEN } from './graph.js';
|
|
21
24
|
import { loadDecisions } from './decisions.js';
|
|
@@ -27,10 +30,29 @@ import { assertTenantId } from './store.js';
|
|
|
27
30
|
* set exceeds this is truncated; `ExtractResult.truncated` records it so the
|
|
28
31
|
* incompleteness is observable rather than silent. */
|
|
29
32
|
export const MAX_EXTRACT_PER_TYPE = 10000;
|
|
33
|
+
// --- Pass 3 (cross-object references) tunables ------------------------------------
|
|
34
|
+
/** A target entity name must be in [MIN, MAX] chars to be matched: MIN skips short /
|
|
35
|
+
* generic words; MAX skips prose (a decision's prose name is never a target, only a
|
|
36
|
+
* source). */
|
|
37
|
+
export const MIN_REF_NAME_LEN = 4;
|
|
38
|
+
export const MAX_REF_NAME_LEN = 80;
|
|
39
|
+
/** Per-source cap so one object cannot explode the graph with references edges. */
|
|
40
|
+
export const MAX_REFERENCES_PER_OBJECT = 25;
|
|
41
|
+
/** Regex-size bound: at most this many distinct target names enter the combined
|
|
42
|
+
* alternation, keeping the scan regex sane on a huge store. */
|
|
43
|
+
export const MAX_TARGET_NAMES = 5000;
|
|
30
44
|
/** Stable map key: entity types share an id space across tables, so key by both. */
|
|
31
45
|
function keyOf(entityType, e2Id) {
|
|
32
46
|
return `${entityType}:${e2Id}`;
|
|
33
47
|
}
|
|
48
|
+
/** Escape a string for safe use as a literal inside a RegExp alternation. */
|
|
49
|
+
function escapeRegex(s) {
|
|
50
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
51
|
+
}
|
|
52
|
+
/** Unordered entity-id pair key, so a relation between a,b is found in either direction. */
|
|
53
|
+
function pairKey(a, b) {
|
|
54
|
+
return a < b ? `${a}:${b}` : `${b}:${a}`;
|
|
55
|
+
}
|
|
34
56
|
/**
|
|
35
57
|
* Load a type's ACTIVE + SUPERSEDED rows (excluding `closed` = retired), normalised.
|
|
36
58
|
* Calls the loader once per status so MAX_EXTRACT_PER_TYPE is a per-status budget
|
|
@@ -40,7 +62,9 @@ function loadType(hippoRoot, tenantId, entityType,
|
|
|
40
62
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
41
63
|
loadFn,
|
|
42
64
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
43
|
-
nameOf
|
|
65
|
+
nameOf,
|
|
66
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
67
|
+
textOf) {
|
|
44
68
|
const rows = [];
|
|
45
69
|
let hitCap = false;
|
|
46
70
|
for (const status of ['active', 'superseded']) {
|
|
@@ -52,6 +76,7 @@ nameOf) {
|
|
|
52
76
|
entityType,
|
|
53
77
|
e2Id: r.id,
|
|
54
78
|
name: nameOf(r),
|
|
79
|
+
searchText: textOf(r),
|
|
55
80
|
memoryId: (r.memoryId ?? null),
|
|
56
81
|
supersededBy: (r.supersededBy ?? null),
|
|
57
82
|
});
|
|
@@ -67,10 +92,10 @@ nameOf) {
|
|
|
67
92
|
export function extractGraph(hippoRoot, tenantId) {
|
|
68
93
|
assertTenantId('extractGraph', tenantId);
|
|
69
94
|
const sources = [
|
|
70
|
-
{ entityType: 'decision', loadFn: loadDecisions, nameOf: (r) => r.decisionText },
|
|
71
|
-
{ entityType: 'policy', loadFn: loadPolicies, nameOf: (r) => r.policyName },
|
|
72
|
-
{ entityType: 'customer', loadFn: loadCustomerNotes, nameOf: (r) => r.customer },
|
|
73
|
-
{ entityType: 'project', loadFn: loadProjectBriefs, nameOf: (r) => r.repo },
|
|
95
|
+
{ entityType: 'decision', loadFn: loadDecisions, nameOf: (r) => r.decisionText, textOf: (r) => [r.decisionText, r.context].filter(Boolean).join(' ') },
|
|
96
|
+
{ entityType: 'policy', loadFn: loadPolicies, nameOf: (r) => r.policyName, textOf: (r) => [r.policyName, r.policyText].filter(Boolean).join(' ') },
|
|
97
|
+
{ entityType: 'customer', loadFn: loadCustomerNotes, nameOf: (r) => r.customer, textOf: (r) => [r.customer, r.note].filter(Boolean).join(' ') },
|
|
98
|
+
{ entityType: 'project', loadFn: loadProjectBriefs, nameOf: (r) => r.repo, textOf: (r) => [r.repo, r.summary].filter(Boolean).join(' ') },
|
|
74
99
|
];
|
|
75
100
|
// Rebuild from scratch: the graph is derived, so clear then re-derive.
|
|
76
101
|
clearGraph(hippoRoot, tenantId);
|
|
@@ -79,10 +104,12 @@ export function extractGraph(hippoRoot, tenantId) {
|
|
|
79
104
|
const allRows = [];
|
|
80
105
|
const entityIdByKey = new Map();
|
|
81
106
|
const memoryIdByKey = new Map();
|
|
107
|
+
// Created entities ONLY (drives Pass 3 sources + targets), in stable insertion order.
|
|
108
|
+
const created = [];
|
|
82
109
|
// Pass 1: entities. Skip rows whose source memory was forgotten (NULL memory_id) -
|
|
83
110
|
// an entity must reference a consolidated memory (entities.memory_id is NOT NULL).
|
|
84
111
|
for (const src of sources) {
|
|
85
|
-
const { rows, hitCap } = loadType(hippoRoot, tenantId, src.entityType, src.loadFn, src.nameOf);
|
|
112
|
+
const { rows, hitCap } = loadType(hippoRoot, tenantId, src.entityType, src.loadFn, src.nameOf, src.textOf);
|
|
86
113
|
if (hitCap)
|
|
87
114
|
truncated.push(src.entityType);
|
|
88
115
|
byType[src.entityType] = 0;
|
|
@@ -110,6 +137,7 @@ export function extractGraph(hippoRoot, tenantId) {
|
|
|
110
137
|
const k = keyOf(row.entityType, row.e2Id);
|
|
111
138
|
entityIdByKey.set(k, entity.id);
|
|
112
139
|
memoryIdByKey.set(k, row.memoryId);
|
|
140
|
+
created.push({ entityId: entity.id, entityType: row.entityType, memoryId: row.memoryId, name, searchText: row.searchText ?? '', superseded: row.supersededBy !== null });
|
|
113
141
|
byType[src.entityType] += 1;
|
|
114
142
|
}
|
|
115
143
|
}
|
|
@@ -117,6 +145,11 @@ export function extractGraph(hippoRoot, tenantId) {
|
|
|
117
145
|
// "Y supersedes X" - but only when BOTH X and Y were extracted (e.g. Y may be closed
|
|
118
146
|
// and absent). The relation is sourced from Y's consolidated memory.
|
|
119
147
|
let relations = 0;
|
|
148
|
+
// Entity-id pairs already related by supersedes (unordered). Pass 3 skips a references
|
|
149
|
+
// edge for such a pair: a version-extends-its-predecessor's-name containment (e.g.
|
|
150
|
+
// "Adopt X (managed)" contains "Adopt X") is a name artifact, not a cross-reference,
|
|
151
|
+
// and supersedes already captures their relationship.
|
|
152
|
+
const supersededPairs = new Set();
|
|
120
153
|
for (const row of allRows) {
|
|
121
154
|
if (row.supersededBy === null)
|
|
122
155
|
continue;
|
|
@@ -133,9 +166,94 @@ export function extractGraph(hippoRoot, tenantId) {
|
|
|
133
166
|
relType: 'supersedes',
|
|
134
167
|
memoryId: yMemoryId,
|
|
135
168
|
});
|
|
169
|
+
supersededPairs.add(pairKey(fromId, toId));
|
|
136
170
|
relations += 1;
|
|
137
171
|
}
|
|
138
|
-
|
|
139
|
-
|
|
172
|
+
// Pass 3: cross-object `references` edges via conservative name matching. A source's
|
|
173
|
+
// text containing a target entity's name -> "source references target". Sources +
|
|
174
|
+
// targets are CREATED entities only, so insertRelation never sees a null source memory.
|
|
175
|
+
const references = extractReferences(hippoRoot, tenantId, created, supersededPairs, truncated);
|
|
176
|
+
relations += references;
|
|
177
|
+
const entities = created.length;
|
|
178
|
+
return { entities, relations, references, byType, truncated };
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Pass 3. Build a target-name index from the created entities (names within the length
|
|
182
|
+
* bounds, ambiguous names dropped), scan each created entity's text once with one
|
|
183
|
+
* combined word-boundary regex, and emit `references` edges (self-skipped, deduped,
|
|
184
|
+
* per-source capped). Returns the number of references edges written.
|
|
185
|
+
*/
|
|
186
|
+
function extractReferences(hippoRoot, tenantId, created, supersededPairs, truncated) {
|
|
187
|
+
// Build the target index: normalised name -> single entity id. A name is a target only
|
|
188
|
+
// if its length is in bounds; a name shared by >1 entity is AMBIGUOUS and dropped.
|
|
189
|
+
const nameToId = new Map();
|
|
190
|
+
const ambiguous = new Set();
|
|
191
|
+
for (const e of created) {
|
|
192
|
+
// References are among ACTIVE entities only: a superseded (outdated) row is not a
|
|
193
|
+
// current cross-reference target (codex).
|
|
194
|
+
if (e.superseded)
|
|
195
|
+
continue;
|
|
196
|
+
// Decisions are SOURCE-only: their name is decision prose, referenced by supersedes,
|
|
197
|
+
// not by name-mention. Excluding them as targets also prevents a decision's own name
|
|
198
|
+
// (== its searchText) from whole-string self-matching and shadowing embedded targets.
|
|
199
|
+
if (e.entityType === 'decision')
|
|
200
|
+
continue;
|
|
201
|
+
const norm = e.name.trim().toLowerCase();
|
|
202
|
+
if (norm.length < MIN_REF_NAME_LEN || norm.length > MAX_REF_NAME_LEN)
|
|
203
|
+
continue;
|
|
204
|
+
if (ambiguous.has(norm))
|
|
205
|
+
continue;
|
|
206
|
+
if (nameToId.has(norm)) {
|
|
207
|
+
// Second distinct entity with this name (and not its own id repeated) -> ambiguous.
|
|
208
|
+
if (nameToId.get(norm) !== e.entityId) {
|
|
209
|
+
nameToId.delete(norm);
|
|
210
|
+
ambiguous.add(norm);
|
|
211
|
+
}
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
nameToId.set(norm, e.entityId);
|
|
215
|
+
}
|
|
216
|
+
if (nameToId.size === 0)
|
|
217
|
+
return 0;
|
|
218
|
+
// Record the truncation (observability, mirroring MAX_EXTRACT_PER_TYPE) so a >cap
|
|
219
|
+
// store's under-matched references are not silent.
|
|
220
|
+
if (nameToId.size > MAX_TARGET_NAMES)
|
|
221
|
+
truncated.push('references-targets');
|
|
222
|
+
// LONGEST name first, then alphabetical: JS regex alternation is leftmost-first, so
|
|
223
|
+
// ordering longer names before their prefixes makes the match longest-at-position
|
|
224
|
+
// (`postgres pro` wins over `postgres`; codex). Deterministic, so truncation is stable.
|
|
225
|
+
const targetNames = [...nameToId.keys()]
|
|
226
|
+
.sort((a, b) => b.length - a.length || (a < b ? -1 : a > b ? 1 : 0))
|
|
227
|
+
.slice(0, MAX_TARGET_NAMES);
|
|
228
|
+
const pattern = `\\b(?:${targetNames.map(escapeRegex).join('|')})\\b`;
|
|
229
|
+
const re = new RegExp(pattern, 'gi');
|
|
230
|
+
let references = 0;
|
|
231
|
+
for (const src of created) {
|
|
232
|
+
if (src.superseded)
|
|
233
|
+
continue; // superseded sources hold only stale references (codex)
|
|
234
|
+
if (!src.searchText)
|
|
235
|
+
continue;
|
|
236
|
+
const targets = new Set();
|
|
237
|
+
for (const m of src.searchText.matchAll(re)) {
|
|
238
|
+
const targetId = nameToId.get(m[0].toLowerCase());
|
|
239
|
+
if (targetId === undefined || targetId === src.entityId)
|
|
240
|
+
continue; // miss / self
|
|
241
|
+
if (supersededPairs.has(pairKey(src.entityId, targetId)))
|
|
242
|
+
continue; // already version-related
|
|
243
|
+
targets.add(targetId);
|
|
244
|
+
if (targets.size >= MAX_REFERENCES_PER_OBJECT)
|
|
245
|
+
break; // per-source cap
|
|
246
|
+
}
|
|
247
|
+
for (const targetId of targets) {
|
|
248
|
+
insertRelation(hippoRoot, tenantId, {
|
|
249
|
+
fromEntityId: src.entityId,
|
|
250
|
+
toEntityId: targetId,
|
|
251
|
+
relType: 'references',
|
|
252
|
+
memoryId: src.memoryId, // the source object's consolidated memory
|
|
253
|
+
});
|
|
254
|
+
references += 1;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return references;
|
|
140
258
|
}
|
|
141
259
|
//# sourceMappingURL=graph-extract.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph-extract.js","sourceRoot":"","sources":["../src/graph-extract.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"graph-extract.js","sourceRoot":"","sources":["../src/graph-extract.ts"],"names":[],"mappings":"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"}
|
package/dist/hooks.js
CHANGED
|
@@ -77,30 +77,30 @@ const HIPPO_OPENCODE_PLUGIN_MARKER = 'HIPPO_OPENCODE_PLUGIN_V1';
|
|
|
77
77
|
* marker match AND content equality, so a plugin-source revision under
|
|
78
78
|
* the same V1 marker re-writes the file on next install.
|
|
79
79
|
*/
|
|
80
|
-
export const OPENCODE_PLUGIN_SOURCE = `// ${HIPPO_OPENCODE_PLUGIN_MARKER}
|
|
81
|
-
// hippo-memory opencode plugin. DO NOT EDIT — regenerated on every
|
|
82
|
-
// \`hippo hook install opencode\` from src/hooks.ts OPENCODE_PLUGIN_SOURCE
|
|
83
|
-
// in https://github.com/kitfunso/hippo-memory. Local changes will be lost.
|
|
84
|
-
|
|
85
|
-
export const HippoPlugin = async ({ $ }) => {
|
|
86
|
-
return {
|
|
87
|
-
event: async ({ event }) => {
|
|
88
|
-
// Defense in depth: opencode currently runs in Bun where $ is the shell
|
|
89
|
-
// template helper. A non-Bun runtime would have $ as undefined; fail
|
|
90
|
-
// closed instead of crashing the host session.
|
|
91
|
-
if (typeof $ !== "function") return;
|
|
92
|
-
try {
|
|
93
|
-
if (event.type === "session.idle") {
|
|
94
|
-
await $\`hippo session-end\`.quiet().nothrow();
|
|
95
|
-
} else if (event.type === "session.created") {
|
|
96
|
-
await $\`hippo last-sleep\`.quiet().nothrow();
|
|
97
|
-
}
|
|
98
|
-
} catch {
|
|
99
|
-
// hippo CLI not on PATH or other failure — never crash the host session.
|
|
100
|
-
}
|
|
101
|
-
},
|
|
102
|
-
};
|
|
103
|
-
};
|
|
80
|
+
export const OPENCODE_PLUGIN_SOURCE = `// ${HIPPO_OPENCODE_PLUGIN_MARKER}
|
|
81
|
+
// hippo-memory opencode plugin. DO NOT EDIT — regenerated on every
|
|
82
|
+
// \`hippo hook install opencode\` from src/hooks.ts OPENCODE_PLUGIN_SOURCE
|
|
83
|
+
// in https://github.com/kitfunso/hippo-memory. Local changes will be lost.
|
|
84
|
+
|
|
85
|
+
export const HippoPlugin = async ({ $ }) => {
|
|
86
|
+
return {
|
|
87
|
+
event: async ({ event }) => {
|
|
88
|
+
// Defense in depth: opencode currently runs in Bun where $ is the shell
|
|
89
|
+
// template helper. A non-Bun runtime would have $ as undefined; fail
|
|
90
|
+
// closed instead of crashing the host session.
|
|
91
|
+
if (typeof $ !== "function") return;
|
|
92
|
+
try {
|
|
93
|
+
if (event.type === "session.idle") {
|
|
94
|
+
await $\`hippo session-end\`.quiet().nothrow();
|
|
95
|
+
} else if (event.type === "session.created") {
|
|
96
|
+
await $\`hippo last-sleep\`.quiet().nothrow();
|
|
97
|
+
}
|
|
98
|
+
} catch {
|
|
99
|
+
// hippo CLI not on PATH or other failure — never crash the host session.
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
104
|
`;
|
|
105
105
|
export { HIPPO_OPENCODE_PLUGIN_MARKER };
|
|
106
106
|
function homeDir() {
|
package/dist/physics-state.js
CHANGED
|
@@ -29,20 +29,20 @@ export function bufferToFloat32(buf) {
|
|
|
29
29
|
* Call this from db.ts MIGRATIONS array.
|
|
30
30
|
*/
|
|
31
31
|
export function createPhysicsTable(db) {
|
|
32
|
-
db.exec(`
|
|
33
|
-
CREATE TABLE IF NOT EXISTS memory_physics (
|
|
34
|
-
memory_id TEXT PRIMARY KEY REFERENCES memories(id) ON DELETE CASCADE,
|
|
35
|
-
position_blob BLOB NOT NULL,
|
|
36
|
-
velocity_blob BLOB NOT NULL,
|
|
37
|
-
mass REAL NOT NULL,
|
|
38
|
-
charge REAL NOT NULL,
|
|
39
|
-
temperature REAL NOT NULL,
|
|
40
|
-
last_simulation TEXT NOT NULL,
|
|
41
|
-
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
CREATE INDEX IF NOT EXISTS idx_memory_physics_mass
|
|
45
|
-
ON memory_physics(mass DESC);
|
|
32
|
+
db.exec(`
|
|
33
|
+
CREATE TABLE IF NOT EXISTS memory_physics (
|
|
34
|
+
memory_id TEXT PRIMARY KEY REFERENCES memories(id) ON DELETE CASCADE,
|
|
35
|
+
position_blob BLOB NOT NULL,
|
|
36
|
+
velocity_blob BLOB NOT NULL,
|
|
37
|
+
mass REAL NOT NULL,
|
|
38
|
+
charge REAL NOT NULL,
|
|
39
|
+
temperature REAL NOT NULL,
|
|
40
|
+
last_simulation TEXT NOT NULL,
|
|
41
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
CREATE INDEX IF NOT EXISTS idx_memory_physics_mass
|
|
45
|
+
ON memory_physics(mass DESC);
|
|
46
46
|
`);
|
|
47
47
|
}
|
|
48
48
|
/**
|
|
@@ -53,11 +53,11 @@ export function loadPhysicsState(db, memoryIds) {
|
|
|
53
53
|
let rows;
|
|
54
54
|
if (memoryIds && memoryIds.length > 0) {
|
|
55
55
|
const placeholders = memoryIds.map(() => '?').join(',');
|
|
56
|
-
rows = db.prepare(`SELECT memory_id, position_blob, velocity_blob, mass, charge, temperature, last_simulation
|
|
56
|
+
rows = db.prepare(`SELECT memory_id, position_blob, velocity_blob, mass, charge, temperature, last_simulation
|
|
57
57
|
FROM memory_physics WHERE memory_id IN (${placeholders})`).all(...memoryIds);
|
|
58
58
|
}
|
|
59
59
|
else {
|
|
60
|
-
rows = db.prepare(`SELECT memory_id, position_blob, velocity_blob, mass, charge, temperature, last_simulation
|
|
60
|
+
rows = db.prepare(`SELECT memory_id, position_blob, velocity_blob, mass, charge, temperature, last_simulation
|
|
61
61
|
FROM memory_physics`).all();
|
|
62
62
|
}
|
|
63
63
|
for (const row of rows) {
|
|
@@ -79,17 +79,17 @@ export function loadPhysicsState(db, memoryIds) {
|
|
|
79
79
|
export function savePhysicsState(db, particles) {
|
|
80
80
|
if (particles.length === 0)
|
|
81
81
|
return;
|
|
82
|
-
const stmt = db.prepare(`
|
|
83
|
-
INSERT INTO memory_physics (memory_id, position_blob, velocity_blob, mass, charge, temperature, last_simulation, updated_at)
|
|
84
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now'))
|
|
85
|
-
ON CONFLICT(memory_id) DO UPDATE SET
|
|
86
|
-
position_blob = excluded.position_blob,
|
|
87
|
-
velocity_blob = excluded.velocity_blob,
|
|
88
|
-
mass = excluded.mass,
|
|
89
|
-
charge = excluded.charge,
|
|
90
|
-
temperature = excluded.temperature,
|
|
91
|
-
last_simulation = excluded.last_simulation,
|
|
92
|
-
updated_at = datetime('now')
|
|
82
|
+
const stmt = db.prepare(`
|
|
83
|
+
INSERT INTO memory_physics (memory_id, position_blob, velocity_blob, mass, charge, temperature, last_simulation, updated_at)
|
|
84
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now'))
|
|
85
|
+
ON CONFLICT(memory_id) DO UPDATE SET
|
|
86
|
+
position_blob = excluded.position_blob,
|
|
87
|
+
velocity_blob = excluded.velocity_blob,
|
|
88
|
+
mass = excluded.mass,
|
|
89
|
+
charge = excluded.charge,
|
|
90
|
+
temperature = excluded.temperature,
|
|
91
|
+
last_simulation = excluded.last_simulation,
|
|
92
|
+
updated_at = datetime('now')
|
|
93
93
|
`);
|
|
94
94
|
db.exec('BEGIN');
|
|
95
95
|
try {
|
package/dist/predictions.js
CHANGED
|
@@ -86,20 +86,20 @@ export function savePrediction(hippoRoot, tenantId, opts, actor = 'cli') {
|
|
|
86
86
|
writeEntry(hippoRoot, mem, {
|
|
87
87
|
actor,
|
|
88
88
|
afterWrite: (db, memoryId) => {
|
|
89
|
-
const result = db.prepare(`
|
|
90
|
-
INSERT INTO predictions(
|
|
91
|
-
memory_id, tenant_id, class_tag, claim_text,
|
|
92
|
-
estimate_value, estimate_unit, target_date,
|
|
93
|
-
actual_value, closure_state, closed_at, closure_note, created_at
|
|
94
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'open', NULL, NULL, ?)
|
|
89
|
+
const result = db.prepare(`
|
|
90
|
+
INSERT INTO predictions(
|
|
91
|
+
memory_id, tenant_id, class_tag, claim_text,
|
|
92
|
+
estimate_value, estimate_unit, target_date,
|
|
93
|
+
actual_value, closure_state, closed_at, closure_note, created_at
|
|
94
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'open', NULL, NULL, ?)
|
|
95
95
|
`).run(memoryId, tenantId, opts.classTag, opts.claimText, opts.estimateValue ?? null, opts.estimateUnit ?? null, opts.targetDate ?? null, null, // actual_value — null until close
|
|
96
96
|
now);
|
|
97
97
|
const predictionId = Number(result.lastInsertRowid ?? 0);
|
|
98
|
-
const row = db.prepare(`
|
|
99
|
-
SELECT id, memory_id, tenant_id, class_tag, claim_text,
|
|
100
|
-
estimate_value, estimate_unit, target_date,
|
|
101
|
-
actual_value, closure_state, closed_at, closure_note, created_at
|
|
102
|
-
FROM predictions WHERE id = ?
|
|
98
|
+
const row = db.prepare(`
|
|
99
|
+
SELECT id, memory_id, tenant_id, class_tag, claim_text,
|
|
100
|
+
estimate_value, estimate_unit, target_date,
|
|
101
|
+
actual_value, closure_state, closed_at, closure_note, created_at
|
|
102
|
+
FROM predictions WHERE id = ?
|
|
103
103
|
`).get(predictionId);
|
|
104
104
|
if (!row) {
|
|
105
105
|
throw new Error('Failed to reload saved prediction row');
|
|
@@ -150,16 +150,16 @@ export function closePrediction(hippoRoot, tenantId, id, opts, actor = 'cli') {
|
|
|
150
150
|
// predict_close audit row. Zero changed rows → caller decides
|
|
151
151
|
// whether it's a "not found" or "already closed" case based on the
|
|
152
152
|
// load-then-close pattern.
|
|
153
|
-
const updateResult = db.prepare(`
|
|
154
|
-
UPDATE predictions
|
|
155
|
-
SET actual_value = ?, closure_state = ?, closed_at = ?, closure_note = ?
|
|
156
|
-
WHERE id = ? AND tenant_id = ? AND closure_state = 'open'
|
|
153
|
+
const updateResult = db.prepare(`
|
|
154
|
+
UPDATE predictions
|
|
155
|
+
SET actual_value = ?, closure_state = ?, closed_at = ?, closure_note = ?
|
|
156
|
+
WHERE id = ? AND tenant_id = ? AND closure_state = 'open'
|
|
157
157
|
`).run(opts.actualValue ?? null, opts.closureState, now, opts.closureNote ?? null, id, tenantId);
|
|
158
158
|
if (updateResult.changes === 0) {
|
|
159
159
|
// Distinguish "not found" from "already closed" so callers (CLI, HTTP)
|
|
160
160
|
// can surface the right error to the user.
|
|
161
|
-
const existing = db.prepare(`
|
|
162
|
-
SELECT closure_state FROM predictions WHERE id = ? AND tenant_id = ?
|
|
161
|
+
const existing = db.prepare(`
|
|
162
|
+
SELECT closure_state FROM predictions WHERE id = ? AND tenant_id = ?
|
|
163
163
|
`).get(id, tenantId);
|
|
164
164
|
if (!existing) {
|
|
165
165
|
throw new Error(`closePrediction: prediction ${id} not found for tenant ${tenantId}`);
|
|
@@ -167,11 +167,11 @@ export function closePrediction(hippoRoot, tenantId, id, opts, actor = 'cli') {
|
|
|
167
167
|
throw new Error(`closePrediction: prediction ${id} is already closed (state='${existing.closure_state}'); ` +
|
|
168
168
|
`cannot re-close. Open predictions only.`);
|
|
169
169
|
}
|
|
170
|
-
const row = db.prepare(`
|
|
171
|
-
SELECT id, memory_id, tenant_id, class_tag, claim_text,
|
|
172
|
-
estimate_value, estimate_unit, target_date,
|
|
173
|
-
actual_value, closure_state, closed_at, closure_note, created_at
|
|
174
|
-
FROM predictions WHERE id = ? AND tenant_id = ?
|
|
170
|
+
const row = db.prepare(`
|
|
171
|
+
SELECT id, memory_id, tenant_id, class_tag, claim_text,
|
|
172
|
+
estimate_value, estimate_unit, target_date,
|
|
173
|
+
actual_value, closure_state, closed_at, closure_note, created_at
|
|
174
|
+
FROM predictions WHERE id = ? AND tenant_id = ?
|
|
175
175
|
`).get(id, tenantId);
|
|
176
176
|
if (!row) {
|
|
177
177
|
throw new Error(`closePrediction: prediction ${id} not found after UPDATE`);
|
|
@@ -208,11 +208,11 @@ export function loadPredictionById(hippoRoot, tenantId, id) {
|
|
|
208
208
|
assertTenantId('loadPredictionById', tenantId);
|
|
209
209
|
const db = openHippoDb(hippoRoot);
|
|
210
210
|
try {
|
|
211
|
-
const row = db.prepare(`
|
|
212
|
-
SELECT id, memory_id, tenant_id, class_tag, claim_text,
|
|
213
|
-
estimate_value, estimate_unit, target_date,
|
|
214
|
-
actual_value, closure_state, closed_at, closure_note, created_at
|
|
215
|
-
FROM predictions WHERE id = ? AND tenant_id = ?
|
|
211
|
+
const row = db.prepare(`
|
|
212
|
+
SELECT id, memory_id, tenant_id, class_tag, claim_text,
|
|
213
|
+
estimate_value, estimate_unit, target_date,
|
|
214
|
+
actual_value, closure_state, closed_at, closure_note, created_at
|
|
215
|
+
FROM predictions WHERE id = ? AND tenant_id = ?
|
|
216
216
|
`).get(id, tenantId);
|
|
217
217
|
return row ? rowToPrediction(row) : null;
|
|
218
218
|
}
|
|
@@ -230,25 +230,25 @@ export function loadPredictionsByClass(hippoRoot, tenantId, classTag, opts = {})
|
|
|
230
230
|
if (!VALID_CLOSURE_STATES.has(opts.closureState)) {
|
|
231
231
|
throw new Error(`loadPredictionsByClass: closureState must be one of ${Array.from(VALID_CLOSURE_STATES).join('|')}; got ${opts.closureState}`);
|
|
232
232
|
}
|
|
233
|
-
rows = db.prepare(`
|
|
234
|
-
SELECT id, memory_id, tenant_id, class_tag, claim_text,
|
|
235
|
-
estimate_value, estimate_unit, target_date,
|
|
236
|
-
actual_value, closure_state, closed_at, closure_note, created_at
|
|
237
|
-
FROM predictions
|
|
238
|
-
WHERE tenant_id = ? AND class_tag = ? AND closure_state = ?
|
|
239
|
-
ORDER BY created_at DESC, id DESC
|
|
240
|
-
LIMIT ?
|
|
233
|
+
rows = db.prepare(`
|
|
234
|
+
SELECT id, memory_id, tenant_id, class_tag, claim_text,
|
|
235
|
+
estimate_value, estimate_unit, target_date,
|
|
236
|
+
actual_value, closure_state, closed_at, closure_note, created_at
|
|
237
|
+
FROM predictions
|
|
238
|
+
WHERE tenant_id = ? AND class_tag = ? AND closure_state = ?
|
|
239
|
+
ORDER BY created_at DESC, id DESC
|
|
240
|
+
LIMIT ?
|
|
241
241
|
`).all(tenantId, classTag, opts.closureState, limit);
|
|
242
242
|
}
|
|
243
243
|
else {
|
|
244
|
-
rows = db.prepare(`
|
|
245
|
-
SELECT id, memory_id, tenant_id, class_tag, claim_text,
|
|
246
|
-
estimate_value, estimate_unit, target_date,
|
|
247
|
-
actual_value, closure_state, closed_at, closure_note, created_at
|
|
248
|
-
FROM predictions
|
|
249
|
-
WHERE tenant_id = ? AND class_tag = ?
|
|
250
|
-
ORDER BY created_at DESC, id DESC
|
|
251
|
-
LIMIT ?
|
|
244
|
+
rows = db.prepare(`
|
|
245
|
+
SELECT id, memory_id, tenant_id, class_tag, claim_text,
|
|
246
|
+
estimate_value, estimate_unit, target_date,
|
|
247
|
+
actual_value, closure_state, closed_at, closure_note, created_at
|
|
248
|
+
FROM predictions
|
|
249
|
+
WHERE tenant_id = ? AND class_tag = ?
|
|
250
|
+
ORDER BY created_at DESC, id DESC
|
|
251
|
+
LIMIT ?
|
|
252
252
|
`).all(tenantId, classTag, limit);
|
|
253
253
|
}
|
|
254
254
|
return rows.map(rowToPrediction);
|
|
@@ -286,14 +286,14 @@ emitAudit = true) {
|
|
|
286
286
|
throw new Error('computePredictionBaserate: classTag is required');
|
|
287
287
|
const db = openHippoDb(hippoRoot);
|
|
288
288
|
try {
|
|
289
|
-
const rows = db.prepare(`
|
|
290
|
-
SELECT estimate_value, actual_value
|
|
291
|
-
FROM predictions
|
|
292
|
-
WHERE tenant_id = ?
|
|
293
|
-
AND class_tag = ?
|
|
294
|
-
AND closure_state = 'closed'
|
|
295
|
-
AND estimate_value IS NOT NULL
|
|
296
|
-
AND actual_value IS NOT NULL
|
|
289
|
+
const rows = db.prepare(`
|
|
290
|
+
SELECT estimate_value, actual_value
|
|
291
|
+
FROM predictions
|
|
292
|
+
WHERE tenant_id = ?
|
|
293
|
+
AND class_tag = ?
|
|
294
|
+
AND closure_state = 'closed'
|
|
295
|
+
AND estimate_value IS NOT NULL
|
|
296
|
+
AND actual_value IS NOT NULL
|
|
297
297
|
`).all(tenantId, classTag);
|
|
298
298
|
const nClosed = rows.length;
|
|
299
299
|
if (nClosed === 0) {
|
|
@@ -373,25 +373,25 @@ export function loadOpenPredictions(hippoRoot, tenantId, opts = {}) {
|
|
|
373
373
|
try {
|
|
374
374
|
let rows;
|
|
375
375
|
if (opts.classTag) {
|
|
376
|
-
rows = db.prepare(`
|
|
377
|
-
SELECT id, memory_id, tenant_id, class_tag, claim_text,
|
|
378
|
-
estimate_value, estimate_unit, target_date,
|
|
379
|
-
actual_value, closure_state, closed_at, closure_note, created_at
|
|
380
|
-
FROM predictions
|
|
381
|
-
WHERE tenant_id = ? AND class_tag = ? AND closure_state = 'open'
|
|
382
|
-
ORDER BY created_at DESC, id DESC
|
|
383
|
-
LIMIT ?
|
|
376
|
+
rows = db.prepare(`
|
|
377
|
+
SELECT id, memory_id, tenant_id, class_tag, claim_text,
|
|
378
|
+
estimate_value, estimate_unit, target_date,
|
|
379
|
+
actual_value, closure_state, closed_at, closure_note, created_at
|
|
380
|
+
FROM predictions
|
|
381
|
+
WHERE tenant_id = ? AND class_tag = ? AND closure_state = 'open'
|
|
382
|
+
ORDER BY created_at DESC, id DESC
|
|
383
|
+
LIMIT ?
|
|
384
384
|
`).all(tenantId, opts.classTag, limit);
|
|
385
385
|
}
|
|
386
386
|
else {
|
|
387
|
-
rows = db.prepare(`
|
|
388
|
-
SELECT id, memory_id, tenant_id, class_tag, claim_text,
|
|
389
|
-
estimate_value, estimate_unit, target_date,
|
|
390
|
-
actual_value, closure_state, closed_at, closure_note, created_at
|
|
391
|
-
FROM predictions
|
|
392
|
-
WHERE tenant_id = ? AND closure_state = 'open'
|
|
393
|
-
ORDER BY created_at DESC, id DESC
|
|
394
|
-
LIMIT ?
|
|
387
|
+
rows = db.prepare(`
|
|
388
|
+
SELECT id, memory_id, tenant_id, class_tag, claim_text,
|
|
389
|
+
estimate_value, estimate_unit, target_date,
|
|
390
|
+
actual_value, closure_state, closed_at, closure_note, created_at
|
|
391
|
+
FROM predictions
|
|
392
|
+
WHERE tenant_id = ? AND closure_state = 'open'
|
|
393
|
+
ORDER BY created_at DESC, id DESC
|
|
394
|
+
LIMIT ?
|
|
395
395
|
`).all(tenantId, limit);
|
|
396
396
|
}
|
|
397
397
|
return rows.map(rowToPrediction);
|
package/dist/refine-llm.js
CHANGED
|
@@ -31,19 +31,19 @@ export async function refineSemanticMemory(merged, sources, opts) {
|
|
|
31
31
|
.slice(0, 8)
|
|
32
32
|
.map((s, i) => `[source ${i + 1}] ${s.content.slice(0, 400)}`)
|
|
33
33
|
.join('\n\n');
|
|
34
|
-
const prompt = `You are refining a semantic memory in an agent's memory store. The rule-based consolidator merged several related episodic memories into one, but the output is clumsy. Produce a single coherent semantic memory that captures the underlying principle.
|
|
35
|
-
|
|
36
|
-
Rules:
|
|
37
|
-
- Output ONLY the refined content — no preamble, no quote marks, no "Here is...".
|
|
38
|
-
- Keep it concise: one paragraph, no headers, no bullet lists unless the sources are inherently a list.
|
|
39
|
-
- Preserve specific facts (names, numbers, paths, IDs) from the sources.
|
|
40
|
-
- Generalize: state the pattern, not each instance.
|
|
41
|
-
- Do NOT include the "[Consolidated from N ...]" marker.
|
|
42
|
-
|
|
43
|
-
Current merged content:
|
|
44
|
-
${merged}
|
|
45
|
-
|
|
46
|
-
Source memories (up to 8 shown):
|
|
34
|
+
const prompt = `You are refining a semantic memory in an agent's memory store. The rule-based consolidator merged several related episodic memories into one, but the output is clumsy. Produce a single coherent semantic memory that captures the underlying principle.
|
|
35
|
+
|
|
36
|
+
Rules:
|
|
37
|
+
- Output ONLY the refined content — no preamble, no quote marks, no "Here is...".
|
|
38
|
+
- Keep it concise: one paragraph, no headers, no bullet lists unless the sources are inherently a list.
|
|
39
|
+
- Preserve specific facts (names, numbers, paths, IDs) from the sources.
|
|
40
|
+
- Generalize: state the pattern, not each instance.
|
|
41
|
+
- Do NOT include the "[Consolidated from N ...]" marker.
|
|
42
|
+
|
|
43
|
+
Current merged content:
|
|
44
|
+
${merged}
|
|
45
|
+
|
|
46
|
+
Source memories (up to 8 shown):
|
|
47
47
|
${sourceBlock}`;
|
|
48
48
|
let res;
|
|
49
49
|
try {
|