@gamaze/hicortex 0.22.2 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -91,8 +91,8 @@ Bare top-level keys (`"serverUrl": …` at the root of `openclaw.json`) still wo
91
91
  | When | What | How |
92
92
  |------|------|-----|
93
93
  | Agent start | Standing identity (`## Identity`) + recent lessons fetched fresh and injected | CC SessionStart hook (calls `hicortex learnings-identity`; `lessons-context` kept as an alias so existing installed hooks don't break) / Hermes plugin `system_prompt_block` / OC `before_agent_start` hook |
94
- | Every prompt (0.14) | A compact **recall index** of relevant memories is injected — one line per memory; the agent lazy-loads full content with `hicortex_get` only when needed | All five harnesses call server `POST /recall-index` per turn: CC UserPromptSubmit hook (`hicortex recall-hook`), Hermes plugin `prefetch` (0.7.0; falls back to `/search` injection against a pre-0.14 server), OC `before_agent_start` hook (fires per inbound message), Pi extension `before_agent_start` (0.20), opencode plugin messages-transform hook (0.21). Turn-based dedup per session; resets on new session/compaction. Fail-soft |
95
- | Nightly | Denoise sessions → POST /distill → server distills + embeds + stores → consolidate (score, reflect, link, decay) | Automatic pipeline — no manual steps |
94
+ | Every prompt (0.14) | A compact **recall index** of relevant memories is injected — one line per memory; the agent lazy-loads full content with `hicortex_get` only when needed | All five harnesses call server `POST /recall-index` per turn: CC UserPromptSubmit hook (`hicortex recall-hook`), Hermes plugin `prefetch` (0.7.0; falls back to `/search` injection against a pre-0.14 server), OC `before_agent_start` hook (fires per inbound message), Pi extension `before_agent_start` (0.20), opencode plugin messages-transform hook (0.21). Turn-based dedup per session; resets on new session/compaction. Fail-soft. Harness plumbing (task-notifications, command envelopes) is skipped deterministically at BOTH the hook and the server — a wrapper carrying real prose still recalls on the full text |
95
+ | Nightly | Denoise sessions → POST /distill → server distills + embeds + stores → consolidate (score, reflect, link, decay) | Automatic pipeline — no manual steps. Volatile status entries (GitHub ticket states, version bumps, commit/push states) are dropped at a deterministic entry gate beside the substance gate — every drop rides the `/distill` response's `dropped` audit trail; decision/policy wording ("switched from X to Y", "never relicense…") always escapes the gate |
96
96
 
97
97
  **Exposure vs use (0.14):** appearing in the recall index only marks a memory as *shown* (it stops decaying while topically active); fetching it with `hicortex_get` marks it as *used* (durable strengthening). Memory importance is driven by what agents actually use, not by what was pushed at them.
98
98
 
@@ -216,6 +216,8 @@ npx @gamaze/hicortex classify-domains # Backfill domain tags over the c
216
216
  npx @gamaze/hicortex classify-types # Reclassify memory types (episode/fact/decision)
217
217
  npx @gamaze/hicortex dedup # Preview near-duplicate memory clusters (dry run, no changes)
218
218
  npx @gamaze/hicortex dedup --apply # Merge near-duplicate clusters (backs up the DB first)
219
+ npx @gamaze/hicortex sweep-volatile # Preview volatile GitHub-status/version rows the gate would retire (dry run)
220
+ npx @gamaze/hicortex sweep-volatile --apply # Retire them via absorb (backs up the DB first; audit trail; never deletes)
219
221
  npx @gamaze/hicortex identity show [name] # Print the standing identity layer (see Identity Layer)
220
222
  npx @gamaze/hicortex identity edit <name> # Edit an identity section in $EDITOR
221
223
  npx @gamaze/hicortex identity show --agent <id> # Show a specific agent's resolved identity (0.13)
@@ -39,7 +39,7 @@
39
39
  - Overview: the counter/bar/headroom key off LIVE memories (absorbed
40
40
  rows show as an "absorbed (kept as evidence)" note); the counter is
41
41
  STATIC (no replay tracking — owner ruling 2026-09-13).
42
- - Recall card: the RAW uses-per-showing ratio + a 7v7-nights trend, NO
42
+ - Recall card: the RAW uses-per-showing ratio, NO
43
43
  grade verdict (#426 owner semantics ruling: recall depends on the
44
44
  conversation, higher is not a target).
45
45
  - Rail header: "Fleet".
@@ -89,7 +89,7 @@
89
89
  default, which used to degrade the corner to a standalone Upgrade
90
90
  button. The upgrade CTA is a MENU ITEM (same plan-unset eligibility
91
91
  the shipped #365 code used); no standalone upgrade button remains.
92
- - RECALL CARD: below the band at most ONE subtle line (trend glyph +
92
+ - RECALL CARD: below the band at most ONE subtle line (zone word +
93
93
  muted "provisional"; pill chrome dropped); the zone word rides the
94
94
  marker, the zone-label row is gone — the card stops setting the row
95
95
  height.
@@ -135,7 +135,7 @@
135
135
  DOWN into the siblings' chart slot (header → big → band → foot).
136
136
  "provisional" is hover-only now — a tooltip on the band (the card
137
137
  stays marked; the visible word failed to communicate). Vocabulary
138
- amendment: Low / HEALTHY / Overfetching. Marker + trend stay.
138
+ amendment: Low / HEALTHY / Overfetching. Marker + zone word stay.
139
139
 
140
140
  REPLAY + RANGE (#452, owner directives 2026-09-16):
141
141
  - 30-DAY DEFAULT FOR ALL VIEWS: the replay ledger, the band and the
@@ -397,10 +397,6 @@
397
397
  .big .n.noconf{ font-size:14.5px; font-weight:520; letter-spacing:0; color:var(--dim); }
398
398
  html:not([data-theme="light"]) .big .n.noconf{ text-shadow:none; }
399
399
  .big .l{ font-size:10px; color:var(--faint); text-transform:uppercase; letter-spacing:.13em; }
400
- .trend{ font-size:12px; line-height:1; flex:none; }
401
- .trend.up{ color:var(--green-mid); }
402
- .trend.down{ color:var(--red); }
403
- .trend.flat{ color:var(--dim); }
404
400
  /* THE card sub-line — one text type */
405
401
  .csub{ font-size:11px; color:var(--dim); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
406
402
  .csub b{ color:var(--green-mid); font-weight:600; }
@@ -419,7 +415,7 @@
419
415
  row). The visible "provisional" word is GONE — the marking is hover-only
420
416
  now, a tooltip on the band (the visible word failed to communicate).
421
417
  Vocabulary (owner 2026-09-14): Low / Healthy / Overfetching. The raw
422
- number stays tooltip-only on the marker; the trend glyph stays (mock
418
+ number stays tooltip-only on the marker (mock
423
419
  family). Colors: the band + word carry concrete per-theme colors from
424
420
  the JS stop table (setTheme re-renders), the dot rides theme tokens. */
425
421
  .rec-word.none{ color:var(--dim); }
@@ -3178,23 +3174,6 @@ function buildTokNights(used, cap, periodStart){
3178
3174
  }
3179
3175
  }
3180
3176
 
3181
- /* trend = mean of the last 7 nights vs the prior 7 (a DEFINED window — the
3182
- old first-vs-last-over-the-range comparison had no window at all). Titles
3183
- state the window so the arrow is never mysterious. */
3184
- function recallTrend(){
3185
- const flat={cls:'flat',sym:'&#9644;'};
3186
- if(!D||!D.series) return Object.assign(flat,{title:'no snapshot data'});
3187
- const vals=D.series.map(s=>s.metrics&&s.metrics.adoption?s.metrics.adoption.uses_per_showing:null).filter(v=>typeof v==='number');
3188
- const last7=vals.slice(-7), prior7=vals.slice(-14,-7);
3189
- const mean=a=>a.length?a.reduce((x,y)=>x+y,0)/a.length:null;
3190
- const a=mean(prior7), b=mean(last7);
3191
- if(a===null||b===null) return Object.assign(flat,{title:'trend needs 7+ nights of history'});
3192
- const d=b-a;
3193
- if(d>0.005) return {cls:'up',sym:'&#8599;',title:'up — mean of the last 7 nights vs the prior 7 ('+b.toFixed(3)+' vs '+a.toFixed(3)+')'};
3194
- if(d<-0.005) return {cls:'down',sym:'&#8600;',title:'down — mean of the last 7 nights vs the prior 7 ('+b.toFixed(3)+' vs '+a.toFixed(3)+')'};
3195
- return Object.assign(flat,{title:'flat — mean of the last 7 nights vs the prior 7 ('+b.toFixed(3)+' vs '+a.toFixed(3)+')'});
3196
- }
3197
-
3198
3177
  /* ---------- Recall level — the designed U-band (#426 final ruling
3199
3178
  2026-09-13). A continuous red→green→red gradient spans the PROVISIONAL
3200
3179
  zone edges (echoed by /dashboard/field as thresholds.recall_uses — the
@@ -3291,16 +3270,11 @@ function renderRecBand(ups){
3291
3270
  word.style.color=''; word.style.textShadow='none';
3292
3271
  word.title='no showings yet';
3293
3272
  }
3294
- /* trend arrow — the mock family's 7v7 window, unchanged */
3295
- if(typeof ups==='number'){
3296
- const tr=recallTrend();
3297
- el('rec-trend').className='trend '+tr.cls;
3298
- el('rec-trend').innerHTML=tr.sym;
3299
- el('rec-trend').title=tr.title;
3300
- }else{
3301
- el('rec-trend').className='trend flat'; el('rec-trend').innerHTML='&#9644;';
3302
- el('rec-trend').title='no showings yet';
3303
- }
3273
+ /* No trend arrow (owner 2026-09-20: removed — unclear). Its renderer and
3274
+ the element writes are removed WITH it: a leftover write to a removed
3275
+ element is a boot-killing TypeError whenever the recall band carries
3276
+ real data (bedrock 0.22.2: "0 memories" — boot died before the counter
3277
+ was ever seeded). */
3304
3278
  }
3305
3279
 
3306
3280
  /* ---------- Memory Precision — Level 1, the pushed index (#476). A compact
@@ -117,11 +117,11 @@ export declare const MEMORY_PRECISION_WINDOW_DAYS = 90;
117
117
  /**
118
118
  * Cosine at/above which a pushed line reads "redundant" (restating the
119
119
  * session's standing context — the top lessons + identity every session-start
120
- * hook injects). Direction-only display guidance, never a gate. 0.80 mirrors
121
- * SUPERSESSION_MIN_SIMILARITY: at/above it a pair reads as the same
122
- * statement, below it as topical overlap — a reasonable first anchor until
123
- * the judged-calibration follow-up measures the real distribution (the
124
- * #426 provisional-anchor posture).
120
+ * hook injects). Direction-only display guidance, never a gate. 0.80 — the
121
+ * anchor it was seeded from (the retired supersession stage's floor, #206-B)
122
+ * at/above which a pair reads as the same statement, below it as topical
123
+ * overlap — a reasonable first anchor until the judged-calibration follow-up
124
+ * measures the real distribution (the #426 provisional-anchor posture).
125
125
  */
126
126
  export declare const MEMORY_PRECISION_REDUNDANT_ABOVE = 0.8;
127
127
  /**
@@ -138,6 +138,23 @@ export declare const MEMORY_PRECISION_DIVERGENCE_MIN_SHOWN = 5;
138
138
  * the same trust boundary as /distill's denoised conversation text).
139
139
  */
140
140
  export declare const MEMORY_PRECISION_PROMPT_EXCERPT_CHARS = 256;
141
+ /**
142
+ * Kill-switch for the deterministic volatility gate (#489, owner decision 5):
143
+ * false disables isVolatileStatusEntry's use in the distill path (entries
144
+ * keep flowing as before) AND the sweep's candidacy rule — one release-managed
145
+ * constant, no config knob (#408 discipline: moving it is a release decision,
146
+ * never a runtime one).
147
+ */
148
+ export declare const VOLATILE_STATUS_FILTER = true;
149
+ /**
150
+ * Max entry length the volatility gate will classify as volatile (#489) — the
151
+ * operationalization of the spec's "DOMINANT content matches": distiller
152
+ * bullets are one-liners, so a status shape in a SHORT entry dominates it,
153
+ * while the same clause riding inside long mixed prose does not (kept — the
154
+ * substance gate's precision-over-recall bias, applied to a second gate).
155
+ * 300 chars ≈ 3× the longest shape in the #489 KEEP/DROP corpus.
156
+ */
157
+ export declare const VOLATILE_GATE_MAX_CHARS = 300;
141
158
  /** Semantic-similarity share of the composite score. 0.50 (raised from 0.40
142
159
  * in the 0.15.2 rebalance, #191 Phase B): on the production corpus effective
143
160
  * strength (0.30) outweighed what similarity could recover — hardened old
@@ -273,13 +290,11 @@ export declare const IMPORTANCE_SETTLE_WINDOW_DAYS = 3;
273
290
  * get the one verdict call. 0.92 — measured on the #191 mechanical audit
274
291
  * corpus (89 clusters / 110 excess rows; data/audit-20260729). */
275
292
  export declare const DEDUP_AUTO_MERGE_THRESHOLD = 0.92;
276
- /** Minimum cosine for a nightly supersession candidate pair (#100 stage,
277
- * 0.15.0): one classify-tier call per pair above the bar. */
278
- export declare const SUPERSESSION_MIN_SIMILARITY = 0.8;
279
293
  /** Minimum cosine for a reconsolidation correction pair (#384). Deliberately
280
- * wider than supersession's 0.80: a retraction often rides inside an
281
- * otherwise unrelated memory; the verdict + confidence gate carry the
282
- * precision. */
294
+ * wide: a retraction often rides inside an otherwise unrelated memory; the
295
+ * verdict + confidence gate carry the precision. (The retired Stage 3.7
296
+ * supersession scan's 0.80 floor — #206-B, owner decision 6 — is subsumed:
297
+ * 3.8 scans all shapes from this wider floor.) */
283
298
  export declare const CORRECTION_MIN_SIMILARITY = 0.75;
284
299
  /** Minimum verdict confidence for the REWRITE (and #392 merge-apply) fork
285
300
  * (#384): below it a `corrects` degrades to mark-only — a weak mark is
@@ -24,8 +24,8 @@
24
24
  * the removal is never silent.
25
25
  */
26
26
  Object.defineProperty(exports, "__esModule", { value: true });
27
- exports.STAGE_TRUTH_STRENGTH = exports.STAGE_BELIEF_STRENGTH = exports.STAGE_FADING_STRENGTH = exports.STAGE_FADING_DAYS = exports.WEAK_PRIMARY_FLOOR = exports.CORRECTION_REWRITE_MIN_CONFIDENCE = exports.CORRECTION_MIN_SIMILARITY = exports.SUPERSESSION_MIN_SIMILARITY = exports.DEDUP_AUTO_MERGE_THRESHOLD = exports.IMPORTANCE_SETTLE_WINDOW_DAYS = exports.IMPORTANCE_CEILING = exports.BM25_WEIGHT_DOMAIN = exports.BM25_WEIGHT_PROJECT = exports.BM25_WEIGHT_BODY = exports.BOTH_CHANNEL_BOOST = exports.RRF_VECTOR_WEIGHT = exports.RRF_FTS_WEIGHT = exports.RRF_COMPOSITE_WEIGHT = exports.RRF_K = exports.SCOPE_AFFINITY_WEIGHT = exports.SUPERSEDED_DEMOTION = exports.RECENCY_HEAD_DAYS = exports.RECENCY_HEAD_WEIGHT = exports.RECENCY_HOURLY_DECAY = exports.SCORE_RECENCY_WEIGHT = exports.CONNECTIONS_SATURATION_DEGREE = exports.SCORE_CONNECTIONS_WEIGHT = exports.SCORE_STRENGTH_WEIGHT = exports.SCORE_SIMILARITY_WEIGHT = exports.MEMORY_PRECISION_PROMPT_EXCERPT_CHARS = exports.MEMORY_PRECISION_DIVERGENCE_MIN_SHOWN = exports.MEMORY_PRECISION_REDUNDANT_ABOVE = exports.MEMORY_PRECISION_WINDOW_DAYS = exports.RECALL_USES_AXIS_MAX = exports.RECALL_USES_NORMAL_MAX = exports.RECALL_USES_LOW_MAX = exports.RECALL_RESHOW_TURNS = exports.NOVELTY_FLOOR_SLOTS = exports.RECALL_TITLE_CHARS = exports.RECALL_MIN_PROMPT_CHARS = exports.RECALL_MAX_ITEMS = exports.RECALL_MIN_SIMILARITY = exports.SESSION_INTENT_WEIGHT = exports.COLD_EXPOSURE_SLOTS = exports.RECENT_WINDOW_DAYS = exports.RECENT_LIMIT = exports.SEARCH_LIMIT = exports.PROMOTION_STRENGTH_FLOOR = exports.PROMOTION_RATE = exports.DECAY_HALF_LIFE_DAYS = void 0;
28
- exports.DIAGNOSTIC_ENV_TIER = exports.OLLAMA_FLUSH_WAIT_MS = exports.OLLAMA_FLUSH_EVERY = exports.NUM_CTX = exports.DEFAULT_FIRST_RUN_LOOKBACK_DAYS = exports.ENRICH_STRENGTH_DELTA = void 0;
27
+ exports.STAGE_BELIEF_STRENGTH = exports.STAGE_FADING_STRENGTH = exports.STAGE_FADING_DAYS = exports.WEAK_PRIMARY_FLOOR = exports.CORRECTION_REWRITE_MIN_CONFIDENCE = exports.CORRECTION_MIN_SIMILARITY = exports.DEDUP_AUTO_MERGE_THRESHOLD = exports.IMPORTANCE_SETTLE_WINDOW_DAYS = exports.IMPORTANCE_CEILING = exports.BM25_WEIGHT_DOMAIN = exports.BM25_WEIGHT_PROJECT = exports.BM25_WEIGHT_BODY = exports.BOTH_CHANNEL_BOOST = exports.RRF_VECTOR_WEIGHT = exports.RRF_FTS_WEIGHT = exports.RRF_COMPOSITE_WEIGHT = exports.RRF_K = exports.SCOPE_AFFINITY_WEIGHT = exports.SUPERSEDED_DEMOTION = exports.RECENCY_HEAD_DAYS = exports.RECENCY_HEAD_WEIGHT = exports.RECENCY_HOURLY_DECAY = exports.SCORE_RECENCY_WEIGHT = exports.CONNECTIONS_SATURATION_DEGREE = exports.SCORE_CONNECTIONS_WEIGHT = exports.SCORE_STRENGTH_WEIGHT = exports.SCORE_SIMILARITY_WEIGHT = exports.VOLATILE_GATE_MAX_CHARS = exports.VOLATILE_STATUS_FILTER = exports.MEMORY_PRECISION_PROMPT_EXCERPT_CHARS = exports.MEMORY_PRECISION_DIVERGENCE_MIN_SHOWN = exports.MEMORY_PRECISION_REDUNDANT_ABOVE = exports.MEMORY_PRECISION_WINDOW_DAYS = exports.RECALL_USES_AXIS_MAX = exports.RECALL_USES_NORMAL_MAX = exports.RECALL_USES_LOW_MAX = exports.RECALL_RESHOW_TURNS = exports.NOVELTY_FLOOR_SLOTS = exports.RECALL_TITLE_CHARS = exports.RECALL_MIN_PROMPT_CHARS = exports.RECALL_MAX_ITEMS = exports.RECALL_MIN_SIMILARITY = exports.SESSION_INTENT_WEIGHT = exports.COLD_EXPOSURE_SLOTS = exports.RECENT_WINDOW_DAYS = exports.RECENT_LIMIT = exports.SEARCH_LIMIT = exports.PROMOTION_STRENGTH_FLOOR = exports.PROMOTION_RATE = exports.DECAY_HALF_LIFE_DAYS = void 0;
28
+ exports.DIAGNOSTIC_ENV_TIER = exports.OLLAMA_FLUSH_WAIT_MS = exports.OLLAMA_FLUSH_EVERY = exports.NUM_CTX = exports.DEFAULT_FIRST_RUN_LOOKBACK_DAYS = exports.ENRICH_STRENGTH_DELTA = exports.STAGE_TRUTH_STRENGTH = void 0;
29
29
  exports.resolveNumCtx = resolveNumCtx;
30
30
  exports.resolveOllamaFlushEvery = resolveOllamaFlushEvery;
31
31
  exports.resolveOllamaFlushWaitMs = resolveOllamaFlushWaitMs;
@@ -144,11 +144,11 @@ exports.MEMORY_PRECISION_WINDOW_DAYS = 90;
144
144
  /**
145
145
  * Cosine at/above which a pushed line reads "redundant" (restating the
146
146
  * session's standing context — the top lessons + identity every session-start
147
- * hook injects). Direction-only display guidance, never a gate. 0.80 mirrors
148
- * SUPERSESSION_MIN_SIMILARITY: at/above it a pair reads as the same
149
- * statement, below it as topical overlap — a reasonable first anchor until
150
- * the judged-calibration follow-up measures the real distribution (the
151
- * #426 provisional-anchor posture).
147
+ * hook injects). Direction-only display guidance, never a gate. 0.80 — the
148
+ * anchor it was seeded from (the retired supersession stage's floor, #206-B)
149
+ * at/above which a pair reads as the same statement, below it as topical
150
+ * overlap — a reasonable first anchor until the judged-calibration follow-up
151
+ * measures the real distribution (the #426 provisional-anchor posture).
152
152
  */
153
153
  exports.MEMORY_PRECISION_REDUNDANT_ABOVE = 0.8;
154
154
  /**
@@ -166,6 +166,28 @@ exports.MEMORY_PRECISION_DIVERGENCE_MIN_SHOWN = 5;
166
166
  */
167
167
  exports.MEMORY_PRECISION_PROMPT_EXCERPT_CHARS = 256;
168
168
  // ---------------------------------------------------------------------------
169
+ // Volatility gate (#489) — deterministic drop of GH-ticket-status /
170
+ // version-bump / commit-state entries at the distill gate, and the one-shot
171
+ // store sweep that reuses the same gate.
172
+ // ---------------------------------------------------------------------------
173
+ /**
174
+ * Kill-switch for the deterministic volatility gate (#489, owner decision 5):
175
+ * false disables isVolatileStatusEntry's use in the distill path (entries
176
+ * keep flowing as before) AND the sweep's candidacy rule — one release-managed
177
+ * constant, no config knob (#408 discipline: moving it is a release decision,
178
+ * never a runtime one).
179
+ */
180
+ exports.VOLATILE_STATUS_FILTER = true;
181
+ /**
182
+ * Max entry length the volatility gate will classify as volatile (#489) — the
183
+ * operationalization of the spec's "DOMINANT content matches": distiller
184
+ * bullets are one-liners, so a status shape in a SHORT entry dominates it,
185
+ * while the same clause riding inside long mixed prose does not (kept — the
186
+ * substance gate's precision-over-recall bias, applied to a second gate).
187
+ * 300 chars ≈ 3× the longest shape in the #489 KEEP/DROP corpus.
188
+ */
189
+ exports.VOLATILE_GATE_MAX_CHARS = 300;
190
+ // ---------------------------------------------------------------------------
169
191
  // Composite ranking weights (was: score*Weight, supersededDemotion,
170
192
  // *AffinityWeight, rrf*; the pre-#430 freshness bonus constants merged into
171
193
  // the RECENCY family below)
@@ -315,13 +337,11 @@ exports.IMPORTANCE_SETTLE_WINDOW_DAYS = 3;
315
337
  * get the one verdict call. 0.92 — measured on the #191 mechanical audit
316
338
  * corpus (89 clusters / 110 excess rows; data/audit-20260729). */
317
339
  exports.DEDUP_AUTO_MERGE_THRESHOLD = 0.92;
318
- /** Minimum cosine for a nightly supersession candidate pair (#100 stage,
319
- * 0.15.0): one classify-tier call per pair above the bar. */
320
- exports.SUPERSESSION_MIN_SIMILARITY = 0.80;
321
340
  /** Minimum cosine for a reconsolidation correction pair (#384). Deliberately
322
- * wider than supersession's 0.80: a retraction often rides inside an
323
- * otherwise unrelated memory; the verdict + confidence gate carry the
324
- * precision. */
341
+ * wide: a retraction often rides inside an otherwise unrelated memory; the
342
+ * verdict + confidence gate carry the precision. (The retired Stage 3.7
343
+ * supersession scan's 0.80 floor — #206-B, owner decision 6 — is subsumed:
344
+ * 3.8 scans all shapes from this wider floor.) */
325
345
  exports.CORRECTION_MIN_SIMILARITY = 0.75;
326
346
  /** Minimum verdict confidence for the REWRITE (and #392 merge-apply) fork
327
347
  * (#384): below it a `corrects` degrades to mark-only — a weak mark is
package/dist/capture.js CHANGED
@@ -303,7 +303,10 @@ async function captureBatches(batches, opts) {
303
303
  lastConfirmedEnd = seg.segEnd;
304
304
  console.log(`[hicortex] → ${result.distilled ?? 0} memories (segment ${body.segment_id})`);
305
305
  for (const d of result.dropped ?? []) {
306
- console.log(`[hicortex] Substance gate: dropped "${d}"`);
306
+ // #489: the response array carries BOTH gate kinds (substance +
307
+ // volatility) — the server's own log names the exact gate; this
308
+ // client-side line is the same #156 trail, labeled generically.
309
+ console.log(`[hicortex] Distill gate: dropped "${d}"`);
307
310
  }
308
311
  }
309
312
  else if (result.status === 200) {
package/dist/cli.d.ts CHANGED
@@ -16,6 +16,9 @@
16
16
  * dedup --apply Execute the merge (default: dry run);
17
17
  * losers are absorbed (kept as evidence,
18
18
  * hidden from recall), not deleted
19
+ * sweep-volatile One-shot sweep of volatile GH-status/version rows (#489)
20
+ * sweep-volatile --apply Demote matches via absorb (default:
21
+ * dry run; backup first, audit trail)
19
22
  * history Show a memory's rewrite history, or roll one back (issue #384)
20
23
  * history --rollback <row> Undo one rewrite + un-absorb triggers
21
24
  * status Show config, DB stats, adapter status
package/dist/cli.js CHANGED
@@ -17,6 +17,9 @@
17
17
  * dedup --apply Execute the merge (default: dry run);
18
18
  * losers are absorbed (kept as evidence,
19
19
  * hidden from recall), not deleted
20
+ * sweep-volatile One-shot sweep of volatile GH-status/version rows (#489)
21
+ * sweep-volatile --apply Demote matches via absorb (default:
22
+ * dry run; backup first, audit trail)
20
23
  * history Show a memory's rewrite history, or roll one back (issue #384)
21
24
  * history --rollback <row> Undo one rewrite + un-absorb triggers
22
25
  * status Show config, DB stats, adapter status
@@ -306,6 +309,29 @@ switch (command) {
306
309
  });
307
310
  break;
308
311
  }
312
+ case "sweep-volatile": {
313
+ // One-shot store sweep of volatile GH-status/version rows (#489,
314
+ // deliverable iii): reuses the distill path's deterministic gate over the
315
+ // stored corpus. Mirrors `dedup`: dry-run default, --apply explicit
316
+ // (backup first, absorb — never delete, volatile_sweep_log audit trail).
317
+ const args = process.argv.slice(3);
318
+ let dbPath;
319
+ try {
320
+ dbPath = (0, cli_args_js_1.readValueFlag)(args, "--db");
321
+ }
322
+ catch {
323
+ console.error("[hicortex] sweep-volatile: --db requires a path value");
324
+ process.exit(1);
325
+ }
326
+ const sweepOptions = { apply: args.includes("--apply"), dbPath };
327
+ import("./sweep-volatile.js").then(({ runSweepVolatile }) => {
328
+ runSweepVolatile(sweepOptions).catch((err) => {
329
+ console.error(err instanceof Error ? err.message : `[hicortex] sweep-volatile failed: ${err}`);
330
+ process.exit(1);
331
+ });
332
+ });
333
+ break;
334
+ }
309
335
  case "history": {
310
336
  // Rewrite history: audit + one-command rollback (#384). Listing is
311
337
  // read-only; --rollback mutates (restores prior content/status and
package/dist/cluster.d.ts CHANGED
@@ -25,8 +25,12 @@ export declare class UnionFind {
25
25
  export declare function clusterEdges(edges: Edge[], threshold: number): string[][];
26
26
  /** Excess = sum(cluster size − 1) — rows that would disappear if every cluster merged to one. */
27
27
  export declare function clusterExcess(clusters: string[][]): number;
28
- /** Metadata fields a merge candidate cluster must agree on. `privacy` is on the row (the
29
- * column still exists) but is NOT a merge-safety field — vestigial since 0.16.2. */
28
+ /** Metadata row shape for the merge-safety check. `privacy` and `source_agent`
29
+ * are on the row (the columns still exist) but are NOT merge-safety fields:
30
+ * privacy is vestigial since 0.16.2, and the agent rail was REMOVED by owner
31
+ * decision (#206 decision 2, 2026-09-21) — cross-agent clusters merge, because
32
+ * attribution survives on the retained absorbed-loser row and no recall path
33
+ * filters by source_agent. Project is the only rail. */
30
34
  export interface ClusterMetaRow {
31
35
  project: string | null;
32
36
  privacy: string | null;
@@ -34,10 +38,9 @@ export interface ClusterMetaRow {
34
38
  }
35
39
  export interface ClusterMetadataMismatch {
36
40
  projectMismatch: boolean;
37
- sourceAgentMismatch: boolean;
38
41
  }
39
- /** Do cluster members disagree on project / source_agent? (merge-safety input for #100).
40
- * Privacy is intentionally NOT checked — it is vestigial since 0.16.2. */
42
+ /** Do cluster members disagree on project? (merge-safety input for #100/#206.)
43
+ * Privacy and source_agent are intentionally NOT checked — see ClusterMetaRow. */
41
44
  export declare function clusterMetadataMismatch(members: ClusterMetaRow[]): ClusterMetadataMismatch;
42
45
  /**
43
46
  * Build the max-cosine edge set via top-K KNN on `memory_vectors`, keeping
package/dist/cluster.js CHANGED
@@ -69,15 +69,11 @@ function clusterEdges(edges, threshold) {
69
69
  function clusterExcess(clusters) {
70
70
  return clusters.reduce((sum, c) => sum + (c.length - 1), 0);
71
71
  }
72
- /** Do cluster members disagree on project / source_agent? (merge-safety input for #100).
73
- * Privacy is intentionally NOT checked — it is vestigial since 0.16.2. */
72
+ /** Do cluster members disagree on project? (merge-safety input for #100/#206.)
73
+ * Privacy and source_agent are intentionally NOT checked — see ClusterMetaRow. */
74
74
  function clusterMetadataMismatch(members) {
75
75
  const projects = new Set(members.map((m) => m.project ?? "\u0000null"));
76
- const agents = new Set(members.map((m) => m.source_agent));
77
- return {
78
- projectMismatch: projects.size > 1,
79
- sourceAgentMismatch: agents.size > 1,
80
- };
76
+ return { projectMismatch: projects.size > 1 };
81
77
  }
82
78
  // ---------------------------------------------------------------------------
83
79
  // KNN edge building
@@ -257,65 +257,6 @@ export declare function classifyLinkCandidates(candidates: LinkCandidate[]): Pro
257
257
  * boundary from the l2ToCosine calibration is preserved.
258
258
  */
259
259
  export declare function classifyRelationship(source: Memory, target: Memory, similarity: number): string;
260
- /** Default minimum COSINE similarity for a supersession candidate pair —
261
- * RELEASE-MANAGED since #408 (calibration.ts SUPERSESSION_MIN_SIMILARITY). */
262
- export declare const DEFAULT_SUPERSESSION_MIN_SIMILARITY = 0.8;
263
- export interface SupersessionOptions {
264
- /** Candidate-pair cosine floor. Release-managed default (calibration.ts);
265
- * this field is the eval/test seam. Invalid → default. */
266
- minSimilarity?: number;
267
- /** The run-wide pipeline deadline (#405) — checked at each candidate
268
- * boundary; on expiry the scan stops and the cursor holds at the last
269
- * fully-considered candidate (resumed next run). */
270
- deadline?: RunDeadline;
271
- }
272
- export interface SupersessionStageResult {
273
- scanned: number;
274
- evaluated: number;
275
- superseded: number;
276
- skipped_infra: number;
277
- skipped_idempotent: number;
278
- cursor: number;
279
- }
280
- /**
281
- * Build the constrained supersession-check prompt. Content is truncated the
282
- * same width as domain-classify.ts's classifier (1500 chars) — this is a
283
- * classify-tier call with the same cost profile.
284
- */
285
- export declare function buildSupersessionPrompt(oldContent: string, newContent: string): string;
286
- /**
287
- * Parse the model's supersession verdict. Returns the boolean on a valid
288
- * reply, or null on anything unparseable (caller skips the pair — no retry,
289
- * unlike domain-classify's tag classifier; a missed pair is retried naturally
290
- * when this stage revisits the corpus).
291
- */
292
- export declare function parseSupersessionReply(reply: string): boolean | null;
293
- /**
294
- * Nightly supersession-detection stage. Scans memories/rowid > cursor whose
295
- * shape is supersedable (decision/correction/fact/state — isSupersedableShape),
296
- * checks each against its older same-shape neighbors, and links confirmed
297
- * supersessions. Dry-run performs discovery + the free idempotency check only —
298
- * no LLM calls, no writes, no
299
- * cursor persistence (mirrors stageImportance/stageContentDomains's dry-run
300
- * convention of never spending budget on a preview).
301
- *
302
- * Cursor discipline is DELIBERATELY simple (owner amendment): the cursor
303
- * advances past a candidate once its neighbor set has been considered,
304
- * REGARDLESS of whether every pair got an LLM call (call budget) or a clean
305
- * verdict (infra skip) — missing one pair is acceptable and self-heals next
306
- * time this memory's neighborhood is re-examined via a NEWER memory's own
307
- * candidacy. It only stops SHORT of a candidate when the budget is already
308
- * exhausted before that candidate starts, so the cursor never skips a
309
- * candidate that was never looked at.
310
- *
311
- * #405: the cursor persists after EVERY fully-considered candidate (the
312
- * post-#404 reconsolidation pattern), not at stage end — a run killed or
313
- * deadline-deferred mid-stage loses at most the candidate in flight. No
314
- * orphan clamp is needed (unlike reconsolidation): supersession applies each
315
- * verdict's link immediately, so `cursor = candidate.__rowid` always sits
316
- * after all of that candidate's writes.
317
- */
318
- export declare function stageSupersession(db: Database.Database, llm: LlmClient, budget: BudgetTracker, embedFn: EmbedFn, dryRun: boolean, stateDir: string | undefined, options?: SupersessionOptions): Promise<SupersessionStageResult>;
319
260
  /**
320
261
  * Exported for the #191 eval baseline (src/eval/decay-eval.ts) so the audit
321
262
  * runs the REAL production prune predicate against a DB snapshot instead of
@@ -404,7 +345,7 @@ export interface DomainStageOptions {
404
345
  */
405
346
  weakPrimaryFloor?: number;
406
347
  }
407
- export declare function runConsolidation(db: Database.Database, llm: LlmClient, embedFn: EmbedFn, dryRun?: boolean, skipReflection?: boolean, stateDir?: string, domainOptions?: DomainStageOptions, supersessionOptions?: SupersessionOptions,
348
+ export declare function runConsolidation(db: Database.Database, llm: LlmClient, embedFn: EmbedFn, dryRun?: boolean, skipReflection?: boolean, stateDir?: string, domainOptions?: DomainStageOptions,
408
349
  /** The ONE per-run LLM-call ceiling (#405/#241). The caller resolves
409
350
  * `nightlyLlmCallBudget` from config (consolidateMaxLlmCalls is a
410
351
  * deprecated alias — resolveNightlyLlmCallBudget) and passes it; unset →
@@ -416,10 +357,9 @@ budgetMaxCalls?: number,
416
357
  memorySoftCap?: number,
417
358
  /** Reconsolidation-stage knobs (#384) — eval/test seams since #408 (the
418
359
  * values are release-managed calibration constants; nightly.ts threads
419
- * NOTHING), exactly like supersessionOptions above; unset fields → the
420
- * stage's calibration defaults. Appended AFTER the pre-#384 params so
421
- * every existing positional caller (tests, hosted nightly) keeps its
422
- * argument meaning. */
360
+ * NOTHING); unset fields → the stage's calibration defaults. The
361
+ * pre-#206-B supersessionOptions param that sat here is retired with its
362
+ * stage — callers updated in the same change. */
423
363
  reconsolidationOptions?: ReconsolidationOptions,
424
364
  /**
425
365
  * The run-wide pipeline deadline (#405), created at nightly start and