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.
Files changed (71) hide show
  1. package/bin/hippo.js +2 -2
  2. package/dist/api.d.ts +28 -0
  3. package/dist/api.d.ts.map +1 -1
  4. package/dist/api.js +31 -7
  5. package/dist/api.js.map +1 -1
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +107 -12
  8. package/dist/cli.js.map +1 -1
  9. package/dist/connectors/github/backfill.js +4 -4
  10. package/dist/connectors/github/cli-impl.js +6 -6
  11. package/dist/connectors/github/dlq.js +14 -14
  12. package/dist/connectors/slack/backfill.js +1 -1
  13. package/dist/connectors/slack/dlq.js +10 -10
  14. package/dist/connectors/slack/workspaces.js +4 -4
  15. package/dist/dag.js +6 -6
  16. package/dist/dashboard.js +7 -7
  17. package/dist/goals.d.ts +11 -0
  18. package/dist/goals.d.ts.map +1 -1
  19. package/dist/goals.js +61 -49
  20. package/dist/goals.js.map +1 -1
  21. package/dist/graph-extract.d.ts +28 -12
  22. package/dist/graph-extract.d.ts.map +1 -1
  23. package/dist/graph-extract.js +138 -20
  24. package/dist/graph-extract.js.map +1 -1
  25. package/dist/hooks.js +24 -24
  26. package/dist/physics-state.js +27 -27
  27. package/dist/predictions.js +67 -67
  28. package/dist/refine-llm.js +13 -13
  29. package/dist/search.d.ts +33 -0
  30. package/dist/search.d.ts.map +1 -1
  31. package/dist/search.js.map +1 -1
  32. package/dist/server.d.ts.map +1 -1
  33. package/dist/server.js +7 -0
  34. package/dist/server.js.map +1 -1
  35. package/dist/src/api.js +31 -7
  36. package/dist/src/api.js.map +1 -1
  37. package/dist/src/cli.js +107 -12
  38. package/dist/src/cli.js.map +1 -1
  39. package/dist/src/connectors/github/backfill.js +4 -4
  40. package/dist/src/connectors/github/cli-impl.js +6 -6
  41. package/dist/src/connectors/github/dlq.js +14 -14
  42. package/dist/src/connectors/slack/backfill.js +1 -1
  43. package/dist/src/connectors/slack/dlq.js +10 -10
  44. package/dist/src/connectors/slack/workspaces.js +4 -4
  45. package/dist/src/dag.js +6 -6
  46. package/dist/src/dashboard.js +7 -7
  47. package/dist/src/goals.js +61 -49
  48. package/dist/src/goals.js.map +1 -1
  49. package/dist/src/graph-extract.js +138 -20
  50. package/dist/src/graph-extract.js.map +1 -1
  51. package/dist/src/hooks.js +24 -24
  52. package/dist/src/physics-state.js +27 -27
  53. package/dist/src/predictions.js +67 -67
  54. package/dist/src/refine-llm.js +13 -13
  55. package/dist/src/search.js.map +1 -1
  56. package/dist/src/server.js +7 -0
  57. package/dist/src/server.js.map +1 -1
  58. package/dist/src/store.js +260 -260
  59. package/dist/src/version.js +1 -1
  60. package/dist/src/working-memory.js +19 -19
  61. package/dist/store.js +260 -260
  62. package/dist/version.d.ts +1 -1
  63. package/dist/version.js +1 -1
  64. package/dist/working-memory.js +19 -19
  65. package/dist-ui/index.html +12 -12
  66. package/extensions/openclaw-plugin/index.ts +650 -650
  67. package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
  68. package/extensions/openclaw-plugin/package.json +1 -1
  69. package/openclaw.plugin.json +1 -1
  70. package/package.json +1 -1
  71. package/dist/benchmarks/e1.3/scenarios.json +0 -2587
@@ -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 function of the current E2
7
- * state, so `extractGraph` is an idempotent REBUILD: clear the tenant's graph, then
8
- * re-derive entities + `supersedes` relations from decisions / policies /
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
- * guard (insertEntity / insertRelation / clearGraph); this module issues no raw SQL.
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
- * Deferred (follow-ups): NLP prose-extraction from raw/distilled memories (the
14
- * 80%-precision gold-set part); skill/incident/process entities (not in the
15
- * entity_type enum); cross-object relations beyond supersedes; the `hippo sleep`
16
- * enqueue-hook; E3.2 multi-hop recall. When a SECOND producer (NLP) lands, extraction
17
- * will need a `source` marker so the rebuild scopes to only the deterministically-
18
- * extracted rows.
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
- const entities = Array.from(entityIdByKey.keys()).length;
139
- return { entities, relations, byType, truncated };
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":"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"}
1
+ {"version":3,"file":"graph-extract.js","sourceRoot":"","sources":["../../src/graph-extract.ts"],"names":[],"mappings":"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"}
package/dist/src/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() {
@@ -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 {
@@ -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);
@@ -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 {