@gamaze/hicortex 0.20.9 → 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 (48) hide show
  1. package/README.md +8 -0
  2. package/assets/dashboard.html +3989 -836
  3. package/dist/calibration.d.ts +119 -0
  4. package/dist/calibration.js +149 -1
  5. package/dist/capture-health.d.ts +87 -0
  6. package/dist/capture-health.js +106 -0
  7. package/dist/capture-pause.d.ts +86 -0
  8. package/dist/capture-pause.js +127 -0
  9. package/dist/capture.d.ts +9 -0
  10. package/dist/capture.js +2 -1
  11. package/dist/cli.js +36 -0
  12. package/dist/consolidate.d.ts +35 -0
  13. package/dist/consolidate.js +85 -9
  14. package/dist/dashboard.d.ts +322 -3
  15. package/dist/dashboard.js +592 -7
  16. package/dist/db.js +105 -0
  17. package/dist/eval/importance-eval.d.ts +85 -0
  18. package/dist/eval/importance-eval.js +286 -0
  19. package/dist/eval/planted-fixtures.d.ts +1 -1
  20. package/dist/eval/ranking-battery.d.ts +78 -0
  21. package/dist/eval/ranking-battery.js +181 -0
  22. package/dist/eval/ranking-eval.d.ts +41 -0
  23. package/dist/eval/ranking-eval.js +391 -0
  24. package/dist/eval/ranking-fixtures.d.ts +77 -0
  25. package/dist/eval/ranking-fixtures.js +226 -0
  26. package/dist/identity-store.d.ts +21 -0
  27. package/dist/identity-store.js +49 -0
  28. package/dist/init.d.ts +14 -0
  29. package/dist/init.js +32 -0
  30. package/dist/mcp-server.d.ts +12 -0
  31. package/dist/mcp-server.js +184 -3
  32. package/dist/nightly.d.ts +9 -1
  33. package/dist/nightly.js +59 -7
  34. package/dist/prompts.d.ts +10 -0
  35. package/dist/prompts.js +28 -5
  36. package/dist/reconsolidation.d.ts +59 -30
  37. package/dist/reconsolidation.js +526 -296
  38. package/dist/rescore-importance.d.ts +80 -0
  39. package/dist/rescore-importance.js +236 -0
  40. package/dist/retrieval.d.ts +12 -0
  41. package/dist/retrieval.js +30 -1
  42. package/dist/stages.d.ts +37 -0
  43. package/dist/stages.js +51 -0
  44. package/dist/state.d.ts +32 -6
  45. package/dist/storage.d.ts +34 -2
  46. package/dist/storage.js +63 -6
  47. package/dist/types.d.ts +48 -0
  48. package/package.json +3 -1
@@ -1,10 +1,12 @@
1
1
  /**
2
- * /dashboard — view-only memory analytics (#224).
2
+ * /dashboard — view-only memory analytics (#224) + the console's live-data
3
+ * endpoints (#409/#421 Phase 1: /dashboard/field, /dashboard/events).
3
4
  *
4
5
  * STRICTLY view-only: this module computes metrics, reads snapshots, writes
5
6
  * ONE snapshot row per full nightly run (the writer is here because the
6
7
  * metric SQL lives next to its definition, not in nightly.ts), and exposes
7
- * the pure data handler mounted at GET /dashboard/data. There are NO mutation
8
+ * the pure data handlers mounted at GET /dashboard/data,
9
+ * GET /dashboard/field and GET /dashboard/events. There are NO mutation
8
10
  * endpoints on the dashboard surface — the only write path is the nightly
9
11
  * snapshot writer + the one-time backfill, both internal.
10
12
  *
@@ -20,17 +22,44 @@
20
22
  */
21
23
  import type express from "express";
22
24
  import type Database from "better-sqlite3";
25
+ import { type Stage } from "./stages.js";
23
26
  /** Corpus-shape snapshot. `adoption` is null in backfilled rows (point-in-time,
24
27
  * can't be reconstructed from created_at). */
