@gamaze/hicortex 0.20.9 → 0.21.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 (48) hide show
  1. package/README.md +8 -0
  2. package/assets/dashboard.html +4174 -835
  3. package/dist/calibration.d.ts +119 -0
  4. package/dist/calibration.js +149 -1
  5. package/dist/capture-health.d.ts +87 -0
  6. package/dist/capture-health.js +106 -0
  7. package/dist/capture-pause.d.ts +86 -0
  8. package/dist/capture-pause.js +127 -0
  9. package/dist/capture.d.ts +9 -0
  10. package/dist/capture.js +2 -1
  11. package/dist/cli.js +36 -0
  12. package/dist/consolidate.d.ts +35 -0
  13. package/dist/consolidate.js +85 -9
  14. package/dist/dashboard.d.ts +322 -3
  15. package/dist/dashboard.js +592 -7
  16. package/dist/db.js +105 -0
  17. package/dist/eval/importance-eval.d.ts +85 -0
  18. package/dist/eval/importance-eval.js +286 -0
  19. package/dist/eval/planted-fixtures.d.ts +1 -1
  20. package/dist/eval/ranking-battery.d.ts +78 -0
  21. package/dist/eval/ranking-battery.js +181 -0
  22. package/dist/eval/ranking-eval.d.ts +41 -0
  23. package/dist/eval/ranking-eval.js +391 -0
  24. package/dist/eval/ranking-fixtures.d.ts +77 -0
  25. package/dist/eval/ranking-fixtures.js +226 -0
  26. package/dist/identity-store.d.ts +21 -0
  27. package/dist/identity-store.js +49 -0
  28. package/dist/init.d.ts +14 -0
  29. package/dist/init.js +32 -0
  30. package/dist/mcp-server.d.ts +12 -0
  31. package/dist/mcp-server.js +184 -3
  32. package/dist/nightly.d.ts +9 -1
  33. package/dist/nightly.js +59 -7
  34. package/dist/prompts.d.ts +10 -0
  35. package/dist/prompts.js +28 -5
  36. package/dist/reconsolidation.d.ts +59 -30
  37. package/dist/reconsolidation.js +526 -296
  38. package/dist/rescore-importance.d.ts +80 -0
  39. package/dist/rescore-importance.js +236 -0
  40. package/dist/retrieval.d.ts +12 -0
  41. package/dist/retrieval.js +30 -1
  42. package/dist/stages.d.ts +37 -0
  43. package/dist/stages.js +51 -0
  44. package/dist/state.d.ts +32 -6
  45. package/dist/storage.d.ts +34 -2
  46. package/dist/storage.js +63 -6
  47. package/dist/types.d.ts +48 -0
  48. package/package.json +3 -1
