@gamaze/hicortex 0.20.7 → 0.20.10

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 (86) hide show
  1. package/README.md +18 -41
  2. package/assets/dashboard.html +3989 -836
  3. package/dist/calibration.d.ts +293 -0
  4. package/dist/calibration.js +379 -0
  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 +24 -3
  10. package/dist/capture.js +11 -1
  11. package/dist/classify-domains.d.ts +6 -0
  12. package/dist/classify-domains.js +7 -1
  13. package/dist/cli.js +38 -3
  14. package/dist/config-read.d.ts +1 -1
  15. package/dist/config-read.js +96 -9
  16. package/dist/consolidate.d.ts +114 -68
  17. package/dist/consolidate.js +302 -182
  18. package/dist/dashboard.d.ts +326 -6
  19. package/dist/dashboard.js +592 -7
  20. package/dist/db.js +105 -0
  21. package/dist/dedup.d.ts +34 -26
  22. package/dist/dedup.js +91 -57
  23. package/dist/distiller.js +1 -1
  24. package/dist/domain-classify.d.ts +7 -6
  25. package/dist/domain-classify.js +12 -10
  26. package/dist/eval/decay-eval.d.ts +3 -3
  27. package/dist/eval/decay-eval.js +4 -4
  28. package/dist/eval/importance-eval.d.ts +85 -0
  29. package/dist/eval/importance-eval.js +286 -0
  30. package/dist/eval/planted-eval.d.ts +26 -0
  31. package/dist/eval/planted-eval.js +97 -0
  32. package/dist/eval/planted-fixtures.d.ts +107 -0
  33. package/dist/eval/planted-fixtures.js +283 -0
  34. package/dist/eval/planted-harness.d.ts +176 -0
  35. package/dist/eval/planted-harness.js +649 -0
  36. package/dist/eval/ranking-battery.d.ts +78 -0
  37. package/dist/eval/ranking-battery.js +181 -0
  38. package/dist/eval/ranking-eval.d.ts +41 -0
  39. package/dist/eval/ranking-eval.js +391 -0
  40. package/dist/eval/ranking-fixtures.d.ts +77 -0
  41. package/dist/eval/ranking-fixtures.js +226 -0
  42. package/dist/identity-store.d.ts +21 -0
  43. package/dist/identity-store.js +49 -0
  44. package/dist/index.js +4 -3
  45. package/dist/init.d.ts +23 -3
  46. package/dist/init.js +84 -9
  47. package/dist/llm.d.ts +43 -58
  48. package/dist/llm.js +87 -101
  49. package/dist/mcp-server.d.ts +12 -0
  50. package/dist/mcp-server.js +213 -32
  51. package/dist/nightly.d.ts +9 -1
  52. package/dist/nightly.js +164 -110
  53. package/dist/nofit.d.ts +4 -11
  54. package/dist/nofit.js +6 -23
  55. package/dist/prompts.d.ts +10 -0
  56. package/dist/prompts.js +28 -5
  57. package/dist/recall-index.d.ts +30 -28
  58. package/dist/recall-index.js +21 -18
  59. package/dist/recall-registry.d.ts +2 -1
  60. package/dist/recall-registry.js +35 -1
  61. package/dist/reconsolidation.d.ts +168 -87
  62. package/dist/reconsolidation.js +818 -377
  63. package/dist/relink.js +3 -4
  64. package/dist/rescore-importance.d.ts +80 -0
  65. package/dist/rescore-importance.js +236 -0
  66. package/dist/retrieval.d.ts +80 -35
  67. package/dist/retrieval.js +322 -105
  68. package/dist/run-deadline.d.ts +62 -0
  69. package/dist/run-deadline.js +73 -0
  70. package/dist/schema-prototypes.d.ts +3 -3
  71. package/dist/schema-prototypes.js +3 -3
  72. package/dist/stages.d.ts +37 -0
  73. package/dist/stages.js +51 -0
  74. package/dist/state.d.ts +34 -9
  75. package/dist/storage.d.ts +50 -18
  76. package/dist/storage.js +125 -30
  77. package/dist/telemetry.d.ts +8 -7
  78. package/dist/token-budget.js +3 -4
  79. package/dist/type-classify.js +4 -4
  80. package/dist/types.d.ts +143 -155
  81. package/domains.example.json +4 -5
  82. package/hermes-plugin/hicortex/README.md +2 -2
  83. package/openclaw.plugin.json +1 -1
  84. package/package.json +4 -1
  85. package/pi-extension/hicortex/README.md +1 -1
  86. package/server.json +3 -3