25
28
  export interface DashboardMetrics {
29
+ /**
30
+ * `mem` counts ALL rows (backcompat — the growth chart's series). #422 adds
31
+ * `live_mem` (non-absorbed: what recall serves + the field paints) and
32
+ * `absorbed` (= mem − live_mem, the dedup/reconsol evidence rows). Both are
33
+ * undefined on backfilled rows — whether a historical row was absorbed at
34
+ * that moment is not reconstructable from created_at (honest omission).
35
+ */
26
36
  totals: {
27
37
  mem: number;
28
38
  lesson: number;
29
39
  link: number;
40
+ live_mem?: number;
41
+ absorbed?: number;
42
+ };
43
+ /**
44
+ * #422 Phase 2 — per-stage counts over LIVE (non-absorbed) rows, derived
45
+ * with the SAME math as /dashboard/field (the shared deriveStageForRow —
46
+ * one formula, two surfaces, drift impossible). Undefined on backfilled
47
+ * rows (historical strengths are not reconstructable). Drives the
48
+ * graduation deltas in the activity bars.
49
+ */
50
+ stage_counts?: {
51
+ forming: number;
52
+ belief: number;
53
+ truth: number;
54
+ fading: number;
30
55
  };
31
56
  by_type: Record<string, number>;
32
57
  by_domain: Record<string, number>;
33
58
  by_source_agent: Record<string, number>;
59
+ /** #421 machine × harness: memories per capture machine. Rows written
60
+ * before migration v15 have NULL source_machine → grouped under
61
+ * "(unstamped)" (the console labels them "earlier captures"). */
62
+ by_source_machine: Record<string, number>;
34
63
  /** Per-run deltas; undefined on backfilled rows (created_at can't reconstruct
35
64
  * what a given nightly produced). */
36
65
  new_this_run?: {
@@ -102,6 +131,16 @@ export interface DashboardMetrics {
102
131
  bytes: number;
103
132
  path?: string;
104
133
  };
134
+ /**
135
+ * #427: reconsolidation scout counters, flat snake_case mirroring the
136
+ * stage report (scout_scanned / scout_correction_shaped /
137
+ * scout_candidates_found). Present whenever consolidation ran —
138
+ * quiet-night zeros are real values; undefined = no consolidation that
139
+ * night (and always absent on backfill rows).
140
+ */
141
+ scout_scanned?: number;
142
+ scout_correction_shaped?: number;
143
+ scout_candidates_found?: number;
105
144
  };
106
145
  /** Corpus capacity (#245). `memory_soft_cap` is the configured ceiling (0 =
107
146
  * disabled); always present in real snapshots, undefined on backfilled
@@ -138,6 +177,12 @@ export interface DashboardData {
138
177
  };
139
178
  headline: {
140
179
  total_memories: number;
180
+ /**
181
+ * #422 Phase 2 — LIVE (non-absorbed) memory count: what recall serves and
182
+ * the field paints. The console's overview counter and cap bar key off
183
+ * THIS (total_memories stays ALL rows for backcompat — the growth chart).
184
+ */
185
+ live_memories: number;
141
186
  uses_per_showing: number | null;
142
187
  cold_count: number;
143
188
  /** Corpus vs cap (#245). `memory_soft_cap` is 0 when the cap is disabled
@@ -163,6 +208,47 @@ export interface DashboardData {
163
208
  by_type: Record<string, number>;
164
209
  by_domain: Record<string, number>;
165
210
  by_source_agent: Record<string, number>;
211
+ /** #421 machine × harness; "(unstamped)" = pre-v15 rows. */
212
+ by_source_machine: Record<string, number>;
213
+ };
214
+ /**
215
+ * #422 Phase 2 — capture health: per machine × agent /distill outcome
216
+ * accounting for the most recent day with rows (posts / sessions / bytes /
217
+ * held / retried). ALWAYS present; {day: null, rows: []} when nothing is
218
+ * recorded (fresh install / all rows pruned — the page degrades to the
219
+ * phase-1 counts-only rows).
220
+ */
221
+ capture_health: {
222
+ day: string | null;
223
+ rows: Array<{
224
+ machine: string;
225
+ agent: string;
226
+ posts: number;
227
+ sessions: number;
228
+ bytes: number;
229
+ held: number;
230
+ retried: number;
231
+ }>;
232
+ };
233
+ /**
234
+ * #423 phase 3 — fleet presence: the operator's capture pauses + per-bundle
235
+ * last-seen (derived ONLY from /distill activity — see capture-pause.ts's
236
+ * module doc for why recall traffic can never attribute). ALWAYS present;
237
+ * empty arrays when nothing is recorded (fresh install). The page degrades
238
+ * to the phase-2 rendering when the block is absent (pre-phase-3 server).
239
+ */
240
+ fleet: {
241
+ pauses: Array<{
242
+ machine: string;
243
+ harness: string;
244
+ paused_at: string;
245
+ }>;
246
+ last_seen: Array<{
247
+ machine: string;
248
+ harness: string;
249
+ last_seen: string;
250
+ last_outcome: string;
251
+ }>;
166
252
  };
