@psiclawops/hypermem 0.1.0 → 0.5.1

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 (153) hide show
  1. package/ARCHITECTURE.md +4 -3
  2. package/README.md +457 -174
  3. package/dist/background-indexer.d.ts +19 -4
  4. package/dist/background-indexer.d.ts.map +1 -1
  5. package/dist/background-indexer.js +329 -17
  6. package/dist/cache.d.ts +110 -0
  7. package/dist/cache.d.ts.map +1 -0
  8. package/dist/cache.js +495 -0
  9. package/dist/compaction-fence.d.ts +1 -1
  10. package/dist/compaction-fence.js +1 -1
  11. package/dist/compositor.d.ts +114 -27
  12. package/dist/compositor.d.ts.map +1 -1
  13. package/dist/compositor.js +1678 -229
  14. package/dist/content-type-classifier.d.ts +41 -0
  15. package/dist/content-type-classifier.d.ts.map +1 -0
  16. package/dist/content-type-classifier.js +181 -0
  17. package/dist/cross-agent.d.ts +5 -0
  18. package/dist/cross-agent.d.ts.map +1 -1
  19. package/dist/cross-agent.js +5 -0
  20. package/dist/db.d.ts +1 -1
  21. package/dist/db.d.ts.map +1 -1
  22. package/dist/db.js +6 -2
  23. package/dist/desired-state-store.d.ts +1 -1
  24. package/dist/desired-state-store.d.ts.map +1 -1
  25. package/dist/desired-state-store.js +15 -5
  26. package/dist/doc-chunk-store.d.ts +26 -1
  27. package/dist/doc-chunk-store.d.ts.map +1 -1
  28. package/dist/doc-chunk-store.js +114 -1
  29. package/dist/doc-chunker.d.ts +1 -1
  30. package/dist/doc-chunker.js +1 -1
  31. package/dist/dreaming-promoter.d.ts +86 -0
  32. package/dist/dreaming-promoter.d.ts.map +1 -0
  33. package/dist/dreaming-promoter.js +381 -0
  34. package/dist/episode-store.d.ts +2 -1
  35. package/dist/episode-store.d.ts.map +1 -1
  36. package/dist/episode-store.js +4 -4
  37. package/dist/fact-store.d.ts +19 -1
  38. package/dist/fact-store.d.ts.map +1 -1
  39. package/dist/fact-store.js +64 -3
  40. package/dist/fleet-store.d.ts +1 -1
  41. package/dist/fleet-store.js +1 -1
  42. package/dist/fos-mod.d.ts +178 -0
  43. package/dist/fos-mod.d.ts.map +1 -0
  44. package/dist/fos-mod.js +416 -0
  45. package/dist/hybrid-retrieval.d.ts +5 -1
  46. package/dist/hybrid-retrieval.d.ts.map +1 -1
  47. package/dist/hybrid-retrieval.js +7 -3
  48. package/dist/image-eviction.d.ts +49 -0
  49. package/dist/image-eviction.d.ts.map +1 -0
  50. package/dist/image-eviction.js +251 -0
  51. package/dist/index.d.ts +50 -11
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +73 -43
  54. package/dist/keystone-scorer.d.ts +51 -0
  55. package/dist/keystone-scorer.d.ts.map +1 -0
  56. package/dist/keystone-scorer.js +52 -0
  57. package/dist/knowledge-graph.d.ts +1 -1
  58. package/dist/knowledge-graph.js +1 -1
  59. package/dist/knowledge-lint.d.ts +29 -0
  60. package/dist/knowledge-lint.d.ts.map +1 -0
  61. package/dist/knowledge-lint.js +116 -0
  62. package/dist/knowledge-store.d.ts +1 -1
  63. package/dist/knowledge-store.d.ts.map +1 -1
  64. package/dist/knowledge-store.js +8 -2
  65. package/dist/library-schema.d.ts +3 -3
  66. package/dist/library-schema.d.ts.map +1 -1
  67. package/dist/library-schema.js +324 -3
  68. package/dist/message-store.d.ts +15 -2
  69. package/dist/message-store.d.ts.map +1 -1
  70. package/dist/message-store.js +51 -1
  71. package/dist/metrics-dashboard.d.ts +114 -0
  72. package/dist/metrics-dashboard.d.ts.map +1 -0
  73. package/dist/metrics-dashboard.js +260 -0
  74. package/dist/obsidian-exporter.d.ts +57 -0
  75. package/dist/obsidian-exporter.d.ts.map +1 -0
  76. package/dist/obsidian-exporter.js +274 -0
  77. package/dist/obsidian-watcher.d.ts +147 -0
  78. package/dist/obsidian-watcher.d.ts.map +1 -0
  79. package/dist/obsidian-watcher.js +403 -0
  80. package/dist/open-domain.d.ts +46 -0
  81. package/dist/open-domain.d.ts.map +1 -0
  82. package/dist/open-domain.js +125 -0
  83. package/dist/preference-store.d.ts +1 -1
  84. package/dist/preference-store.js +1 -1
  85. package/dist/preservation-gate.d.ts +1 -1
  86. package/dist/preservation-gate.js +1 -1
  87. package/dist/proactive-pass.d.ts +63 -0
  88. package/dist/proactive-pass.d.ts.map +1 -0
  89. package/dist/proactive-pass.js +239 -0
  90. package/dist/profiles.d.ts +44 -0
  91. package/dist/profiles.d.ts.map +1 -0
  92. package/dist/profiles.js +227 -0
  93. package/dist/provider-translator.d.ts +13 -3
  94. package/dist/provider-translator.d.ts.map +1 -1
  95. package/dist/provider-translator.js +63 -9
  96. package/dist/rate-limiter.d.ts +1 -1
  97. package/dist/rate-limiter.js +1 -1
  98. package/dist/repair-tool-pairs.d.ts +38 -0
  99. package/dist/repair-tool-pairs.d.ts.map +1 -0
  100. package/dist/repair-tool-pairs.js +138 -0
  101. package/dist/retrieval-policy.d.ts +51 -0
  102. package/dist/retrieval-policy.d.ts.map +1 -0
  103. package/dist/retrieval-policy.js +77 -0
  104. package/dist/schema.d.ts +2 -2
  105. package/dist/schema.d.ts.map +1 -1
  106. package/dist/schema.js +28 -2
  107. package/dist/secret-scanner.d.ts +1 -1
  108. package/dist/secret-scanner.js +1 -1
  109. package/dist/seed.d.ts +2 -2
  110. package/dist/seed.js +2 -2
  111. package/dist/session-flusher.d.ts +53 -0
  112. package/dist/session-flusher.d.ts.map +1 -0
  113. package/dist/session-flusher.js +69 -0
  114. package/dist/session-topic-map.d.ts +41 -0
  115. package/dist/session-topic-map.d.ts.map +1 -0
  116. package/dist/session-topic-map.js +77 -0
  117. package/dist/spawn-context.d.ts +54 -0
  118. package/dist/spawn-context.d.ts.map +1 -0
  119. package/dist/spawn-context.js +159 -0
  120. package/dist/system-store.d.ts +1 -1
  121. package/dist/system-store.js +1 -1
  122. package/dist/temporal-store.d.ts +80 -0
  123. package/dist/temporal-store.d.ts.map +1 -0
  124. package/dist/temporal-store.js +149 -0
  125. package/dist/topic-detector.d.ts +35 -0
  126. package/dist/topic-detector.d.ts.map +1 -0
  127. package/dist/topic-detector.js +249 -0
  128. package/dist/topic-store.d.ts +1 -1
  129. package/dist/topic-store.js +1 -1
  130. package/dist/topic-synthesizer.d.ts +51 -0
  131. package/dist/topic-synthesizer.d.ts.map +1 -0
  132. package/dist/topic-synthesizer.js +315 -0
  133. package/dist/trigger-registry.d.ts +63 -0
  134. package/dist/trigger-registry.d.ts.map +1 -0
  135. package/dist/trigger-registry.js +163 -0
  136. package/dist/types.d.ts +214 -10
  137. package/dist/types.d.ts.map +1 -1
  138. package/dist/types.js +1 -1
  139. package/dist/vector-store.d.ts +43 -5
  140. package/dist/vector-store.d.ts.map +1 -1
  141. package/dist/vector-store.js +189 -10
  142. package/dist/version.d.ts +34 -0
  143. package/dist/version.d.ts.map +1 -0
  144. package/dist/version.js +34 -0
  145. package/dist/wiki-page-emitter.d.ts +65 -0
  146. package/dist/wiki-page-emitter.d.ts.map +1 -0
  147. package/dist/wiki-page-emitter.js +258 -0
  148. package/dist/work-store.d.ts +1 -1
  149. package/dist/work-store.js +1 -1
  150. package/package.json +15 -5
  151. package/dist/redis.d.ts +0 -188
  152. package/dist/redis.d.ts.map +0 -1
  153. package/dist/redis.js +0 -534