package/dist/storage.js CHANGED
@@ -39,11 +39,13 @@ var __importStar = (this && this.__importStar) || (function () {
39
39
  Object.defineProperty(exports, "__esModule", { value: true });
40
40
  exports.FTS_MATCH_MAX_TOKENS = void 0;
41
41
  exports.embedToBlob = embedToBlob;
42
+ exports.sanitizeSourceMachine = sanitizeSourceMachine;
42
43
  exports.insertMemory = insertMemory;
43
44
  exports.resolveMemoryId = resolveMemoryId;
44
45
  exports.getMemory = getMemory;
45
46
  exports.updateMemory = updateMemory;
46
47
  exports.strengthenMemory = strengthenMemory;
48
+ exports.enrichMemory = enrichMemory;
47
49
  exports.touchMemoriesShown = touchMemoriesShown;
48
50
  exports.deleteMemory = deleteMemory;
49
51
  exports.memoryRowid = memoryRowid;
@@ -90,6 +92,18 @@ function rowToMemory(row) {
90
92
  // ---------------------------------------------------------------------------
91
93
  // Single memory CRUD
92
94
  // ---------------------------------------------------------------------------
95
+ /**
96
+ * #421 machine × harness identity: optional capture-machine provenance on
97
+ * /ingest + /distill. A string, trimmed, capped at 128 chars; anything else
98
+ * (absent, wrong type, blank) becomes NULL — a wrong machine name is worse
99
+ * than none. Never filtered; presentation grouping only.
100
+ */
101
+ function sanitizeSourceMachine(v) {
102
+ if (typeof v !== "string")
103
+ return null;
104
+ const t = v.trim();
105
+ return t.length > 0 ? t.slice(0, 128) : null;
106
+ }
93
107
  /**
94
108
  * Insert a memory and its vector embedding. Returns the memory's UUID.
95
109
  *
@@ -108,9 +122,9 @@ function insertMemory(db, content, embedding, opts = {}) {
108
122
  .prepare(`INSERT OR IGNORE INTO memories
109
123
  (id, content, base_strength, last_accessed, access_count,
110
124
  created_at, ingested_at, source_agent, source_agent_id, source_session,
111
- source_domain, project, privacy, memory_type)
112
- VALUES (?, ?, ?, ?, 0, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
113
- .run(id, content, opts.baseStrength ?? 0.5, ts, ts, ingestedTs, opts.sourceAgent ?? "default", opts.sourceAgentId ?? null, sourceSession, opts.sourceDomain ?? null, opts.project ?? null, opts.privacy ?? null, opts.memoryType ?? "experience");
125
+ source_domain, project, privacy, memory_type, source_machine)
126
+ VALUES (?, ?, ?, ?, 0, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
127
+ .run(id, content, opts.baseStrength ?? 0.5, ts, ts, ingestedTs, opts.sourceAgent ?? "default", opts.sourceAgentId ?? null, sourceSession, opts.sourceDomain ?? null, opts.project ?? null, opts.privacy ?? null, opts.memoryType ?? "experience", opts.sourceMachine ?? null);
114
128
  if (result.changes > 0) {
115
129
  // New row — store its vector.
116
130
  db.prepare("INSERT INTO memory_vectors (id, embedding) VALUES (?, ?)").run(id, embedToBlob(embedding));
@@ -172,6 +186,10 @@ const ALLOWED_UPDATE_FIELDS = new Set([
172
186
  // reconsolidation stage, explicit ingest marks, and history rollback.
173
187
  // Code-defined vocabulary — see Memory.status.
174
188
  "status",
189
+ // Importance scored-at watermark (#425, migration v19): written by
190
+ // stageImportance, the enrich path, and the rescore-importance backfill —
191
+ // the moment a row's base_strength is settled under some rubric.
192
+ "importance_scored_at",
175
193
  ]);
176
194
  /**
177
195
  * Update specific fields on a memory.
@@ -198,6 +216,42 @@ function strengthenMemory(db, memoryId, nowIsoStr) {
198
216
  SET access_count = access_count + 1, last_accessed = ?
199
217
  WHERE id = ?`).run(nowIsoStr, memoryId);
200
218
  }
219
+ /**
220
+ * Record an owner corroboration (#423 phase 3): one UPDATE that bumps
221
+ * corroboration_count, nudges base_strength up by the calibration delta
222
+ * (MIN-capped at the importance ceiling, #425; COALESCE keeps NULL bases
223
+ * honest — the unscored default), stamps the importance watermark, and
224
+ * refreshes last_accessed (the console's "last confirmed" cell reads it: an
225
+ * enrich IS a confirmation, and it keeps the stage fading gate honest).
226
+ * EVIDENCE ABOUT IMPORTANCE ONLY — never access_count (reserved for real
227
+ * recall use) nor shown_count (index exposure): faking either corrupts the
228
+ * uses-per-showing adoption metric.
229
+ *
230
+ * Nightly interaction: the importance pool keys on importance_scored_at IS
231
+ * NULL (#425), so the watermark stamp here keeps the pre-#425 contract — an
232
+ * enriched memory leaves the pool and the owner's mark stands in for (and is
233
+ * never stomped by) the first LLM score. Dedup merges take
234
+ * max(base_strength), so a merge never loses the mark. Returns the fresh row
235
+ * values (re-read after the update); null when the id matches nothing.
236
+ */
237
+ function enrichMemory(db, memoryId, nowIsoStr) {
238
+ const result = db
239
+ .prepare(`UPDATE memories
240
+ SET corroboration_count = corroboration_count + 1,
241
+ base_strength = MIN(?, COALESCE(base_strength, 0.5) + ?),
242
+ importance_scored_at = COALESCE(importance_scored_at, ?),
243
+ last_accessed = ?
244
+ WHERE id = ?`)
245
+ .run(CALIBRATION.IMPORTANCE_CEILING, CALIBRATION.ENRICH_STRENGTH_DELTA, nowIsoStr, nowIsoStr, memoryId);
246
+ if (result.changes === 0)
247
+ return null;
248
+ const row = db
249
+ .prepare("SELECT corroboration_count, base_strength FROM memories WHERE id = ?")
250
+ .get(memoryId);
251
+ return row
252
+ ? { corroborationCount: row.corroboration_count, baseStrength: row.base_strength }
253
+ : null;
254
+ }
201
255
  /**
202
256
  * Record that memories appeared in a pushed recall index (#192): bump
203
257
  * shown_count and refresh last_accessed (a mild strengthen — the decay clock
@@ -679,14 +733,17 @@ function getAllLinkCounts(db) {
679
733
  return counts;
680
734
  }
681
735
  /**
682
- * Get all memories with default base_strength (never scored).
683
- * Absorbed memories are excluded (#384): they are invisible to recall, so
736
+ * Get all never-scored memories — the nightly importance pool, keyed on the
737
+ * importance_scored_at watermark (#425, migration v19). The pre-v19 sentinel
738
+ * (`base_strength = 0.5`) re-rolled every row the model genuinely scored
739
+ * 0.5, every night; NULL watermark = never scored under ANY rubric. Absorbed
740
+ * memories are excluded (#384): they are invisible to recall, so
684
741
  * importance-scoring one would spend an LLM call on dead evidence.
685
742
  */
686
743
  function getUnscoredMemories(db) {
687
744
  const rows = db
688
745
  .prepare(`SELECT * FROM memories
689
- WHERE base_strength = 0.5 AND COALESCE(status, '') != 'absorbed'
746
+ WHERE importance_scored_at IS NULL AND COALESCE(status, '') != 'absorbed'
690
747
  ORDER BY ingested_at ASC`)
691
748
  .all();
692
749
  return rows.map(rowToMemory);
package/dist/types.d.ts CHANGED
@@ -42,6 +42,26 @@ export interface Memory {
42
42
  * kept as evidence and rollback reference).
43
43
  */
44
44
  status?: string | null;
45
+ /**
46
+ * Explicit owner corroboration count (#423 phase 3, migration v17). Each
47
+ * POST /enrich bumps it together with base_strength (+the calibration
48
+ * delta, capped at the importance ceiling) — EVIDENCE ABOUT IMPORTANCE,
49
+ * never access (access_count) nor index exposure (shown_count). Optional
50
+ * because rowToMemory is a cast over SELECT * rows; 0 (the column default)
51
+ * on all pre-v17 rows and until first enriched.
52
+ */
53
+ corroboration_count?: number;
54
+ /**
55
+ * Importance scored-at watermark (#425, migration v19). NULL = never
56
+ * scored (the nightly's unscored pool); a timestamp = this row's
57
+ * base_strength is settled under some rubric and the nightly will not
58
+ * re-roll it. Written by stageImportance, the enrich path (the owner's
59
+ * mark stands in for the first LLM score), and the rescore-importance
60
+ * backfill. Optional because rowToMemory is a cast over SELECT * rows;
61
+ * pre-v19 rows were stamped by the migration backfill (except sentinel
62
+ * rows, which stay NULL for exactly one scoring under the new rubric).
63
+ */
64
+ importance_scored_at?: string | null;
45
65
  }
46
66
  /** A link between two memories. */
47
67
  export interface MemoryLink {
@@ -282,6 +302,30 @@ export interface ConsolidationReport {
282
302
  explicit_divergent: number;
283
303
  /** reconsolidationCursor after this run (unchanged in dry-run). */
284
304
  cursor: number;
305
+ /**
306
+ * #439: verdict calls on pairs whose candidate rowid was at/below the
307
+ * run-start scan high-water (state.reconsolidationScannedRowid) —
308
+ * re-judgments of work a prior run already judged but could not apply
309
+ * (the cursor held below it). Convergence evidence: this number must
310
+ * fall to 0 once the backlog drains. pairs_evaluated =
311
+ * pairs_reevaluated + pairs_new.
312
+ */
313
+ pairs_reevaluated: number;
314
+ /** #439: verdict calls on candidates ABOVE the high-water — first-time judgments. */
315
+ pairs_new: number;
316
+ /**
317
+ * #439: snapshot candidates skipped because a mid-scan absorb (a merge
318
+ * loser or rewrite trigger absorbed at an earlier candidate's boundary)
319
+ * had already absorbed them — the scan-stability guard.
320
+ */
321
+ skipped_absorbed: number;
322
+ /**
323
+ * #439: confirmed merge pairs still un-applied at run end — deadline
324
+ * deferrals at a boundary, a failed pre-merge backup, and lock-busy
325
+ * survivors of the final drain. Each holds the cursor below its
326
+ * candidate and re-detects next run.
327
+ */
328
+ merge_pairs_deferred: number;
285
329
  /**
286
330
  * #392: the deterministic merge zone's own report (pairs >= the
287
331
  * ceiling, union-find merged, zero LLM). Present on every run —
@@ -746,6 +790,10 @@ export interface InsertMemoryOptions {
746
790
  sourceAgentId?: string | null;
747
791
  /** Client-declared topic/domain of the capturing agent. Provenance only. */
748
792
  sourceDomain?: string | null;
793
+ /** Machine the capture ran on (#421 machine × harness identity). Provenance
794
+ * only — stamped by the nightly (config `machineName` ?? os.hostname()),
795
+ * accepted optionally from /distill + /ingest. Null on pre-v15 rows. */
796
+ sourceMachine?: string | null;
749
797
  project?: string | null;
750
798
  /** 0.16.x: vestigial — stored but never filtered. null (or absent) when the
751
799
  * caller doesn't declare one; an explicit value is honored as-is. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gamaze/hicortex",
3
- "version": "0.20.9",
3
+ "version": "0.21.0",
4
4
  "description": "Persistent agent identity for AI agents \u2014 a hand-edited identity layer, nightly-distilled experience, and lessons injected every session, shared across your whole fleet. Works with Hermes, OpenClaw, Claude Code, Pi, and opencode.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -43,6 +43,8 @@
43
43
  "test:watch": "vitest",
44
44
  "eval": "node dist/eval/run-eval.js",
45
45
  "eval:planted": "node dist/eval/planted-eval.js",
46
+ "eval:ranking": "node dist/eval/ranking-eval.js",
47
+ "eval:importance": "node dist/eval/importance-eval.js",
46
48
  "eval:recall-sweep": "node dist/eval/recall-sweep.js",
47
49
  "eval:relevance": "node dist/eval/relevance-eval.js",
48
50
  "prepack": "npm run build && rm -rf ./hermes-plugin && mkdir -p ./hermes-plugin && cp -r ../../hermes-plugin/hicortex ./hermes-plugin/ && find ./hermes-plugin -name __pycache__ -type d -exec rm -rf {} + 2>/dev/null || true && rm -rf ./pi-extension && mkdir -p ./pi-extension && cp -r ../../pi-extension/hicortex ./pi-extension/ && rm -rf ./opencode-plugin && mkdir -p ./opencode-plugin && cp -r ../../opencode-plugin/hicortex ./opencode-plugin/",