@geravant/sinain 1.0.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 (53) hide show
  1. package/README.md +183 -0
  2. package/index.ts +2096 -0
  3. package/install.js +155 -0
  4. package/openclaw.plugin.json +59 -0
  5. package/package.json +21 -0
  6. package/sinain-memory/common.py +403 -0
  7. package/sinain-memory/demo_knowledge_transfer.sh +85 -0
  8. package/sinain-memory/embedder.py +268 -0
  9. package/sinain-memory/eval/__init__.py +0 -0
  10. package/sinain-memory/eval/assertions.py +288 -0
  11. package/sinain-memory/eval/judges/__init__.py +0 -0
  12. package/sinain-memory/eval/judges/base_judge.py +61 -0
  13. package/sinain-memory/eval/judges/curation_judge.py +46 -0
  14. package/sinain-memory/eval/judges/insight_judge.py +48 -0
  15. package/sinain-memory/eval/judges/mining_judge.py +42 -0
  16. package/sinain-memory/eval/judges/signal_judge.py +45 -0
  17. package/sinain-memory/eval/schemas.py +247 -0
  18. package/sinain-memory/eval_delta.py +109 -0
  19. package/sinain-memory/eval_reporter.py +642 -0
  20. package/sinain-memory/feedback_analyzer.py +221 -0
  21. package/sinain-memory/git_backup.sh +19 -0
  22. package/sinain-memory/insight_synthesizer.py +181 -0
  23. package/sinain-memory/memory/2026-03-01.md +11 -0
  24. package/sinain-memory/memory/playbook-archive/sinain-playbook-2026-03-01-1418.md +15 -0
  25. package/sinain-memory/memory/playbook-logs/2026-03-01.jsonl +1 -0
  26. package/sinain-memory/memory/sinain-playbook.md +21 -0
  27. package/sinain-memory/memory-config.json +39 -0
  28. package/sinain-memory/memory_miner.py +183 -0
  29. package/sinain-memory/module_manager.py +695 -0
  30. package/sinain-memory/playbook_curator.py +225 -0
  31. package/sinain-memory/requirements.txt +3 -0
  32. package/sinain-memory/signal_analyzer.py +141 -0
  33. package/sinain-memory/test_local.py +402 -0
  34. package/sinain-memory/tests/__init__.py +0 -0
  35. package/sinain-memory/tests/conftest.py +189 -0
  36. package/sinain-memory/tests/test_curator_helpers.py +94 -0
  37. package/sinain-memory/tests/test_embedder.py +210 -0
  38. package/sinain-memory/tests/test_extract_json.py +124 -0
  39. package/sinain-memory/tests/test_feedback_computation.py +121 -0
  40. package/sinain-memory/tests/test_miner_helpers.py +71 -0
  41. package/sinain-memory/tests/test_module_management.py +458 -0
  42. package/sinain-memory/tests/test_parsers.py +96 -0
  43. package/sinain-memory/tests/test_tick_evaluator.py +430 -0
  44. package/sinain-memory/tests/test_triple_extractor.py +255 -0
  45. package/sinain-memory/tests/test_triple_ingest.py +191 -0
  46. package/sinain-memory/tests/test_triple_migrate.py +138 -0
  47. package/sinain-memory/tests/test_triplestore.py +248 -0
  48. package/sinain-memory/tick_evaluator.py +392 -0
  49. package/sinain-memory/triple_extractor.py +402 -0
  50. package/sinain-memory/triple_ingest.py +290 -0
  51. package/sinain-memory/triple_migrate.py +275 -0
  52. package/sinain-memory/triple_query.py +184 -0
  53. package/sinain-memory/triplestore.py +498 -0
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env python3
2
+ """Phase 3 Step 1 (idle): Memory Miner — deep-mine daily memory files.
3
+
4
+ Reads mining index from playbook to find unmined files, reads 2 daily memory
5
+ files + devmatrix-summary.md, uses LLM to find patterns and cross-references.
6
+ Updates the mining index in the playbook.
7
+
8
+ Usage:
9
+ python3 memory_miner.py --memory-dir memory/
10
+ """
11
+
12
+ import argparse
13
+ import json
14
+ import re
15
+ import sys
16
+ from datetime import datetime, timedelta, timezone
17
+ from pathlib import Path
18
+
19
+ from common import (
20
+ LLMError,
21
+ call_llm,
22
+ extract_json,
23
+ list_daily_memory_files,
24
+ output_json,
25
+ parse_mining_index,
26
+ read_effective_playbook,
27
+ read_file_safe,
28
+ read_playbook,
29
+ )
30
+
31
+ SYSTEM_PROMPT = """\
32
+ You are a memory mining agent for a personal AI assistant (sinain).
33
+ Your job: read daily memory files and extract patterns, preferences, and insights
34
+ that should be added to the evolving playbook.
35
+
36
+ You receive daily memory files (markdown with session notes, decisions, research)
37
+ and the current playbook. Cross-reference to find:
38
+
39
+ 1. Patterns that appear across multiple days but aren't in the playbook
40
+ 2. User preferences (tools, workflows, topics) that are consistent
41
+ 3. Multi-day trends: recurring errors, evolving interests, productivity rhythms
42
+ 4. Contradictions: daily notes that conflict with playbook entries
43
+ 5. Architectural decisions or technical insights worth preserving
44
+
45
+ Respond with ONLY a JSON object:
46
+ {
47
+ "findings": "2-3 sentence summary of what was discovered",
48
+ "newPatterns": ["pattern description", ...],
49
+ "contradictions": ["playbook entry X contradicts observation Y", ...],
50
+ "preferences": ["user preference observed", ...]
51
+ }"""
52
+
53
+
54
+ def get_unmined_files(memory_dir: str, mined_dates: list[str]) -> list[str]:
55
+ """Find daily memory files not yet mined (not in index)."""
56
+ all_files = list_daily_memory_files(memory_dir)
57
+ unmined = []
58
+ for f in all_files:
59
+ # Extract date from filename (YYYY-MM-DD.md)
60
+ stem = Path(f).stem # "2026-02-17"
61
+ if stem not in mined_dates:
62
+ unmined.append(f)
63
+ return unmined
64
+
65
+
66
+ def update_mining_index(memory_dir: str, playbook: str, new_dates: list[str]) -> None:
67
+ """Update mining-index comment in playbook, removing dates older than 7 days."""
68
+ current_index = parse_mining_index(playbook)
69
+ cutoff = (datetime.now(timezone.utc) - timedelta(days=7)).strftime("%Y-%m-%d")
70
+
71
+ # Merge and filter
72
+ all_dates = set(current_index + new_dates)
73
+ valid_dates = sorted([d for d in all_dates if d >= cutoff], reverse=True)
74
+ new_index_str = ",".join(valid_dates)
75
+ new_comment = f"<!-- mining-index: {new_index_str} -->"
76
+
77
+ # Replace or insert
78
+ playbook_path = Path(memory_dir) / "sinain-playbook.md"
79
+ if not playbook_path.exists():
80
+ playbook_path.write_text(new_comment + "\n", encoding="utf-8")
81
+ return
82
+
83
+ text = playbook_path.read_text(encoding="utf-8")
84
+ if re.search(r"<!--\s*mining-index:", text):
85
+ text = re.sub(r"<!--\s*mining-index:\s*[^>]*-->", new_comment, text)
86
+ else:
87
+ text = new_comment + "\n" + text
88
+
89
+ playbook_path.write_text(text, encoding="utf-8")
90
+
91
+
92
+ def main():
93
+ parser = argparse.ArgumentParser(description="Phase 3: Memory mining (idle)")
94
+ parser.add_argument("--memory-dir", required=True, help="Path to memory/ directory")
95
+ args = parser.parse_args()
96
+
97
+ raw_playbook = read_playbook(args.memory_dir)
98
+ playbook = read_effective_playbook(args.memory_dir)
99
+ mined_dates = parse_mining_index(raw_playbook)
100
+ unmined = get_unmined_files(args.memory_dir, mined_dates)
101
+
102
+ if not unmined:
103
+ output_json({
104
+ "findings": "All daily memory files have been mined",
105
+ "newPatterns": [],
106
+ "minedSources": [],
107
+ })
108
+ return
109
+
110
+ # Pick up to 3 unmined files
111
+ to_mine = unmined[:3]
112
+ mined_contents = {}
113
+ for f in to_mine:
114
+ content = read_file_safe(f)
115
+ if content:
116
+ mined_contents[Path(f).name] = content
117
+
118
+ if not mined_contents:
119
+ output_json({
120
+ "findings": "Selected daily files were empty",
121
+ "newPatterns": [],
122
+ "minedSources": [Path(f).name for f in to_mine],
123
+ })
124
+ return
125
+
126
+ # Also read devmatrix-summary.md for broader context
127
+ devmatrix = read_file_safe(str(Path(args.memory_dir) / "devmatrix-summary.md"))
128
+
129
+ # Build LLM prompt
130
+ parts = [f"## Current Playbook\n{playbook}"]
131
+ for name, content in mined_contents.items():
132
+ # Truncate very large files
133
+ if len(content) > 6000:
134
+ content = content[:6000] + "\n... [truncated]"
135
+ parts.append(f"## Daily Memory: {name}\n{content}")
136
+ if devmatrix:
137
+ if len(devmatrix) > 3000:
138
+ devmatrix = devmatrix[:3000] + "\n... [truncated]"
139
+ parts.append(f"## DevMatrix Summary\n{devmatrix}")
140
+
141
+ # Inject graph context if triple store available
142
+ try:
143
+ from triple_query import get_related_concepts
144
+ keywords = [Path(f).stem for f in to_mine]
145
+ graph_ctx = get_related_concepts(args.memory_dir, keywords)
146
+ if graph_ctx:
147
+ parts.append(f"## Knowledge Graph Context\n{graph_ctx}")
148
+ except ImportError:
149
+ pass
150
+
151
+ user_prompt = "\n\n".join(parts)
152
+
153
+ llm_ok = False
154
+ try:
155
+ raw = call_llm(SYSTEM_PROMPT, user_prompt, script="memory_miner", json_mode=True)
156
+ result = extract_json(raw)
157
+ llm_ok = True
158
+ except (ValueError, LLMError) as e:
159
+ print(f"[warn] {e}", file=sys.stderr)
160
+ result = {
161
+ "findings": "Mining completed but LLM response was not parseable",
162
+ "newPatterns": [],
163
+ }
164
+
165
+ # Only mark files as mined on successful parse — failed files will be retried
166
+ new_dates = [Path(f).stem for f in to_mine]
167
+ if llm_ok:
168
+ update_mining_index(args.memory_dir, raw_playbook, new_dates)
169
+ print(f"[info] Updated mining index with {new_dates}", file=sys.stderr)
170
+ else:
171
+ print(f"[info] Skipped mining index update (LLM failed) — {new_dates} will be retried", file=sys.stderr)
172
+
173
+ output_json({
174
+ "findings": result.get("findings", ""),
175
+ "newPatterns": result.get("newPatterns", []),
176
+ "contradictions": result.get("contradictions", []),
177
+ "preferences": result.get("preferences", []),
178
+ "minedSources": [Path(f).name for f in to_mine],
179
+ })
180
+
181
+
182
+ if __name__ == "__main__":
183
+ main()