@gamaze/hicortex 0.20.7 → 0.20.9

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 (61) hide show
  1. package/README.md +10 -41
  2. package/dist/calibration.d.ts +174 -0
  3. package/dist/calibration.js +231 -0
  4. package/dist/capture.d.ts +15 -3
  5. package/dist/capture.js +10 -1
  6. package/dist/classify-domains.d.ts +6 -0
  7. package/dist/classify-domains.js +7 -1
  8. package/dist/cli.js +2 -3
  9. package/dist/config-read.d.ts +1 -1
  10. package/dist/config-read.js +96 -9
  11. package/dist/consolidate.d.ts +79 -68
  12. package/dist/consolidate.js +218 -174
  13. package/dist/dashboard.d.ts +4 -3
  14. package/dist/dedup.d.ts +34 -26
  15. package/dist/dedup.js +91 -57
  16. package/dist/distiller.js +1 -1
  17. package/dist/domain-classify.d.ts +7 -6
  18. package/dist/domain-classify.js +12 -10
  19. package/dist/eval/decay-eval.d.ts +3 -3
  20. package/dist/eval/decay-eval.js +4 -4
  21. package/dist/eval/planted-eval.d.ts +26 -0
  22. package/dist/eval/planted-eval.js +97 -0
  23. package/dist/eval/planted-fixtures.d.ts +107 -0
  24. package/dist/eval/planted-fixtures.js +283 -0
  25. package/dist/eval/planted-harness.d.ts +176 -0
  26. package/dist/eval/planted-harness.js +649 -0
  27. package/dist/index.js +4 -3
  28. package/dist/init.d.ts +9 -3
  29. package/dist/init.js +52 -9
  30. package/dist/llm.d.ts +43 -58
  31. package/dist/llm.js +87 -101
  32. package/dist/mcp-server.js +29 -29
  33. package/dist/nightly.js +105 -103
  34. package/dist/nofit.d.ts +4 -11
  35. package/dist/nofit.js +6 -23
  36. package/dist/recall-index.d.ts +30 -28
  37. package/dist/recall-index.js +21 -18
  38. package/dist/recall-registry.d.ts +2 -1
  39. package/dist/recall-registry.js +35 -1
  40. package/dist/reconsolidation.d.ts +124 -72
  41. package/dist/reconsolidation.js +359 -148
  42. package/dist/relink.js +3 -4
  43. package/dist/retrieval.d.ts +68 -35
  44. package/dist/retrieval.js +292 -104
  45. package/dist/run-deadline.d.ts +62 -0
  46. package/dist/run-deadline.js +73 -0
  47. package/dist/schema-prototypes.d.ts +3 -3
  48. package/dist/schema-prototypes.js +3 -3
  49. package/dist/state.d.ts +2 -3
  50. package/dist/storage.d.ts +16 -16
  51. package/dist/storage.js +62 -24
  52. package/dist/telemetry.d.ts +8 -7
  53. package/dist/token-budget.js +3 -4
  54. package/dist/type-classify.js +4 -4
  55. package/dist/types.d.ts +95 -155
  56. package/domains.example.json +4 -5
  57. package/hermes-plugin/hicortex/README.md +2 -2
  58. package/openclaw.plugin.json +1 -1
  59. package/package.json +2 -1
  60. package/pi-extension/hicortex/README.md +1 -1
  61. package/server.json +3 -3