@@ -1,5 +1,5 @@
1
1
  /**
2
- * HyperMem Library Schema — Fleet-Wide Structured Knowledge
2
+ * hypermem Library Schema — Fleet-Wide Structured Knowledge
3
3
  *
4
4
  * Single database: ~/.openclaw/hypermem/library.db
5
5
  * The "crown jewel" — durable, backed up, low-write-frequency.
@@ -17,6 +17,6 @@
17
17
  * 10. Topics (cross-session thread tracking)
18
18
  */
19
19
  import type { DatabaseSync } from 'node:sqlite';
20
- export declare const LIBRARY_SCHEMA_VERSION = 7;
21
- export declare function migrateLibrary(db: DatabaseSync): void;
20
+ export declare const LIBRARY_SCHEMA_VERSION = 13;
21
+ export declare function migrateLibrary(db: DatabaseSync, engineVersion?: string): void;
22
22
  //# sourceMappingURL=library-schema.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"library-schema.d.ts","sourceRoot":"","sources":["../src/library-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,eAAO,MAAM,sBAAsB,IAAI,CAAC;AA4rBxC,wBAAgB,cAAc,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CA8DrD"}
1
+ {"version":3,"file":"library-schema.d.ts","sourceRoot":"","sources":["../src/library-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAs5BzC,wBAAgB,cAAc,CAAC,EAAE,EAAE,YAAY,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CA8L7E"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * HyperMem Library Schema — Fleet-Wide Structured Knowledge
2
+ * hypermem Library Schema — Fleet-Wide Structured Knowledge
3
3
  *
4
4
  * Single database: ~/.openclaw/hypermem/library.db
5
5
  * The "crown jewel" — durable, backed up, low-write-frequency.
@@ -16,7 +16,7 @@
16
16
  * 9. Work items (fleet kanban)
17
17
  * 10. Topics (cross-session thread tracking)
18
18
  */
19
- export const LIBRARY_SCHEMA_VERSION = 7;
19
+ export const LIBRARY_SCHEMA_VERSION = 13;
20
20
  function nowIso() {
21
21
  return new Date().toISOString();
22
22
  }
@@ -662,8 +662,211 @@ function applyV7KnowledgeVersioning(db) {
662
662
  END
663
663
  `);
664
664
  }
665
+ // ── V9: Add session_key to doc_chunks ───────────────────────
666
+ // Enables ephemeral session-scoped doc chunks for subagent context inheritance.
667
+ // Chunks stored with a session_key are transient — clearSessionChunks() removes them.
668
+ function applyV9DocChunkSessionKey(db) {
669
+ const cols = db.prepare('PRAGMA table_info(doc_chunks)').all()
670
+ .map(r => r.name);
671
+ if (!cols.includes('session_key')) {
672
+ db.exec('ALTER TABLE doc_chunks ADD COLUMN session_key TEXT');
673
+ }
674
+ db.exec('CREATE INDEX IF NOT EXISTS idx_doc_chunks_session ON doc_chunks(session_key) WHERE session_key IS NOT NULL');
675
+ }
676
+ // ── V8: Add source_message_id to episodes ───────────────────
677
+ function applyV8EpisodeSourceMessageId(db) {
678
+ // ALTER TABLE ADD COLUMN is safe — existing rows get NULL for new column
679
+ const cols = db.prepare('PRAGMA table_info(episodes)').all()
680
+ .map(r => r.name);
681
+ if (!cols.includes('source_message_id')) {
682
+ db.exec('ALTER TABLE episodes ADD COLUMN source_message_id INTEGER');
683
+ }
684
+ db.exec('CREATE INDEX IF NOT EXISTS idx_episodes_source_msg ON episodes(agent_id, source_message_id)');
685
+ }
686
+ // ── V12: FOS / MOD tables + builtin seed data ──────────────
687
+ function applyV12FosMod(db) {
688
+ // fleet_output_standard: fleet-wide output formatting standards
689
+ db.exec(`
690
+ CREATE TABLE IF NOT EXISTS fleet_output_standard (
691
+ id TEXT PRIMARY KEY,
692
+ name TEXT NOT NULL,
693
+ directives TEXT NOT NULL,
694
+ task_variants TEXT DEFAULT '{}',
695
+ token_budget INTEGER DEFAULT 250,
696
+ active INTEGER DEFAULT 0,
697
+ source TEXT DEFAULT 'builtin',
698
+ version INTEGER DEFAULT 1,
699
+ last_validated_at TEXT,
700
+ created_at TEXT NOT NULL,
701
+ updated_at TEXT NOT NULL
702
+ )
703
+ `);
704
+ // model_output_directives: per-model corrections and calibration
705
+ db.exec(`
706
+ CREATE TABLE IF NOT EXISTS model_output_directives (
707
+ id TEXT PRIMARY KEY,
708
+ match_pattern TEXT NOT NULL,
709
+ priority INTEGER DEFAULT 0,
710
+ corrections TEXT NOT NULL,
711
+ calibration TEXT NOT NULL,
712
+ task_overrides TEXT DEFAULT '{}',
713
+ token_budget INTEGER DEFAULT 150,
714
+ version INTEGER DEFAULT 1,
715
+ source TEXT DEFAULT 'builtin',
716
+ enabled INTEGER DEFAULT 1,
717
+ last_validated_at TEXT,
718
+ created_at TEXT NOT NULL,
719
+ updated_at TEXT NOT NULL
720
+ )
721
+ `);
722
+ // output_metrics: per-request telemetry for drift analytics
723
+ db.exec(`
724
+ CREATE TABLE IF NOT EXISTS output_metrics (
725
+ id TEXT PRIMARY KEY,
726
+ timestamp TEXT NOT NULL,
727
+ agent_id TEXT NOT NULL,
728
+ session_key TEXT NOT NULL,
729
+ model_id TEXT NOT NULL,
730
+ provider TEXT NOT NULL,
731
+ fos_version INTEGER,
732
+ mod_version INTEGER,
733
+ mod_id TEXT,
734
+ task_type TEXT,
735
+ output_tokens INTEGER NOT NULL,
736
+ input_tokens INTEGER,
737
+ cache_read_tokens INTEGER,
738
+ corrections_fired TEXT DEFAULT '[]',
739
+ latency_ms INTEGER,
740
+ created_at TEXT NOT NULL
741
+ )
742
+ `);
743
+ db.exec('CREATE INDEX IF NOT EXISTS idx_output_metrics_model ON output_metrics(model_id, timestamp)');
744
+ db.exec('CREATE INDEX IF NOT EXISTS idx_output_metrics_agent ON output_metrics(agent_id, timestamp)');
745
+ // ── Seed builtin FOS profile ──
746
+ const now = nowIso();
747
+ const fosDirectives = JSON.stringify({
748
+ structural: [
749
+ 'Lead with the answer. Conclusion first, reasoning after.',
750
+ 'Headers earn their place. Under 200 words: no headers.',
751
+ 'Lists cap at 7 items. Technical enumerations exempt.',
752
+ 'One metaphor lands. Two is the limit.',
753
+ ],
754
+ anti_patterns: [
755
+ 'No sycophantic openings: Great question, Certainly, Absolutely, Of course',
756
+ 'No em dashes',
757
+ 'No preamble restating the question',
758
+ 'No: Let me know if you need anything else',
759
+ 'No AI vocabulary: delve, tapestry, pivotal, fostering, garner, underscore, vibrant, leverage, noteworthy, realm',
760
+ 'No unverifiable references — don\'t cite "you mentioned earlier" or "as discussed" without a direct quote',
761
+ 'No claiming actions completed without tool results to back them up',
762
+ 'No attributing statements to people without quoting the actual message',
763
+ ],
764
+ density_targets: {
765
+ simple: '1-3 sentences',
766
+ analysis: '200-500 words',
767
+ code: 'code first, explain only non-obvious parts',
768
+ },
769
+ voice: [
770
+ 'Every sentence states a fact, makes a decision, or advances an argument',
771
+ 'Numbers over adjectives',
772
+ 'Vary sentence length deliberately',
773
+ 'Match confidence to evidence: facts zero hedges, inference one hedge max',
774
+ ],
775
+ });
776
+ const fosVariants = JSON.stringify({
777
+ 'council-deliberation': {
778
+ density_target: '400-800 words. Depth over brevity.',
779
+ structure: 'Headers required. Position statement, risk assessment, confidence, action.',
780
+ },
781
+ 'code-generation': {
782
+ density_target: 'Minimize prose. Code is the deliverable.',
783
+ list_cap: 'DISABLED',
784
+ },
785
+ 'quick-answer': {
786
+ density_target: '1-3 sentences.',
787
+ structure: 'No headers. No lists unless the answer is genuinely a list.',
788
+ },
789
+ });
790
+ const existingFos = db.prepare("SELECT id FROM fleet_output_standard WHERE id = 'psiclawops-default'").get();
791
+ if (!existingFos) {
792
+ db.prepare(`
793
+ INSERT INTO fleet_output_standard (id, name, directives, task_variants, token_budget, active, source, version, created_at, updated_at)
794
+ VALUES ('psiclawops-default', 'PsiClawOps Default', ?, ?, 250, 1, 'builtin', 1, ?, ?)
795
+ `).run(fosDirectives, fosVariants, now, now);
796
+ }
797
+ // ── Seed builtin MOD profiles ──
798
+ const mods = [
799
+ {
800
+ id: 'gpt-5.4',
801
+ match_pattern: 'gpt-5.4*',
802
+ priority: 10,
803
+ corrections: JSON.stringify([
804
+ { id: 'plan-loop', rule: 'If 2+ responses without concrete output, execute immediately. Ship partial.', severity: 'hard' },
805
+ { id: 'first-person-opening', rule: 'Do not open with I.', severity: 'medium' },
806
+ { id: 'throat-clearing', rule: 'No preamble before the answer.', severity: 'medium' },
807
+ { id: 'conditional-hedging', rule: 'Decision questions: answer + 1-2 reasons. No if-X-then-Y branching.', severity: 'medium' },
808
+ ]),
809
+ calibration: JSON.stringify([
810
+ { id: 'verbosity-offset', fos_target: 'analysis: 200-500 words', model_tendency: '~600 words vs Opus baseline', adjustment: 'Actively compress. Your natural output is ~2x the target. Cut first drafts in half.' },
811
+ { id: 'list-length-offset', fos_target: '7 items max', model_tendency: 'defaults to 12-15 items', adjustment: 'After drafting a list, cut the bottom half.' },
812
+ ]),
813
+ },
814
+ {
815
+ id: 'claude-opus-4.6',
816
+ match_pattern: 'claude-opus-4*',
817
+ priority: 10,
818
+ corrections: JSON.stringify([
819
+ { id: 'over-structuring', rule: 'Resist adding headers and sections to short answers.', severity: 'medium' },
820
+ { id: 'premature-enumeration', rule: "Don't list when prose works. Lists require 3+ genuinely distinct items.", severity: 'medium' },
821
+ ]),
822
+ calibration: JSON.stringify([
823
+ { id: 'verbosity-offset', fos_target: 'analysis: 200-500 words', model_tendency: '1.1x target', adjustment: 'Near target. Minor compression on detailed analysis.' },
824
+ ]),
825
+ },
826
+ {
827
+ id: 'claude-sonnet-4.6',
828
+ match_pattern: 'claude-sonnet-4*',
829
+ priority: 10,
830
+ corrections: JSON.stringify([
831
+ { id: 'caveat-frontloading', rule: "Don't open with caveats. State the answer, then caveats if needed.", severity: 'medium' },
832
+ { id: 'safety-hedging', rule: 'Minimize safety qualifiers on unambiguous requests.', severity: 'medium' },
833
+ ]),
834
+ calibration: JSON.stringify([
835
+ { id: 'verbosity-offset', fos_target: 'analysis: 200-500 words', model_tendency: '1.3x target', adjustment: 'Compress by ~25%. Cut qualifications and restatements.' },
836
+ ]),
837
+ },
838
+ {
839
+ id: 'gemini-3.1',
840
+ match_pattern: 'gemini-3.1*',
841
+ priority: 10,
842
+ corrections: JSON.stringify([
843
+ { id: 'numbered-list-default', rule: "Don't default to numbered lists. Use prose unless order matters.", severity: 'hard' },
844
+ { id: 'source-attribution-noise', rule: 'Skip attribution boilerplate unless sourcing is specifically requested.', severity: 'medium' },
845
+ ]),
846
+ calibration: JSON.stringify([
847
+ { id: 'list-length-offset', fos_target: '7 items max', model_tendency: '1.5x target', adjustment: 'Cut lists to 7 items. Merge or drop the rest.' },
848
+ ]),
849
+ },
850
+ {
851
+ id: 'default',
852
+ match_pattern: '*',
853
+ priority: 0,
854
+ corrections: JSON.stringify([]),
855
+ calibration: JSON.stringify([]),
856
+ },
857
+ ];
858
+ for (const mod of mods) {
859
+ const existing = db.prepare('SELECT id FROM model_output_directives WHERE id = ?').get(mod.id);
860
+ if (!existing) {
861
+ db.prepare(`
862
+ INSERT INTO model_output_directives (id, match_pattern, priority, corrections, calibration, task_overrides, token_budget, version, source, enabled, created_at, updated_at)
863
+ VALUES (?, ?, ?, ?, ?, '{}', 150, 1, 'builtin', 1, ?, ?)
864
+ `).run(mod.id, mod.match_pattern, mod.priority, mod.corrections, mod.calibration, now, now);
865
+ }
866
+ }
867
+ }
665
868
  // ── Migration runner ──────────────────────────────────────────
666
- export function migrateLibrary(db) {
869
+ export function migrateLibrary(db, engineVersion) {
667
870
  db.exec(`
668
871
  CREATE TABLE IF NOT EXISTS schema_version (
669
872
  version INTEGER PRIMARY KEY,
@@ -713,5 +916,123 @@ export function migrateLibrary(db) {
713
916
  db.prepare('INSERT INTO schema_version (version, applied_at) VALUES (?, ?)')
714
917
  .run(7, nowIso());
715
918
  }
919
+ if (currentVersion < 8) {
920
+ applyV8EpisodeSourceMessageId(db);
921
+ db.prepare('INSERT INTO schema_version (version, applied_at) VALUES (?, ?)')
922
+ .run(8, nowIso());
923
+ }
924
+ if (currentVersion < 9) {
925
+ applyV9DocChunkSessionKey(db);
926
+ db.prepare('INSERT INTO schema_version (version, applied_at) VALUES (?, ?)')
927
+ .run(9, nowIso());
928
+ }
929
+ if (currentVersion < 10) {
930
+ db.exec(`
931
+ CREATE TABLE IF NOT EXISTS meta (
932
+ key TEXT PRIMARY KEY,
933
+ value TEXT NOT NULL,
934
+ updated_at TEXT NOT NULL
935
+ )
936
+ `);
937
+ db.prepare('INSERT INTO schema_version (version, applied_at) VALUES (?, ?)')
938
+ .run(10, nowIso());
939
+ }
940
+ // ── V11: Topics FTS + indexer watermarks ──────────────────
941
+ // topics_fts was missing from V3 (topics table was created without FTS).
942
+ // indexer_watermarks tracks per-agent indexer progress for resumable indexing.
943
+ if (currentVersion < 11) {
944
+ db.exec(`
945
+ CREATE VIRTUAL TABLE IF NOT EXISTS topics_fts USING fts5(
946
+ name,
947
+ description,
948
+ content='topics',
949
+ content_rowid='id'
950
+ )
951
+ `);
952
+ db.exec(`
953
+ CREATE TRIGGER IF NOT EXISTS topics_fts_ai AFTER INSERT ON topics BEGIN
954
+ INSERT INTO topics_fts(rowid, name, description) VALUES (new.id, new.name, new.description);
955
+ END
956
+ `);
957
+ db.exec(`
958
+ CREATE TRIGGER IF NOT EXISTS topics_fts_ad AFTER DELETE ON topics BEGIN
959
+ INSERT INTO topics_fts(topics_fts, rowid, name, description) VALUES('delete', old.id, old.name, old.description);
960
+ END
961
+ `);
962
+ db.exec(`
963
+ CREATE TRIGGER IF NOT EXISTS topics_fts_au AFTER UPDATE ON topics BEGIN
964
+ INSERT INTO topics_fts(topics_fts, rowid, name, description) VALUES('delete', old.id, old.name, old.description);
965
+ INSERT INTO topics_fts(rowid, name, description) VALUES (new.id, new.name, new.description);
966
+ END
967
+ `);
968
+ db.exec(`
969
+ CREATE TABLE IF NOT EXISTS indexer_watermarks (
970
+ agent_id TEXT PRIMARY KEY,
971
+ last_message_id INTEGER NOT NULL DEFAULT 0,
972
+ last_run_at TEXT NOT NULL
973
+ )
974
+ `);
975
+ db.prepare('INSERT INTO schema_version (version, applied_at) VALUES (?, ?)')
976
+ .run(11, nowIso());
977
+ }
978
+ // ── V12: FOS/MOD tables + builtin seed data ──────────────
979
+ // fleet_output_standard: fleet-wide output standards
980
+ // model_output_directives: per-model correction & calibration profiles
981
+ // output_metrics: per-request telemetry for drift analytics
982
+ if (currentVersion < 12) {
983
+ applyV12FosMod(db);
984
+ db.prepare('INSERT INTO schema_version (version, applied_at) VALUES (?, ?)')
985
+ .run(12, nowIso());
986
+ }
987
+ // ── V13: Temporal index ──────────────────────────────────────────────────
988
+ // Maps fact_id → occurred_at (unix ms). Initially backfilled from created_at
989
+ // (ingest time as proxy). Enables time-range retrieval for LoCoMo temporal
990
+ // questions without vector similarity.
991
+ if (currentVersion < 13) {
992
+ db.exec(`
993
+ CREATE TABLE IF NOT EXISTS temporal_index (
994
+ fact_id INTEGER PRIMARY KEY REFERENCES facts(id) ON DELETE CASCADE,
995
+ agent_id TEXT NOT NULL,
996
+ occurred_at INTEGER NOT NULL,
997
+ ingest_at INTEGER NOT NULL,
998
+ time_ref TEXT,
999
+ confidence REAL NOT NULL DEFAULT 0.5
1000
+ )
1001
+ `);
1002
+ db.exec('CREATE INDEX IF NOT EXISTS idx_temporal_agent_time ON temporal_index(agent_id, occurred_at DESC)');
1003
+ db.exec('CREATE INDEX IF NOT EXISTS idx_temporal_occurred ON temporal_index(occurred_at DESC)');
1004
+ // Backfill existing facts using created_at as occurred_at proxy
1005
+ db.exec(`
1006
+ INSERT OR IGNORE INTO temporal_index (fact_id, agent_id, occurred_at, ingest_at, confidence)
1007
+ SELECT
1008
+ id,
1009
+ agent_id,
1010
+ CAST((julianday(created_at) - 2440587.5) * 86400000 AS INTEGER),
1011
+ CAST((julianday(created_at) - 2440587.5) * 86400000 AS INTEGER),
1012
+ 0.5
1013
+ FROM facts
1014
+ WHERE superseded_by IS NULL
1015
+ `);
1016
+ db.prepare('INSERT INTO schema_version (version, applied_at) VALUES (?, ?)')
1017
+ .run(13, nowIso());
1018
+ }
1019
+ // Always ensure meta exists before stamping the running engine version.
1020
+ // Some legacy/stale DBs reached schema >=10 without the V10 migration having
1021
+ // actually created the table, which would make startup fail with
1022
+ // "no such table: meta" during an otherwise unrelated init path.
1023
+ db.exec(`
1024
+ CREATE TABLE IF NOT EXISTS meta (
1025
+ key TEXT PRIMARY KEY,
1026
+ value TEXT NOT NULL,
1027
+ updated_at TEXT NOT NULL
1028
+ )
1029
+ `);
1030
+ // Always stamp the running engine version so any query can surface it.
1031
+ if (engineVersion) {
1032
+ db.prepare(`
1033
+ INSERT INTO meta (key, value, updated_at) VALUES ('engine_version', ?, ?)
1034
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at
1035
+ `).run(engineVersion, nowIso());
1036
+ }
716
1037
  }
717
1038
  //# sourceMappingURL=library-schema.js.map
@@ -1,12 +1,12 @@
1
1
  /**
2
- * HyperMem Message Store
2
+ * hypermem Message Store
3
3
  *
4
4
  * CRUD operations for conversations and messages in SQLite.
5
5
  * All messages are stored in provider-neutral format.
6
6
  * This is the write-through layer: Redis → here.
7
7
  */
8
8
  import type { DatabaseSync } from 'node:sqlite';
9
- import type { NeutralMessage, StoredMessage, Conversation, ChannelType, ConversationStatus } from './types.js';
9
+ import type { NeutralMessage, StoredMessage, Conversation, ChannelType, ConversationStatus, RecentTurn } from './types.js';
10
10
  export declare class MessageStore {
11
11
  private readonly db;
12
12
  constructor(db: DatabaseSync);
@@ -52,6 +52,13 @@ export declare class MessageStore {
52
52
  * Get recent messages for a conversation.
53
53
  */
54
54
  getRecentMessages(conversationId: number, limit?: number): StoredMessage[];
55
+ /**
56
+ * Get recent messages scoped to a topic (P3.4, Option B).
57
+ * Returns messages matching the topic_id OR with topic_id IS NULL
58
+ * (legacy messages created before topic tracking was introduced).
59
+ * This is transition-safe: no legacy messages are silently dropped.
60
+ */
61
+ getRecentMessagesByTopic(conversationId: number, topicId: string, limit?: number): StoredMessage[];
55
62
  /**
56
63
  * Get messages across all conversations for an agent (cross-session query).
57
64
  */
@@ -64,6 +71,12 @@ export declare class MessageStore {
64
71
  * Full-text search across all messages for an agent.
65
72
  */
66
73
  searchMessages(agentId: string, query: string, limit?: number): StoredMessage[];
74
+ /**
75
+ * Get recent turns for a session, in chronological order, with tool calls stripped.
76
+ * Joins messages through conversations to find by session_key.
77
+ * Returns up to `n` turns (capped at 50).
78
+ */
79
+ getRecentTurns(sessionKey: string, n: number): RecentTurn[];
67
80
  /**
68
81
  * Get message count for a conversation.
69
82
  */
@@ -1 +1 @@
1
- {"version":3,"file":"message-store.d.ts","sourceRoot":"","sources":["../src/message-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,YAAY,EACZ,WAAW,EACX,kBAAkB,EACnB,MAAM,YAAY,CAAC;AA8CpB,qBAAa,YAAY;IACX,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,YAAY;IAI7C;;OAEG;IACH,uBAAuB,CACrB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,IAAI,CAAC,EAAE;QACL,WAAW,CAAC,EAAE,WAAW,CAAC;QAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,GACA,YAAY;IAgDf;;OAEG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAQxD;;OAEG;IACH,gBAAgB,CACd,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE;QACL,MAAM,CAAC,EAAE,kBAAkB,CAAC;QAC5B,WAAW,CAAC,EAAE,WAAW,CAAC;QAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,GACA,YAAY,EAAE;IAwBjB;;OAEG;IACH,kBAAkB,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE;QAClD,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,kBAAkB,CAAC;QAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,IAAI;IA2BR;;;OAGG;IACH,aAAa,CACX,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,cAAc,EACvB,IAAI,CAAC,EAAE;QACL,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,WAAW,CAAC,EAAE,OAAO,CAAC;KACvB,GACA,aAAa;IA+DhB;;OAEG;IACH,iBAAiB,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,aAAa,EAAE;IAY9E;;OAEG;IACH,gBAAgB,CACd,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE;QACL,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,iBAAiB,CAAC,EAAE,OAAO,CAAC;KAC7B,GACA,aAAa,EAAE;IAuBlB;;OAEG;IACH,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,aAAa,EAAE;IAmBnF;;OAEG;IACH,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM;IAS/C;;OAEG;IACH,OAAO,CAAC,gBAAgB;CASzB"}
1
+ {"version":3,"file":"message-store.d.ts","sourceRoot":"","sources":["../src/message-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,YAAY,EACZ,WAAW,EACX,kBAAkB,EAClB,UAAU,EACX,MAAM,YAAY,CAAC;AA8CpB,qBAAa,YAAY;IACX,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,YAAY;IAI7C;;OAEG;IACH,uBAAuB,CACrB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,IAAI,CAAC,EAAE;QACL,WAAW,CAAC,EAAE,WAAW,CAAC;QAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,GACA,YAAY;IAgDf;;OAEG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAQxD;;OAEG;IACH,gBAAgB,CACd,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE;QACL,MAAM,CAAC,EAAE,kBAAkB,CAAC;QAC5B,WAAW,CAAC,EAAE,WAAW,CAAC;QAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,GACA,YAAY,EAAE;IAwBjB;;OAEG;IACH,kBAAkB,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE;QAClD,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,kBAAkB,CAAC;QAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,IAAI;IA2BR;;;OAGG;IACH,aAAa,CACX,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,cAAc,EACvB,IAAI,CAAC,EAAE;QACL,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,WAAW,CAAC,EAAE,OAAO,CAAC;KACvB,GACA,aAAa;IA+DhB;;OAEG;IACH,iBAAiB,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,aAAa,EAAE;IAY9E;;;;;OAKG;IACH,wBAAwB,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,aAAa,EAAE;IAYtG;;OAEG;IACH,gBAAgB,CACd,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE;QACL,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,iBAAiB,CAAC,EAAE,OAAO,CAAC;KAC7B,GACA,aAAa,EAAE;IAuBlB;;OAEG;IACH,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,aAAa,EAAE;IAmBnF;;;;OAIG;IACH,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,UAAU,EAAE;IA+B3D;;OAEG;IACH,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM;IAS/C;;OAEG;IACH,OAAO,CAAC,gBAAgB;CASzB"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * HyperMem Message Store
2
+ * hypermem Message Store
3
3
  *
4
4
  * CRUD operations for conversations and messages in SQLite.
5
5
  * All messages are stored in provider-neutral format.
@@ -200,6 +200,22 @@ export class MessageStore {
200
200
  // Reverse to get chronological order
201
201
  return rows.reverse().map(parseMessageRow);
202
202
  }
203
+ /**
204
+ * Get recent messages scoped to a topic (P3.4, Option B).
205
+ * Returns messages matching the topic_id OR with topic_id IS NULL
206
+ * (legacy messages created before topic tracking was introduced).
207
+ * This is transition-safe: no legacy messages are silently dropped.
208
+ */
209
+ getRecentMessagesByTopic(conversationId, topicId, limit = 50) {
210
+ const rows = this.db.prepare(`
211
+ SELECT * FROM messages
212
+ WHERE conversation_id = ? AND (topic_id = ? OR topic_id IS NULL)
213
+ ORDER BY message_index DESC
214
+ LIMIT ?
215
+ `).all(conversationId, topicId, limit);
216
+ // Reverse to get chronological order
217
+ return rows.reverse().map(parseMessageRow);
218
+ }
203
219
  /**
204
220
  * Get messages across all conversations for an agent (cross-session query).
205
221
  */
@@ -241,6 +257,40 @@ export class MessageStore {
241
257
  `).all(query, limit);
242
258
  return rows.map(parseMessageRow);
243
259
  }
260
+ /**
261
+ * Get recent turns for a session, in chronological order, with tool calls stripped.
262
+ * Joins messages through conversations to find by session_key.
263
+ * Returns up to `n` turns (capped at 50).
264
+ */
265
+ getRecentTurns(sessionKey, n) {
266
+ const limit = Math.min(n, 50);
267
+ try {
268
+ const rows = this.db.prepare(`
269
+ SELECT m.role, m.text_content, m.created_at, m.message_index
270
+ FROM messages m
271
+ JOIN conversations c ON m.conversation_id = c.id
272
+ WHERE c.session_key = ?
273
+ AND m.role IN ('user', 'assistant')
274
+ ORDER BY m.message_index DESC
275
+ LIMIT ?
276
+ `).all(sessionKey, limit);
277
+ // Reverse to chronological order
278
+ rows.reverse();
279
+ return rows.map(row => ({
280
+ role: row.role,
281
+ // text_content only — tool calls are stored separately and excluded here
282
+ content: row.text_content ?? '',
283
+ timestamp: row.created_at
284
+ ? new Date(row.created_at).getTime()
285
+ : Date.now(),
286
+ seq: row.message_index,
287
+ }));
288
+ }
289
+ catch (err) {
290
+ console.warn('[hypermem:message-store] getRecentTurns failed:', err.message);
291
+ return [];
292
+ }
293
+ }
244
294
  /**
245
295
  * Get message count for a conversation.
246
296
  */
@@ -0,0 +1,114 @@
1
+ /**
2
+ * hypermem Metrics Dashboard
3
+ *
4
+ * Provides a unified surface for observing system health:
5
+ * - Memory counts (facts, pages, episodes, vectors)
6
+ * - Composition performance (avg assembly time, budget utilization)
7
+ * - Ingestion stats (indexer throughput, promotion rate)
8
+ * - Embedding stats (cache hit rate, Ollama availability)
9
+ *
10
+ * All queries are read-only and safe to call on hot DBs.
11
+ */
12
+ import type { DatabaseSync } from 'node:sqlite';
13
+ export interface FactMetrics {
14
+ /** Total facts indexed across all agents */
15
+ totalFacts: number;
16
+ /** Facts per agent breakdown */
17
+ byAgent: Record<string, number>;
18
+ /** Facts added in the last 24h */
19
+ recentFacts: number;
20
+ }
21
+ export interface WikiMetrics {
22
+ /** Total synthesized wiki pages (non-superseded) */
23
+ totalPages: number;
24
+ /** Pages per agent */
25
+ byAgent: Record<string, number>;
26
+ /** Pages synthesized in the last 24h */
27
+ recentPages: number;
28
+ /** Oldest page age in hours (staleness indicator) */
29
+ oldestPageAgeHours: number | null;
30
+ }
31
+ export interface EpisodeMetrics {
32
+ /** Total episodes stored */
33
+ totalEpisodes: number;
34
+ /** Episodes per agent */
35
+ byAgent: Record<string, number>;
36
+ /** Average episode significance score (0-1) */
37
+ avgSignificance: number | null;
38
+ }
39
+ export interface VectorMetrics {
40
+ /** Total vectors indexed */
41
+ totalVectors: number;
42
+ /** Breakdown by source table */
43
+ byTable: Record<string, number>;
44
+ /** Embedding cache hit rate (0-1) for this process lifetime */
45
+ cacheHitRate: number | null;
46
+ }
47
+ export interface CompositionMetrics {
48
+ /** Average assembly time in ms (from output_metrics table) */
49
+ avgAssemblyMs: number | null;
50
+ /** p95 assembly time in ms */
51
+ p95AssemblyMs: number | null;
52
+ /** Average output tokens per turn */
53
+ avgOutputTokens: number | null;
54
+ /** Average input tokens per turn (context size) */
55
+ avgInputTokens: number | null;
56
+ /** Number of turns recorded */
57
+ totalTurns: number;
58
+ /** Average cache read tokens (Anthropic prompt cache utilization) */
59
+ avgCacheReadTokens: number | null;
60
+ }
61
+ export interface IngestionMetrics {
62
+ /** Total messages processed by the background indexer */
63
+ totalMessagesIndexed: number;
64
+ /** Total facts extracted */
65
+ totalFactsExtracted: number;
66
+ /** Noise rejection rate (1 - facts/messages, approximate) */
67
+ noiseRejectionRate: number | null;
68
+ /** Total episodes created */
69
+ totalEpisodesCreated: number;
70
+ /** Total knowledge items promoted by dreaming promoter */
71
+ totalKnowledgePromoted: number;
72
+ }
73
+ export interface SystemHealth {
74
+ /** Whether the main DB is readable */
75
+ mainDbOk: boolean;
76
+ /** Whether the library DB is readable */
77
+ libraryDbOk: boolean;
78
+ /** Main DB schema version */
79
+ mainSchemaVersion: number | null;
80
+ /** Library DB schema version */
81
+ librarySchemaVersion: number | null;
82
+ /** hypermem package version */
83
+ packageVersion: string;
84
+ /** Cache connection status (if provided) */
85
+ cacheOk: boolean | null;
86
+ /** Timestamp of this snapshot */
87
+ snapshotAt: string;
88
+ }
89
+ export interface HyperMemMetrics {
90
+ facts: FactMetrics;
91
+ wiki: WikiMetrics;
92
+ episodes: EpisodeMetrics;
93
+ vectors: VectorMetrics;
94
+ composition: CompositionMetrics;
95
+ ingestion: IngestionMetrics;
96
+ health: SystemHealth;
97
+ }
98
+ export interface MetricsDashboardOptions {
99
+ /** Agent IDs to scope to. If omitted, returns fleet-wide metrics. */
100
+ agentIds?: string[];
101
+ /** Include per-agent breakdowns. Default: true */
102
+ includeBreakdowns?: boolean;
103
+ }
104
+ /**
105
+ * Collect all metrics in a single pass.
106
+ * Safe to call on live DBs — all queries are read-only.
107
+ */
108
+ export declare function collectMetrics(mainDb: DatabaseSync, libraryDb: DatabaseSync, opts?: MetricsDashboardOptions): Promise<HyperMemMetrics>;
109
+ /**
110
+ * Format metrics as a human-readable summary string.
111
+ * Suitable for logging or status replies.
112
+ */
113
+ export declare function formatMetricsSummary(m: HyperMemMetrics): string;
114
+ //# sourceMappingURL=metrics-dashboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics-dashboard.d.ts","sourceRoot":"","sources":["../src/metrics-dashboard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAiB,MAAM,aAAa,CAAC;AAE/D,MAAM,WAAW,WAAW;IAC1B,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,gCAAgC;IAChC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB;IACtB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,wCAAwC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,cAAc;IAC7B,4BAA4B;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,yBAAyB;IACzB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,+CAA+C;IAC/C,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,aAAa;IAC5B,4BAA4B;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,gCAAgC;IAChC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,+DAA+D;IAC/D,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAkB;IACjC,8DAA8D;IAC9D,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,8BAA8B;IAC9B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,qCAAqC;IACrC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,mDAAmD;IACnD,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,+BAA+B;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,qEAAqE;IACrE,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,gBAAgB;IAC/B,yDAAyD;IACzD,oBAAoB,EAAE,MAAM,CAAC;IAC7B,4BAA4B;IAC5B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,6DAA6D;IAC7D,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,6BAA6B;IAC7B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,0DAA0D;IAC1D,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,YAAY;IAC3B,sCAAsC;IACtC,QAAQ,EAAE,OAAO,CAAC;IAClB,yCAAyC;IACzC,WAAW,EAAE,OAAO,CAAC;IACrB,6BAA6B;IAC7B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,gCAAgC;IAChC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,+BAA+B;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACxB,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,WAAW,CAAC;IACnB,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE,aAAa,CAAC;IACvB,WAAW,EAAE,kBAAkB,CAAC;IAChC,SAAS,EAAE,gBAAgB,CAAC;IAC5B,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,MAAM,WAAW,uBAAuB;IACtC,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,kDAAkD;IAClD,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AA2TD;;;GAGG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,YAAY,EACpB,SAAS,EAAE,YAAY,EACvB,IAAI,GAAE,uBAA4B,GACjC,OAAO,CAAC,eAAe,CAAC,CAU1B;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,eAAe,GAAG,MAAM,CA6C/D"}