@@ -151,10 +151,10 @@ async function classifyMemoryType(content, llm) {
151
151
  for (let attempt = 0; attempt < 2; attempt++) {
152
152
  let raw;
153
153
  try {
154
- // No per-call cap (#391): the classify-tier ceiling (classifyMaxTokens,
155
- // default 1024) resolves inside completeClassify — a hardcoded 20
156
- // starved reasoning models whose thinking ate the whole output budget.
157
- const r = await llm.completeClassify(prompt);
154
+ // No per-call cap (#391/#405): maxTokens — the ONE ceiling — resolves
155
+ // inside complete(); the old hardcoded 20 starved reasoning models
156
+ // whose thinking ate the whole output budget.
157
+ const r = await llm.complete(prompt);
158
158
  raw = r.text;
159
159
  }
160
160
  catch (err) {
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 {
@@ -91,6 +111,8 @@ export interface ResolutionBandStat {
91
111
  merge: number;
92
112
  corrects: number;
93
113
  supersedes: number;
114
+ /** #393 guard-C: verdicts that flagged a genuine conflict (link, both kept). */
115
+ conflicts: number;
94
116
  none: number;
95
117
  /** Merge verdicts below the confidence gate — both memories kept. */
96
118
  merge_below_gate: number;
@@ -100,16 +122,15 @@ export interface ResolutionBandStat {
100
122
  metadata_skipped?: number;
101
123
  }
102
124
  /**
103
- * Report of the deterministic merge zone (#392) — the >= dedupAutoMergeThreshold
104
- * band, merged by the dedup core's union-find clustering with ZERO LLM calls.
125
+ * Report of the deterministic merge zone (#392) — the band at/above the merge
126
+ * ceiling (release-managed since #408; was the dedupAutoMergeThreshold config
127
+ * key), merged by the dedup core's union-find clustering with ZERO LLM calls.
105
128
  * Computed in dedup.ts (runDeterministicMergeZone); surfaced verbatim as
106
129
  * `stages.reconsolidation.merges`.
107
130
  */
108
131
  export interface DeterministicMergeZoneReport {
109
- /** The cosine ceiling in force (config dedupAutoMergeThreshold; default 0.92). */
132
+ /** The cosine ceiling in force (release-managed calibration; default 0.92). */
110
133
  threshold: number;
111
- /** The pacing cap in force (dedupNightlyMaxMerges; 0 = machinery disabled). */
112
- max_merges: number;
113
134
  /** Every cluster found at the threshold (mergeable + mismatch-skipped). */
114
135
  clusters_found: number;
115
136
  /** Clusters that passed the metadata rails (would merge). */
@@ -122,8 +143,19 @@ export interface DeterministicMergeZoneReport {
122
143
  links_repointed: number;
123
144
  /** Clusters skipped — members disagree on project / source_agent. */
124
145
  skipped_metadata_mismatch: number;
125
- /** Mergeable clusters NOT attempted because the pacing cap was exhausted. */
146
+ /**
147
+ * #393 guard-C: clusters skipped because a member pair holds a `conflicts`
148
+ * link — a judge-flagged genuine conflict is never blended, both records
149
+ * stay live.
150
+ */
151
+ skipped_conflict: number;
152
+ /**
153
+ * Mergeable clusters NOT attempted (pacing cap retired, #405): the run
154
+ * deadline fired before them. Deferred clusters drain on the next run.
155
+ */
126
156
  capped: number;
157
+ /** #405: clusters in `capped` that stopped specifically on the deadline. */
158
+ deadline_deferred?: number;
127
159
  /** Clusters whose merge transaction failed (rolled back; retried next run). */
128
160
  failed: number;
129
161
  /** Apply only: the capture lock was busy — zero merges, fail-soft. */
@@ -144,7 +176,14 @@ export interface ConsolidationReport {
144
176
  started_at: string;
145
177
  completed_at?: string;
146
178
  dry_run: boolean;
147
- status: "completed" | "skipped" | "failed";
179
+ /**
180
+ * "deferred" (#405): the run-wide wall-clock deadline
181
+ * (nightlyTimeBudgetMinutes) fired — at least one stage stopped at a safe
182
+ * boundary and its remaining work drains on the next run (cursors hold
183
+ * below it). Like "endpoint_down" it must NOT advance lastConsolidated,
184
+ * so the pending-set queries re-find the deferred work.
185
+ */
186
+ status: "completed" | "skipped" | "failed" | "deferred";
148
187
  elapsed_seconds?: number;
149
188
  stages: {
150
189
  precheck?: {
@@ -178,7 +217,8 @@ export interface ConsolidationReport {
178
217
  primaries_updated?: number;
179
218
  /**
180
219
  * No-fit path: memories that earned a WEAK primary (argmax prototype
181
- * cosine >= weakPrimaryFloor) after the LLM found no fitting domain.
220
+ * cosine >= the weak-primary floor — release-managed since #408) after
221
+ * the LLM found no fitting domain.
182
222
  */
183
223
  weak_primary?: number;
184
224
  /**
@@ -217,7 +257,8 @@ export interface ConsolidationReport {
217
257
  * Reconsolidation (#384) — runs after supersession, before decay/prune.
218
258
  * Since #392 this is THE unified resolution stage: its verdict also carries
219
259
  * a `merge` disposition, and the deterministic merge zone (pairs at/above
220
- * `dedupAutoMergeThreshold`) runs inside it, LLM-free, before the scan.
260
+ * the merge ceiling — release-managed since #408) runs inside it,
261
+ * LLM-free, before the scan.
221
262
  */
222
263
  reconsolidation?: {
223
264
  /** Candidates examined this run (rowid > cursor; no shape filter). */
@@ -225,14 +266,17 @@ export interface ConsolidationReport {
225
266
  /** Pairs actually sent to the verdict LLM (detection + explicit-mark verification). */
226
267
  pairs_evaluated: number;
227
268
  /**
228
- * #394: pairs the similarity floor discovered this run (KNN neighbors
229
- * at/above correctionMinSimilarity), counted before any skip or
230
- * judgment — the only sizing number a dry-run can show, where
231
- * pairs_evaluated is always 0.
269
+ * #394: pairs the detection sources discovered this run, counted before
270
+ * any skip or judgment — the only sizing number a dry-run can show,
271
+ * where pairs_evaluated is always 0. #393 B: BOTH sources join this
272
+ * total (KNN neighbors at/above the correction floor + the scout's
273
+ * FTS hits on correction-shaped memories; see scout_candidates_found
274
+ * for the scout's share).
232
275
  */
233
276
  pairs_discovered: number;
234
277
  /** #394: discovered pairs with no resolution link yet — the actionable
235
- * candidates (deterministic-zone work + would-be verdict calls). */
278
+ * candidates (deterministic-zone work + would-be verdict calls). #393 B:
279
+ * covers both detection sources. */
236
280
  pairs_discovered_unlinked: number;
237
281
  /** Targets rewritten in place this run (one history row each). */
238
282
  rewritten: number;
@@ -244,7 +288,7 @@ export interface ConsolidationReport {
244
288
  marked_superseded: number;
245
289
  /** Memories marked status 'retracted' (mark-only: below gate / non-fact / failed contract). */
246
290
  marked_retracted: number;
247
- /** Verdicts that were `corrects` but below correctionRewriteMinConfidence. */
291
+ /** Verdicts that were `corrects` but below the rewrite confidence gate. */
248
292
  below_gate: number;
249
293
  /** Rewrite groups degraded to mark-only on a failed rewrite contract. */
250
294
  contract_failed: number;
@@ -258,6 +302,30 @@ export interface ConsolidationReport {
258
302
  explicit_divergent: number;
259
303
  /** reconsolidationCursor after this run (unchanged in dry-run). */
260
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;
261
329
  /**
262
330
  * #392: the deterministic merge zone's own report (pairs >= the
263
331
  * ceiling, union-find merged, zero LLM). Present on every run —
@@ -267,11 +335,13 @@ export interface ConsolidationReport {
267
335
  merges: DeterministicMergeZoneReport;
268
336
  /** #392: judged-zone pair merges applied this run (merge verdicts at/above the confidence gate). */
269
337
  merge_pairs_applied: number;
270
- /** #392: merge verdicts below correctionRewriteMinConfidence — both memories kept. */
338
+ /** #392: merge verdicts below the rewrite confidence gate — both memories kept. */
271
339
  merge_below_gate: number;
272
340
  /**
273
341
  * #392: pairs the scan saw at/above the ceiling — owned by the
274
342
  * deterministic zone (or waiting for its cap), never LLM-judged.
343
+ * #393 B: similarity-source pairs only; the scout's FTS pairs are
344
+ * exempt (no similarity gate — cosine never blocks that source).
275
345
  */
276
346
  skipped_above_ceiling: number;
277
347
  /**
@@ -280,6 +350,34 @@ export interface ConsolidationReport {
280
350
  * the verdict was rendered, this is not an infra failure.
281
351
  */
282
352
  skipped_metadata_mismatch: number;
353
+ /**
354
+ * #393 guard-C: verdicts that flagged a genuine conflict — a `conflicts`
355
+ * link was written, both memories stay live (no status change, no
356
+ * rewrite, no merge queue).
357
+ */
358
+ conflict_flagged: number;
359
+ /**
360
+ * #393 guard-C: merges refused because the pair (deterministic-zone
361
+ * cluster or judged merge) holds a `conflicts` link — aggregates the
362
+ * judged-path refusals plus the zone's `skipped_conflict`. Both records
363
+ * kept live in every case.
364
+ */
365
+ conflict_skipped: number;
366
+ /**
367
+ * #393 B: memories given the scout's correction-shape call this run
368
+ * (ONE classify-tier call per scanned candidate; always 0 on dry-run —
369
+ * the shape call is LLM work, and dry-runs make zero LLM calls).
370
+ */
371
+ scout_scanned: number;
372
+ /** #393 B: shape verdicts that flagged a correction/retraction/supersession. */
373
+ scout_correction_shaped: number;
374
+ /**
375
+ * #393 B: FTS hits that became candidate pairs — the scout's share of
376
+ * pairs_discovered (after older-only/self filtering and dedup against
377
+ * the KNN neighbors; a pair found by both sources counts as
378
+ * similarity). Counted before the idempotency skip, the #394 discipline.
379
+ */
380
+ scout_candidates_found: number;
283
381
  /**
284
382
  * #392: per-run verdict statistics by cosine band ("0.75-0.8" …
285
383
  * ">=0.92"; labels derive from the live floor/ceiling). Calibration
@@ -407,7 +505,7 @@ export interface HicortexConfig {
407
505
  * Server-mode `init` scaffolds a generic 5-domain default (Work, Personal,
408
506
  * People, Health, Finance — see GENERIC_DEFAULT_DOMAINS in init.ts) when
409
507
  * this key is absent, and NEVER touches an existing list. A power-user
410
- * example (custom weakPrimaryFloor) ships as
508
+ * example (a wider life-sphere set) ships as
411
509
  * domains.example.json in the package root.
412
510
  *
413
511
  * NO fallback bucket is needed or special-cased (owner amendment 07.07):
@@ -437,14 +535,28 @@ export interface HicortexConfig {
437
535
  */
438
536
  consolidationHours?: number[];
439
537
  /**
440
- * Ceiling on total LLM calls across all classify-tier consolidation stages
441
- * (content-domain, link discovery, supersession) per nightly run (0.17, #241).
442
- * A runaway backstop, not a throughput throttle — on a free local model the
443
- * binding constraint is the nightly unit's wall-clock timeout, not call count.
444
- * Default `5000` (was a hard-coded 200 that starved link/supersession during a
445
- * classification backlog and drained large backlogs at ~cap/night).
446
- */
447
- consolidateMaxLlmCalls?: number;
538
+ * The ONE wall-clock budget (minutes) for a nightly run (#405): capture +
539
+ * every consolidation stage share one cooperative deadline, checked at safe
540
+ * boundaries (capture segments, stage boundaries, item loops, the merge
541
+ * zone). A run whose deadline fires reports consolidation status
542
+ * "deferred" and resumes from its cursors next run — no work is lost or
543
+ * redone. Default 240; 0 or invalid → default (a deadline ALWAYS exists —
544
+ * unlike the retired reconsolidationMaxMinutes, 0 is not "off"). The
545
+ * systemd unit's TimeoutStartSec is derived from this (budget + 60 min
546
+ * slack) at init.
547
+ */
548
+ nightlyTimeBudgetMinutes?: number;
549
+ /**
550
+ * The ONE per-run ceiling on LLM calls across the whole nightly pipeline
551
+ * (#405; successor of consolidateMaxLlmCalls, #241). Consumed in run
552
+ * order — run order IS the fair share; a stage that exhausts the budget
553
+ * defers its remainder to the next run via its cursor. Bounds money/load
554
+ * independent of latency: a fast metered or capacity-limited endpoint
555
+ * permits thousands of calls inside the wall-clock budget. Default 5000;
556
+ * 0 or invalid → default. The legacy `consolidateMaxLlmCalls` key is
557
+ * honored as a deprecated alias for one release.
558
+ */
559
+ nightlyLlmCallBudget?: number;
448
560
  /**
449
561
  * Release channel pinned into the generated daemon/timer ExecStart for
450
562
  * **npx-thin** installs (global-binary installs use the absolute binary path
@@ -454,36 +566,6 @@ export interface HicortexConfig {
454
566
  * Absent → auto-detect (bare on `latest`, else `@next`). (0.17.1)
455
567
  */
456
568
  updateChannel?: string;
457
- /**
458
- * Minimum cosine(memory embedding, best domain prototype) for a no-fit
459
- * memory to earn a WEAK primary instead of decaying (see nofit.ts).
460
- * Number in (0, 1); default 0.45. Tune from the corpus weight distribution
461
- * (the memory_tags.weight histogram of LLM-tagged rows — set the floor
462
- * near its lower tail). See domains.example.json for a worked example.
463
- */
464
- weakPrimaryFloor?: number;
465
- /**
466
- * Per-attempt timeout (ms) for the CLIENT nightly's pre-flight GET /health
467
- * check before capturing (#163). Default 15000. Overridable per machine —
468
- * a wired Pi vs a sleeping laptop want different values. See runClientNightly
469
- * in nightly.ts. No effect in server mode (server capture is localhost).
470
- */
471
- preflightTimeoutMs?: number;
472
- /**
473
- * Max attempts for the client nightly's pre-flight /health retry loop (#163).
474
- * Default 3. Attempts are spaced preflightRetryGapMs apart; on exhaustion the
475
- * run aborts with a non-zero exit code and an ok=false telemetry ping so the
476
- * failure is visible to systemd/launchd and the activity aggregate.
477
- */
478
- preflightAttempts?: number;
479
- /**
480
- * Gap (ms) between pre-flight /health attempts in the client nightly (#163).
481
- * Default 60000. Wall-clock-optimistic on a sleeping laptop — setTimeout does
482
- * NOT advance while macOS is asleep, so real elapsed time can exceed the
483
- * nominal worst case. Not a defect (capture lock isn't held; cursor design is
484
- * dup-over-loss); just don't treat the nominal sum as a hard bound.
485
- */
486
- preflightRetryGapMs?: number;
487
569
  /**
488
570
  * Max output tokens for the ONE LLM model used by all phases — distillation,
489
571
  * reflection, classification, and scoring. Default 8192. An explicit value
@@ -492,18 +574,6 @@ export interface HicortexConfig {
492
574
  * when it finishes early. Read in llm.ts; see #220.
493
575
  */
494
576
  maxTokens?: number;
495
- /**
496
- * Max output tokens for the classify tier ONLY — the short JSON-verdict
497
- * calls: correction/supersession verdicts, rewrite contracts, and type +
498
- * domain tag classification. Default 1024. A ceiling, not a target
499
- * (generation stops at the model's natural end) — raise it when a
500
- * reasoning-style model spends the budget on internal reasoning and returns
501
- * empty verdicts (the pre-#391 hardcoded per-call caps starved exactly that
502
- * shape; a local non-reasoning model is unaffected by the raise).
503
- * `maxTokens` continues to govern the heavy phases (distill/reflect).
504
- * Read in llm.ts; see #391.
505
- */
506
- classifyMaxTokens?: number;
507
577
  /**
508
578
  * Toggle the model's internal reasoning ("thinking") stream on the openai-compat
509
579
  * path — applies to ALL phases (distill / reflect / classify / scoring) since one
@@ -519,38 +589,6 @@ export interface HicortexConfig {
519
589
  * anthropic or claude-cli paths. See #220, #231.
520
590
  */
521
591
  enableThinking?: boolean;
522
- /**
523
- * Context window for ollama (the one model, all phases). Default 8192 — the point
524
- * where context stops being the binding constraint for a sub-8B model on ollama
525
- * (above it the SMALL_MODEL_MAX_CHUNK_CHARS speed cap binds instead, so extra
526
- * context buys nothing). Also drives `detectChunkSize`'s chunk sizing
527
- * (chunkChars ≤ numCtx × 0.6 × 4 chars), so numCtx is the single dial and the
528
- * chunker/request agreement is enforced by construction (#228). For an ≥8B model
529
- * on ollama the speed cap is 60,000 chars, needing numCtx ≈ 25000 to reach — raise
530
- * it if running 8B+ locally. No effect for non-ollama providers.
531
- */
532
- numCtx?: number;
533
- /**
534
- * Flush ollama's accumulated memory every N scoring calls — workaround for
535
- * ollama's per-request memory growth (the runner's RSS climbs ~171 MB/call and
536
- * isn't freed between requests), which swap-thrashes RAM-constrained boxes
537
- * during long consolidations. Default 0 (off). When >0, every Nth scoring call
538
- * (`completeFast`) triggers a `keep_alive:0` unload + an `ollamaFlushWaitMs`
539
- * pause for the runner to exit + release, then the next call reloads fresh.
540
- * N=15 caps a cycle at ~2.5 GB. Scoped to the fast tier (scoring) only. Note:
541
- * N counts **logical** scoring calls, not raw HTTP requests — `complete()`
542
- * retries up to 4× on timeout, so under retry pressure the actual accumulation
543
- * may be up to 4×N calls' worth. In practice the flush prevents the thrash that
544
- * causes retries, keeping the count accurate.
545
- */
546
- ollamaFlushEvery?: number;
547
- /**
548
- * Milliseconds to wait after an ollama flush (`keep_alive:0`) for the runner
549
- * to exit + release its accumulated memory before the next call reloads.
550
- * Default 180000 (3 min — the runner takes >90 s to exit after keep_alive:0;
551
- * doubled for margin). Only relevant when `ollamaFlushEvery` > 0.
552
- */
553
- ollamaFlushWaitMs?: number;
554
592
  /**
555
593
  * ONE per-attempt timeout ceiling (ms) for every LLM phase — distill,
556
594
  * reflect, classify, and scoring alike (#337). Default 900000 (15 min). The
@@ -564,21 +602,6 @@ export interface HicortexConfig {
564
602
  * claude-cli (subprocess timeout).
565
603
  */
566
604
  llmTimeoutMs?: number;
567
- /**
568
- * Consecutive ladder-exhausted TOTAL failures (fetch-failed / ECONNREFUSED /
569
- * timeout / "Headers Timeout" class) after which the per-endpoint circuit
570
- * breaker opens (#337). Default 3; `0` disables. While open, calls throw
571
- * `LlmCircuitOpenError` immediately with NO network I/O. HTTP error statuses
572
- * with a response, parse errors, and rate limits never count (they throw
573
- * before the retry ladder can be exhausted). Any success resets the counter.
574
- */
575
- llmBreakerThreshold?: number;
576
- /**
577
- * How long (ms) an open circuit breaker stays open before the next call
578
- * becomes a half-open trial (#337). Default 600000 (10 min). A trial failure
579
- * re-opens the breaker; a trial success resets it.
580
- */
581
- llmBreakerCooldownMs?: number;
582
605
  /**
583
606
  * Timeout (ms) for the readiness probe's single 1-token generation attempt
584
607
  * (#337). Default 60000. The probe asks "can this endpoint GENERATE", which
@@ -610,8 +633,8 @@ export interface HicortexConfig {
610
633
  /**
611
634
  * Max memories per recall on the OpenClaw plugin's legacy /search fallback
612
635
  * (pre-0.14 servers, #316). Default 8. Does NOT size the pushed
613
- * /recall-index — that is server config (`recallMaxItems`); the server
614
- * accepts no client limit.
636
+ * /recall-index — that is sized by the server's release-managed calibration
637
+ * (#408); the server accepts no client limit.
615
638
  */
616
639
  recallLimit?: number;
617
640
  /**
@@ -706,45 +729,6 @@ export interface HicortexConfig {
706
729
  orgName?: string;
707
730
  /** Plan/tier label rendered as a small badge (e.g. "Cloud · Early bird"). */
708
731
  planLabel?: string;
709
- /**
710
- * Minimum cosine similarity for a reconsolidation candidate pair (#384):
711
- * each new-since-cursor memory is paired with up to 5 older KNN neighbors
712
- * at/above this bar before the verdict call. Default 0.75 — a touch wider
713
- * than the supersession stage's 0.80 because a retraction often rides inside
714
- * an otherwise unrelated memory; the verdict + confidence gate carry the
715
- * precision. Number in (0, 1]; invalid/absent keeps the default.
716
- */
717
- correctionMinSimilarity?: number;
718
- /**
719
- * Minimum verdict confidence for the REWRITE fork of reconsolidation (#384):
720
- * a `corrects` verdict at/above this bar on a fact-shaped target is rewritten
721
- * in place; below it the pair degrades to mark-only (a weak mark is
722
- * recoverable, a weak rewrite is corruption). Default 0.80. Number in
723
- * (0, 1]; invalid/absent keeps the default. Since #392 this same gate also
724
- * decides whether a `merge` verdict is applied (analogous reasoning: a weak
725
- * merge keeps both memories, a confirmed merge hides one).
726
- */
727
- correctionRewriteMinConfidence?: number;
728
- /**
729
- * Deterministic merge ceiling for the unified resolution pass (#392): memory
730
- * pairs at/above this cosine are merged by the dedup core's union-find
731
- * clustering with ZERO LLM calls; pairs in [correctionMinSimilarity, this
732
- * value) get the one unified verdict call (merge/corrects/supersedes/none).
733
- * Default 0.92 (the #100/#191 calibration). The legacy `dedupMergeThreshold`
734
- * key is honored as a fallback when this key is absent. Number in (0, 1];
735
- * invalid/absent keeps the default. Also read by the manual `hicortex dedup`
736
- * CLI (same precedence: --threshold > this key > legacy key > 0.92).
737
- */
738
- dedupAutoMergeThreshold?: number;
739
- /**
740
- * Pacing cap on merge OPERATIONS per nightly run (#392): deterministic-zone
741
- * clusters plus judged pair merges count against ONE cap, so a
742
- * misbehaving-distiller burst is bounded and a large pre-existing backlog
743
- * drains over a few nights rather than in one run. Default 250. `0` disables
744
- * the merge machinery entirely (the deterministic zone is skipped; a
745
- * confirmed merge verdict keeps both memories). Non-negative integer.
746
- */
747
- dedupNightlyMaxMerges?: number;
748
732
  }
749
733
  /** A config-owned life-sphere domain (see HicortexConfig.domains). */
750
734
  export interface DomainDef {
@@ -806,6 +790,10 @@ export interface InsertMemoryOptions {
806
790
  sourceAgentId?: string | null;
807
791
  /** Client-declared topic/domain of the capturing agent. Provenance only. */
808
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;
809
797
  project?: string | null;
810
798
  /** 0.16.x: vestigial — stored but never filtered. null (or absent) when the
811
799
  * caller doesn't declare one; an explicit value is honored as-is. */
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "_readme": [
3
3
  "Hicortex memory domains — example config.",
4
- "Copy the `domains` key (and optionally `weakPrimaryFloor`) into ~/.hicortex/config.json on the SERVER machine.",
4
+ "Copy the `domains` key into ~/.hicortex/config.json on the SERVER machine.",
5
5
  "Domains are your top-level memory spheres. Each memory gets multiple weighted tags plus a derived primary.",
6
6
  "They can be life areas OR project/topic areas — edit to match how YOU think.",
7
7
  "The `domains` list below is the generic default that `hicortex init` scaffolds automatically.",
8
- "There is NO fallback category: memories that fit nothing are handled automatically (weak-primary floor + decay).",
8
+ "There is NO fallback category: memories that fit nothing are handled automatically (weak-primary floor + decay — release-managed calibration, not config).",
9
9
  "Backfill an existing corpus with: hicortex classify-domains"
10
10
  ],
11
11
  "domains": [
@@ -34,7 +34,7 @@
34
34
  "_readme": [
35
35
  "A narrower life-sphere set for users who want tighter buckets.",
36
36
  "The PRIMARY domain is derived by argmax association weight (LLM tag order breaks ties) — no manual override flag.",
37
- "`weakPrimaryFloor` (default 0.45) is the minimum embedding similarity for a no-fit memory to earn a weak primary; tune it from your corpus."
37
+ "The weak-primary floor is release-managed since #408 (a calibration constant shipped with each release) — not a config key."
38
38
  ],
39
39
  "domains": [
40
40
  {
@@ -69,7 +69,6 @@
69
69
  "name": "Travel",
70
70
  "description": "Trips, destinations, bookings, travel plans"
71
71
  }
72
- ],
73
- "weakPrimaryFloor": 0.5
72
+ ]
74
73
  }
75
74
  }
@@ -22,7 +22,7 @@ That's the whole surface. No `sync_turn`, no compaction/session-end capture —
22
22
 
23
23
  ### Pushed recall index (0.7.0, server ≥ 0.14)
24
24
 
25
- Instead of injecting full memory content every turn, `prefetch` sends the user's message to the server's `POST /recall-index` and injects the returned **index block** verbatim — one line per memory (id, title, date), capped and relevance-gated server-side. The agent fetches full content with `hicortex_get(id)` only when a line is actually relevant; that fetch is what strengthens the memory (exposure ≠ use). All tuning knobs (`recallMaxItems`, `recallMinSimilarity`, `recallReshowTurns`, `recallMinPromptChars`, …) live in the **server** config — the plugin carries none. Dedup is turn-based and server-side per session; the plugin resets it at `initialize` (the Hermes `MemoryProvider` interface exposes no compaction signal, so a mid-session context rebuild cannot trigger a reset — the server's turn-based re-show window covers that gap). Against a pre-0.14 server (404) the plugin falls back to the 0.6.x `GET /search` full-content prefetch, fail-soft, re-probing the endpoint every 10 minutes so a later server upgrade is picked up without a gateway restart. The recall calls carry the profile's configured `default_project` (and `mission_domains`) and use a short dedicated timeout (1.5 s) so a slow server can never stall a turn. (`privacy_filter` is deprecated since 0.7.2 — the server ignores privacy; see [Configuration](#configure-activate).)
25
+ Instead of injecting full memory content every turn, `prefetch` sends the user's message to the server's `POST /recall-index` and injects the returned **index block** verbatim — one line per memory (id, title, date), capped and relevance-gated server-side. The agent fetches full content with `hicortex_get(id)` only when a line is actually relevant; that fetch is what strengthens the memory (exposure ≠ use). All tuning knobs (`recallMaxItems`, `recallMinSimilarity`, `recallReshowTurns`, `recallMinPromptChars`, …) are release-managed constants on the server (they ship with each release and change only with published eval evidence) — the plugin carries none, and neither does the server's config. Dedup is turn-based and server-side per session; the plugin resets it at `initialize` (the Hermes `MemoryProvider` interface exposes no compaction signal, so a mid-session context rebuild cannot trigger a reset — the server's turn-based re-show window covers that gap). Against a pre-0.14 server (404) the plugin falls back to the 0.6.x `GET /search` full-content prefetch, fail-soft, re-probing the endpoint every 10 minutes so a later server upgrade is picked up without a gateway restart. The recall calls carry the profile's configured `default_project` (and `mission_domains`) and use a short dedicated timeout (1.5 s) so a slow server can never stall a turn. (`privacy_filter` is deprecated since 0.7.2 — the server ignores privacy; see [Configuration](#configure-activate).)
26
26
 
27
27
  ### Per-agent standing context (0.13)
28
28
 
@@ -78,7 +78,7 @@ hermes memory setup # select "hicortex", enter the server URL/token when promp
78
78
 
79
79
  Run it once per profile if you use Hermes profiles. Hermes allows **one** external memory provider at a time, so disable Honcho (or any other) first, then restart the gateway.
80
80
 
81
- Setup asks exactly two questions: the **server URL** and the **auth token**. Everything else has a correct default and is configured — if ever needed — directly in `$HERMES_HOME/plugins/hicortex/config.json`: `default_project` (blank = memories unattributed), `recall_limit` (default 5; sizes the tools and the legacy `/search` fallback only — the pushed recall index is sized by SERVER config `recallMaxItems`), `agent_name` (blank auto-derives from the running profile; pin it only for fleet re-installs — see [Per-agent standing context](#per-agent-standing-context-013)), and `mission_domains` (blank = off; an optional recall boost keyed to the server's domain vocabulary). The auth token is a **secret** — set it via env, not the JSON file:
81
+ Setup asks exactly two questions: the **server URL** and the **auth token**. Everything else has a correct default and is configured — if ever needed — directly in `$HERMES_HOME/plugins/hicortex/config.json`: `default_project` (blank = memories unattributed), `recall_limit` (default 5; sizes the tools and the legacy `/search` fallback only — the pushed recall index is sized by the server's release-managed calibration), `agent_name` (blank auto-derives from the running profile; pin it only for fleet re-installs — see [Per-agent standing context](#per-agent-standing-context-013)), and `mission_domains` (blank = off; an optional recall boost keyed to the server's domain vocabulary). The auth token is a **secret** — set it via env, not the JSON file:
82
82
 
83
83
  ```bash
84
84
  export HICORTEX_AUTH_TOKEN=hctx-<your-token> # or your custom token
@@ -24,7 +24,7 @@
24
24
  "recallLimit": {
25
25
  "type": "number",
26
26
  "default": 8,
27
- "description": "Max memories per recall on the pre-0.14 /search fallback (default 8). The pushed recall index is sized by SERVER config (recallMaxItems) — the server accepts no client limit."
27
+ "description": "Max memories per recall on the pre-0.14 /search fallback (default 8). The pushed recall index is sized by the server's release-managed calibration — the server accepts no client limit."
28
28
  },
29
29
  "scaffoldDeadMan": {
30
30
  "type": "boolean",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gamaze/hicortex",
3
- "version": "0.20.7",
3
+ "version": "0.20.10",
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": {
@@ -42,6 +42,9 @@
42
42
  "test": "vitest run",
43
43
  "test:watch": "vitest",
44
44
  "eval": "node dist/eval/run-eval.js",
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",
45
48
  "eval:recall-sweep": "node dist/eval/recall-sweep.js",
46
49
  "eval:relevance": "node dist/eval/relevance-eval.js",
47
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/",
@@ -18,7 +18,7 @@ Gives [Pi](https://pi.dev) agents self-learning memory backed by a Hicortex serv
18
18
 
19
19
  ### Pushed recall index
20
20
 
21
- Each user prompt is POSTed to the server's `/recall-index`; the returned **index block** (one line per relevant memory: id, title, date) is injected into the turn. All gating and dedup knobs (`recallMaxItems`, `recallMinSimilarity`, `recallReshowTurns`, …) live in the **server** config — the extension carries none. A dedup reset fired at session start is awaited by the first turn's recall POST, so it can never land after it and wipe the turn state.
21
+ Each user prompt is POSTed to the server's `/recall-index`; the returned **index block** (one line per relevant memory: id, title, date) is injected into the turn. All gating and dedup knobs (`recallMaxItems`, `recallMinSimilarity`, `recallReshowTurns`, …) are release-managed constants on the server (they ship with each release and change only with published eval evidence) — the extension carries none, and neither does the server's config. A dedup reset fired at session start is awaited by the first turn's recall POST, so it can never land after it and wipe the turn state.
22
22
 
23
23
  ### Identity layer
24
24
 
package/server.json CHANGED
@@ -2,13 +2,13 @@
2
2
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
3
  "name": "io.github.gamaze-labs/hicortex",
4
4
  "title": "Hicortex",
5
- "description": "Shared fleet memory for AI agents \u2014 what one agent learns, the whole fleet knows.",
6
- "version": "0.20.5",
5
+ "description": "Shared fleet memory for AI agents: nightly self-correction, recall every prompt (supported agents).",
6
+ "version": "0.20.9",
7
7
  "packages": [
8
8
  {
9
9
  "registryType": "npm",
10
10
  "identifier": "@gamaze/hicortex",
11
- "version": "0.20.5",
11
+ "version": "0.20.9",
12
12
  "transport": {
13
13
  "type": "stdio",
14
14
  "command": "npx",