167
253
  digest: {
168
254
  date: string | null;
@@ -215,9 +301,23 @@ export interface DashboardData {
215
301
  /**
216
302
  * Compute the full corpus-shape metrics from the live DB. The same function
217
303
  * backs both the nightly snapshot writer and the live /dashboard/data
218
- * composition view — one definition of corpus shape.
304
+ * composition view — one definition of corpus shape. #422 adds the
305
+ * live/absorbed split (`totals.live_mem`/`absorbed`) and `stage_counts` —
306
+ * both derived here so every snapshot row carries them automatically.
219
307
  */
220
308
  export declare function computeDashboardMetrics(db: Database.Database): DashboardMetrics;
309
+ /**
310
+ * Count LIVE (non-absorbed) memories per derived stage — the snapshot's
311
+ * `stage_counts` (#422 Phase 2). Same math as the field payload via the
312
+ * shared deriveStageForRow; absorbed rows are excluded exactly like the
313
+ * field paints them (invisible evidence is not a maturity stage).
314
+ */
315
+ export declare function computeStageCounts(db: Database.Database): {
316
+ forming: number;
317
+ belief: number;
318
+ truth: number;
319
+ fading: number;
320
+ };
221
321
  export interface NightlyDelta {
222
322
  added: number;
223
323
  lessonsGenerated?: number;
@@ -284,6 +384,15 @@ export interface NightlyDelta {
284
384
  backupPath?: string;
285
385
  backupBytes?: number;
286
386
  backupOk?: boolean;
387
+ /**
388
+ * #427: reconsolidation scout counters. Forwarded whenever consolidation
389
+ * ran (the stage's quiet-night shape carries real zeros — the scan doesn't
390
+ * run on a quiet night); undefined when consolidation didn't run at all.
391
+ * Stamped flat snake_case, mirroring the stage report.
392
+ */
393
+ scoutScanned?: number;
394
+ scoutCorrectionShaped?: number;
395
+ scoutCandidatesFound?: number;
287
396
  }
288
397
  /**
289
398
  * Write one snapshot row for `runAt` (an ISO timestamp the caller chooses —
@@ -369,3 +478,213 @@ export declare function accountHandler(getConfig: () => Record<string, unknown>
369
478
  * {error} exactly like accountHandler.
370
479
  */
371
480
  export declare function accountTokenHandler(getToken: () => string | undefined): express.RequestHandler;
481
+ /** One memory in the field payload — minimal fields, no content body. */
482
+ export interface DashboardFieldMemory {
483
+ id: string;
484
+ /** First content line, de-markdowned, ≤ RECALL_TITLE_CHARS (memoryTitle). */
485
+ title: string;
486
+ /** Derived primary domain (argmax tag weight); null = unscoped. */
487
+ domain: string | null;
488
+ /** Derived maturity stage (E-reframe: presentation, never stored). */
489
+ stage: Stage;
490
+ /** effectiveStrength rounded to 1e-6, same as retrieval's wire format. */
491
+ strength: number;
492
+ access_count: number;
493
+ created_at: string;
494
+ source_agent: string | null;
495
+ /** Machine the capture ran on (#421 machine × harness). Null on rows
496
+ * written before migration v15 — the console groups those under
497
+ * "earlier captures". */
498
+ source_machine: string | null;
499
+ }
500
+ /** One link edge — endpoints + relationship only (strength stays off-wire). */
501
+ export interface DashboardFieldLink {
502
+ a: string;
503
+ b: string;
504
+ rel: string;
505
+ }
506
+ /** The /dashboard/field response. */
507
+ export interface DashboardField {
508
+ generated_at: string;
509
+ /** The thresholds the stages were derived with, echoed so the page paints
510
+ * from the same constants the server scored with (calibration.ts is the
511
+ * single home — the echo is display, not a second source). Recall grades
512
+ * are GONE (#426 owner semantics ruling 2026-09-13: recall depends on the
513
+ * conversation, a graded scale implies a target that does not exist). */
514
+ thresholds: {
515
+ stage: {
516
+ fading_days: number;
517
+ fading_strength: number;
518
+ belief: number;
519
+ truth: number;
520
+ };
521
+ /** The recall relevance floor (RECALL_MIN_SIMILARITY) — echoed so the
522
+ * console gates its /search calls with the same constant the server's
523
+ * own recall gates with (#409 console polish; the echo is display, not
524
+ * a second source — same rule as the stage bands). Absent nothing: the
525
+ * key always rides the payload; a pre-polish page ignores it. */
526
+ recall_min_similarity: number;
527
+ /** #426 final ruling 2026-09-13: the console's recall-level band edges
528
+ * (RECALL_USES_* — PROVISIONAL, owner anchor 2026-09-13, pending fleet
529
+ * telemetry). Same echo rule as recall_min_similarity: calibration.ts is
530
+ * the single home, the page never hardcodes the edges, and a page older
531
+ * than this key simply ignores it (no band). These classify the console
532
+ * card's zone word and position the gradient; per-memory recall grades
533
+ * remain GONE from the field payload (the earlier #426 ruling — a
534
+ * memory's recall fitness is conversation-dependent). */
535
+ recall_uses: {
536
+ /** Below this reads Low. */
537
+ low_max: number;
538
+ /** Below this (and ≥ low_max) reads Normal; at/above reads Overfetching. */
539
+ normal_max: number;
540
+ /** Display-axis maximum (marker clamps here). */
541
+ axis_max: number;
542
+ };
543
+ };
544
+ memories: DashboardFieldMemory[];
545
+ links: DashboardFieldLink[];
546
+ }
547
+ /**
548
+ * The pure data handler for GET /dashboard/field. Reads the whole live store
549
+ * (minus absorbed rows) + all link edges, derives stage + effective strength
550
+ * per memory, and returns the field payload. Never throws on empty stores —
551
+ * an empty brain is a valid field.
552
+ */
553
+ export declare function handleDashboardField(db: Database.Database): {
554
+ status: number;
555
+ body: DashboardField;
556
+ };
557
+ /**
558
+ * Express adapter for GET /dashboard/field. Bearer-only by construction
559
+ * (mounted after createAuthMiddleware — no exemption). Gzips the payload
560
+ * when the client advertises Accept-Encoding: gzip: the field is one row per
561
+ * memory on possibly very large stores, so the wire cost is the point
562
+ * (minimal fields + compression, per the #409 payload-size risk note).
563
+ * Failures surface as a 500 {error} like the sibling adapters.
564
+ */
565
+ export declare function dashboardFieldHandler(getDb: () => Database.Database): express.RequestHandler;
566
+ /** One night of the replay ledger. `by_agent` keys are source_agent strings
567
+ * ("(unknown)" when the memory row is gone); learned counts added, enriched
568
+ * counts distinct enriched memories, merged counts dedup losers. */
569
+ export interface DashboardEventsNight {
570
+ date: string;
571
+ added: string[];
572
+ enriched: string[];
573
+ merged: Array<{
574
+ loser: string;
575
+ canonical: string;
576
+ }>;
577
+ linked: Array<{
578
+ a: string;
579
+ b: string;
580
+ }>;
581
+ by_agent: Record<string, {
582
+ learned: number;
583
+ enriched: number;
584
+ merged: number;
585
+ }>;
586
+ }
587
+ /** The /dashboard/events response. */
588
+ export interface DashboardEvents {
589
+ nights: DashboardEventsNight[];
590
+ }
591
+ /**
592
+ * The pure data handler for GET /dashboard/events?days=N. Buckets the four
593
+ * event sources into UTC nights (only nights with events, ascending) within
594
+ * the last `days` calendar days (today inclusive).
595
+ */
596
+ export declare function handleDashboardEvents(db: Database.Database, query: {
597
+ days?: unknown;
598
+ }): {
599
+ status: number;
600
+ body: DashboardEvents | {
601
+ error: string;
602
+ };
603
+ };
604
+ /**
605
+ * Express adapter for GET /dashboard/events. Bearer-only by construction
606
+ * (mounted after createAuthMiddleware — no exemption). The ledger is ids
607
+ * only (no titles), so it stays plain JSON — gzip is the /field adapter's
608
+ * concern. Failures surface as a 500 {error} like the sibling adapters.
609
+ */
610
+ export declare function dashboardEventsHandler(getDb: () => Database.Database): express.RequestHandler;
611
+ /** The GET /dashboard/model + PUT-success response shape (snake_case wire). */
612
+ export interface DashboardModelSettings {
613
+ /** Boot-resolved runtime provider label (e.g. "ollama", "claude-cli",
614
+ * "openai" for the openai-compat path); null when the daemon runs no LLM.
615
+ * Runtime truth, not config — the card's model line comes from
616
+ * /health/detail, this carries only the provider. */
617
+ provider: string | null;
618
+ /** config llmBackend — null when unset (baseUrl+apiKey = openai-compat). */
619
+ backend: string | null;
620
+ /** config llmBaseUrl — null when unset (defaults are the UI's placeholders,
621
+ * never resolved here). */
622
+ base_url: string | null;
623
+ /** config llmModel — null when unset. */
624
+ model: string | null;
625
+ /** config maxTokens — null when unset. */
626
+ max_tokens: number | null;
627
+ /** config enableThinking — null when unset. */
628
+ enable_thinking: boolean | null;
629
+ /** Whether llmApiKey is set. The VALUE is never on the wire. */
630
+ api_key_set: boolean;
631
+ /** Constant true — the daemon resolves config at boot, so every write
632
+ * lands on restart. The modal footnotes it. */
633
+ applies_on_restart: true;
634
+ }
635
+ /**
636
+ * The pure GET handler: echo the CONFIG values raw (null when unset — the UI
637
+ * shows defaults as placeholders, so this never resolves them) + the runtime
638
+ * provider from the daemon's in-memory llmConfig.
639
+ */
640
+ export declare function handleDashboardModelGet(config: Record<string, unknown> | null | undefined, llmConfig: {
641
+ provider: string;
642
+ } | null): {
643
+ status: 200;
644
+ body: DashboardModelSettings;
645
+ };
646
+ /**
647
+ * The pure PUT handler: validate the allowlisted subset, persist via the
648
+ * injected writer (which THROWS on a malformed config — the adapter maps that
649
+ * to a 500 and the file stays untouched), answer with the fresh GET shape
650
+ * built from the post-write config. `null` REMOVES a config key.
651
+ */
652
+ export declare function handleDashboardModelPut(body: unknown, persist: (updates: Record<string, unknown>) => Record<string, unknown>, getLlmConfig: () => {
653
+ provider: string;
654
+ } | null): {
655
+ status: number;
656
+ body: DashboardModelSettings | {
657
+ error: string;
658
+ };
659
+ };
660
+ /**
661
+ * Express adapter for GET /dashboard/model. Bearer-only by construction
662
+ * (mounted after createAuthMiddleware — NO exemption, unlike the page shells:
663
+ * this carries install config). Failures surface as a 500 {error}.
664
+ */
665
+ export declare function dashboardModelGetHandler(getConfig: () => Record<string, unknown> | null | undefined, getLlmConfig: () => {
666
+ provider: string;
667
+ } | null): express.RequestHandler;
668
+ /**
669
+ * Express adapter for PUT /dashboard/model. Same auth posture as the GET.
670
+ * The injected persist closure owns the config path (the server passes
671
+ * init.ts persistConfigUpdates over stateDir/config.json); its load/persist
672
+ * failures (malformed config, unwritable file) map to a 500 {error} with the
673
+ * file left untouched — never a silent partial write.
674
+ */
675
+ export declare function dashboardModelPutHandler(persist: (updates: Record<string, unknown>) => Record<string, unknown>, getLlmConfig: () => {
676
+ provider: string;
677
+ } | null): express.RequestHandler;
678
+ /** The PUT's body: {machine?: string|null, harness: string, paused: boolean}. */
679
+ export declare function handleDashboardCapturePausePut(body: unknown, setPause: (machine: string, harness: string, paused: boolean) => string | null): {
680
+ status: number;
681
+ body: Record<string, unknown>;
682
+ };
683
+ /**
684
+ * Express adapter for PUT /dashboard/capture-pause. Bearer-only by
685
+ * construction (mounted after createAuthMiddleware, no shell exemption).
686
+ * The effect is immediate — no restart: the /distill handler reads the
687
+ * pause table on every post, so the very next capture POST from the bundle
688
+ * is skipped (200) or captured as before.
689
+ */
690
+ export declare function dashboardCapturePausePutHandler(getDb: () => Database.Database): express.RequestHandler;