@@ -121,9 +121,9 @@ export declare function computeTagWeights(db: Database.Database, memoryId: strin
121
121
  *
122
122
  * Used by the no-fit path (nofit.ts, owner amendment 07.07): when the LLM
123
123
  * says no domain fits, the memory can still earn a WEAK primary from pure
124
- * embedding association — provided the best cosine clears the configured
125
- * weakPrimaryFloor (the caller checks the floor; this function just reports
126
- * the argmax).
124
+ * embedding association — provided the best cosine clears the weak-primary
125
+ * floor (release-managed since #408; the caller checks the floor, this
126
+ * function just reports the argmax).
127
127
  *
128
128
  * Returns null when the memory has no stored vector or no configured domain
129
129
  * has a prototype (nothing to associate against). Ties resolve to the FIRST
@@ -239,9 +239,9 @@ function computeTagWeights(db, memoryId, tags, prototypes) {
239
239
  *
240
240
  * Used by the no-fit path (nofit.ts, owner amendment 07.07): when the LLM
241
241
  * says no domain fits, the memory can still earn a WEAK primary from pure
242
- * embedding association — provided the best cosine clears the configured
243
- * weakPrimaryFloor (the caller checks the floor; this function just reports
244
- * the argmax).
242
+ * embedding association — provided the best cosine clears the weak-primary
243
+ * floor (release-managed since #408; the caller checks the floor, this
244
+ * function just reports the argmax).
245
245
  *
246
246
  * Returns null when the memory has no stored vector or no configured domain
247
247
  * has a prototype (nothing to associate against). Ties resolve to the FIRST
package/dist/state.d.ts CHANGED
@@ -55,9 +55,8 @@ export interface HicortexState {
55
55
  * B) — highest memories.rowid whose decision/correction candidates have
56
56
  * been evaluated (or infra-skipped) this run. Absent/0 = never run. Unlike
57
57
  * relinkCursor/domainCursor (separate resumable CLI commands), this cursor
58
- * advances a SMALL amount per night (config `supersessionMaxCalls`, default
59
- * 30) as part of the regular nightly — the corpus is back-processed
60
- * gradually over many nights.
58
+ * advances within the shared nightly LLM call budget as part of the regular
59
+ * nightly — the corpus is back-processed gradually over many nights.
61
60
  */
62
61
  supersessionCursor?: number;
63
62
  /**
package/dist/storage.d.ts CHANGED
@@ -132,15 +132,15 @@ export declare function vectorSearch(db: Database.Database, queryEmbedding: Floa
132
132
  distance: number;
133
133
  }>;
134
134
  /**
135
- * BM25F field weights (config-driven via {@link configureBm25Fts}, called from
136
- * retrieval.configureScoring at boot). The order mirrors the FTS5 column
137
- * declaration in db.ts (content, project, domain) — `bm25(memories_fts, …)`
138
- * takes weights POSITIONALLY, so a new FTS column MUST be added here in the
139
- * same position or the weighting silently shifts. Defaults favor scope fields
140
- * (project/domain) over body so cross-scope noise that wins on raw token
141
- * frequency (the marine "battery" memory on a hardware query) is demoted
142
- * without excluding it — the same "graded, never binary" discipline as
143
- * computeScore's affinity terms.
135
+ * BM25F field weights (release-managed since #408 — the defaults resolve from
136
+ * calibration.ts; {@link configureBm25Fts} is the eval/test seam). The order
137
+ * mirrors the FTS5 column declaration in db.ts (content, project, domain) —
138
+ * `bm25(memories_fts, …)` takes weights POSITIONALLY, so a new FTS column
139
+ * MUST be added here in the same position or the weighting silently shifts.
140
+ * Defaults favor scope fields (project/domain) over body so cross-scope noise
141
+ * that wins on raw token frequency (the marine "battery" memory on a hardware
142
+ * query) is demoted without excluding it — the same "graded, never binary"
143
+ * discipline as computeScore's affinity terms.
144
144
  */
145
145
  export interface Bm25Weights {
146
146
  body: number;
@@ -148,14 +148,14 @@ export interface Bm25Weights {
148
148
  domain: number;
149
149
  }
150
150
  /**
151
- * Configure BM25F weights from config. Called by retrieval.configureScoring
152
- * (which itself is called at server + nightly boot) so storage and retrieval
153
- * rank identically. Invalid/out-of-range values keep the shipped default per
154
- * key. Range [0, ∞) — a 0 weight effectively drops that field from the score;
155
- * negative values are rejected (BM25F sign semantics break otherwise). Returns
156
- * the resolved set for logging/tests.
151
+ * Configure BM25F weights from RESOLVED overrides (the eval/test seam — #408;
152
+ * production calls it with no argument so storage and retrieval rank with the
153
+ * identical calibration defaults). Invalid/out-of-range values keep the
154
+ * shipped default per key. Range [0, ∞) — a 0 weight effectively drops that
155
+ * field from the score; negative values are rejected (BM25F sign semantics
156
+ * break otherwise). Returns the resolved set for logging/tests.
157
157
  */
158
- export declare function configureBm25Fts(config?: Record<string, unknown> | null): Bm25Weights;
158
+ export declare function configureBm25Fts(overrides?: Partial<Bm25Weights> | null): Bm25Weights;
159
159
  /** Current resolved weights (tests + status output). */
160
160
  export declare function getBm25Weights(): Bm25Weights;
161
161
  /**
package/dist/storage.js CHANGED
@@ -3,6 +3,39 @@
3
3
  * Storage layer — CRUD operations for the SQLite + sqlite-vec database.
4
4
  * Ported from hicortex/storage.py. All functions are synchronous (better-sqlite3).
5
5
  */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
6
39
  Object.defineProperty(exports, "__esModule", { value: true });
7
40
  exports.FTS_MATCH_MAX_TOKENS = void 0;
8
41
  exports.embedToBlob = embedToBlob;
@@ -38,6 +71,7 @@ exports.getAllLinkCounts = getAllLinkCounts;
38
71
  exports.getUnscoredMemories = getUnscoredMemories;
39
72
  const node_crypto_1 = require("node:crypto");
40
73
  const schema_prototypes_js_1 = require("./schema-prototypes.js");
74
+ const CALIBRATION = __importStar(require("./calibration.js"));
41
75
  // ---------------------------------------------------------------------------
42
76
  // Helpers
43
77
  // ---------------------------------------------------------------------------
@@ -354,28 +388,28 @@ function vectorSearch(db, queryEmbedding, limit = 10, excludeIds = []) {
354
388
  return results;
355
389
  }
356
390
  const BM25_DEFAULTS = {
357
- body: 1.0,
358
- project: 2.0,
359
- domain: 2.0,
391
+ body: CALIBRATION.BM25_WEIGHT_BODY,
392
+ project: CALIBRATION.BM25_WEIGHT_PROJECT,
393
+ domain: CALIBRATION.BM25_WEIGHT_DOMAIN,
360
394
  };
361
395
  let bm25Weights = { ...BM25_DEFAULTS };
362
396
  /**
363
- * Configure BM25F weights from config. Called by retrieval.configureScoring
364
- * (which itself is called at server + nightly boot) so storage and retrieval
365
- * rank identically. Invalid/out-of-range values keep the shipped default per
366
- * key. Range [0, ∞) — a 0 weight effectively drops that field from the score;
367
- * negative values are rejected (BM25F sign semantics break otherwise). Returns
368
- * the resolved set for logging/tests.
369
- */
370
- function configureBm25Fts(config) {
371
- const num = (key, dflt) => {
372
- const v = Number(config?.[key]);
373
- return Number.isFinite(v) && v >= 0 ? v : dflt;
397
+ * Configure BM25F weights from RESOLVED overrides (the eval/test seam — #408;
398
+ * production calls it with no argument so storage and retrieval rank with the
399
+ * identical calibration defaults). Invalid/out-of-range values keep the
400
+ * shipped default per key. Range [0, ∞) — a 0 weight effectively drops that
401
+ * field from the score; negative values are rejected (BM25F sign semantics
402
+ * break otherwise). Returns the resolved set for logging/tests.
403
+ */
404
+ function configureBm25Fts(overrides) {
405
+ const num = (v, dflt) => {
406
+ const n = Number(v);
407
+ return Number.isFinite(n) && n >= 0 ? n : dflt;
374
408
  };
375
409
  bm25Weights = {
376
- body: num("bm25WeightBody", BM25_DEFAULTS.body),
377
- project: num("bm25WeightProject", BM25_DEFAULTS.project),
378
- domain: num("bm25WeightDomain", BM25_DEFAULTS.domain),
410
+ body: num(overrides?.body, BM25_DEFAULTS.body),
411
+ project: num(overrides?.project, BM25_DEFAULTS.project),
412
+ domain: num(overrides?.domain, BM25_DEFAULTS.domain),
379
413
  };
380
414
  return { ...bm25Weights };
381
415
  }
@@ -487,14 +521,18 @@ function searchFts(db, query, limit = 10, sourceAgent) {
487
521
  * Create a link between two memories.
488
522
  */
489
523
  function addLink(db, sourceId, targetId, relationship, strength = 0.5) {
490
- // Guard: superseded_by and corrected_by are the ranking-demotion /
491
- // correction-resolution signals, so never let a different relationship
492
- // clobber an existing one for the same pair — INSERT OR REPLACE would
493
- // otherwise silently remove the resolution (corrected_by is protected
494
- // exactly like superseded_by, #384 AC9).
495
- if (relationship !== "superseded_by" && relationship !== "corrected_by") {
524
+ // Guard: superseded_by, corrected_by, and conflicts are the ranking-demotion /
525
+ // correction-resolution / conflict-preservation signals, so never let a
526
+ // different relationship clobber an existing one for the same pair — INSERT
527
+ // OR REPLACE would otherwise silently remove the resolution (corrected_by is
528
+ // protected exactly like superseded_by, #384 AC9; conflicts joins the set in
529
+ // #393 guard-C — a conflicts edge marks the pair as never-blendable, so it
530
+ // must survive arbitrary later link writes exactly the same way).
531
+ if (relationship !== "superseded_by" &&
532
+ relationship !== "corrected_by" &&
533
+ relationship !== "conflicts") {
496
534
  const protectedLink = db
497
- .prepare("SELECT 1 FROM memory_links WHERE source_id = ? AND target_id = ? AND relationship IN ('superseded_by', 'corrected_by') LIMIT 1")
535
+ .prepare("SELECT 1 FROM memory_links WHERE source_id = ? AND target_id = ? AND relationship IN ('superseded_by', 'corrected_by', 'conflicts') LIMIT 1")
498
536
  .get(sourceId, targetId);
499
537
  if (protectedLink)
500
538
  return;
@@ -91,17 +91,17 @@ export interface TelemetryPayload {
91
91
  * `runConsolidation`'s status: "completed" | "skipped" | "failed", plus
92
92
  * "no_llm" when consolidation was skipped because no LLM was configured,
93
93
  * "throttled" (#246) when the run was skipped because the
94
- * `llmTokensPerMonth` fair-use cap was projected to be exceeded, and
95
- * "endpoint_down" (#337) when the pre-consolidation readiness probe failed
96
- * or the LLM circuit breaker was open after the run — a TRANSIENT state
97
- * (retried next run), never reported as "completed" even though the stages
98
- * fail soft.
94
+ * `llmTokensPerMonth` fair-use cap was projected to be exceeded,
95
+ * "endpoint_down" (#337) when the LLM circuit breaker was open after the
96
+ * run, and "deferred" (#405) when the run-wide wall-clock deadline fired —
97
+ * the latter two are TRANSIENT states (retried next run), never reported
98
+ * as "completed" even though the stages fail soft.
99
99
  * "skipped" = the built-in nothing-to-do short-circuit (no new + no unscored
100
100
  * memories → zero LLM calls), NOT a failure. Lets the fleet aggregate tell a
101
101
  * real consolidation run from a no-op without repurposing `ok` (which is the
102
102
  * capture-health signal). 0.17+.
103
103
  */
104
- consolidation?: "completed" | "skipped" | "failed" | "no_llm" | "throttled" | "endpoint_down";
104
+ consolidation?: "completed" | "skipped" | "failed" | "no_llm" | "throttled" | "endpoint_down" | "deferred";
105
105
  /**
106
106
  * Total LLM tokens consumed by THIS nightly's consolidation (#246) — the
107
107
  * BudgetTracker total. Server-mode only (capture-only + client runs make no
@@ -111,7 +111,8 @@ export interface TelemetryPayload {
111
111
  */
112
112
  tokens_this_run?: number;
113
113
  /**
114
- * True when the per-tenant consolidation budget (`consolidateMaxLlmCalls`)
114
+ * True when the per-tenant consolidation budget (`nightlyLlmCallBudget`,
115
+ * #405 — formerly consolidateMaxLlmCalls)
115
116
  * was exhausted this run (#255) — LLM-bound stages deferred remaining work.
116
117
  * A quality-degradation signal concentrated on heavy users; absent on a
117
118
  * pre-#255 ping, a capture-only/no-LLM/throttled/skipped run, or when the
@@ -105,10 +105,9 @@ function recordDistillUsage(stateDir, usage) {
105
105
  let periodStart = "";
106
106
  (0, state_js_1.updateState)((s) => {
107
107
  const prev = s.llmTokensThisPeriod;
108
- // Monthly reset (year+month) — matches shouldThrottleTokens's staleness check.
109
- const stale = !prev?.periodStart ||
110
- new Date(prev.periodStart).getUTCFullYear() !== new Date().getUTCFullYear() ||
111
- new Date(prev.periodStart).getUTCMonth() !== new Date().getUTCMonth();
108
+ // Monthly reset — the ONE staleness helper (#405; was a hand-rolled copy
109
+ // of shouldThrottleTokens's check).
110
+ const stale = (0, consolidate_js_1.isStaleTokenPeriod)(prev?.periodStart);
112
111
  if (stale) {
113
112
  s.llmTokensThisPeriod = {
114
113
  prompt: usage.prompt,
@@ -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) {