tsunami-memory 1.0.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.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +501 -0
  3. package/README.zh-CN.md +485 -0
  4. package/package.json +46 -0
  5. package/server/api.ts +125 -0
  6. package/server/mcp.ts +221 -0
  7. package/src/bun_memory_store.ts +340 -0
  8. package/src/classifier_keywords.ts +115 -0
  9. package/src/core/project_state.ts +88 -0
  10. package/src/index.ts +54 -0
  11. package/src/legacy_compat/tsunami_compat.ts +22 -0
  12. package/src/legacy_compat/tsunami_legacy_identity.ts +13 -0
  13. package/src/legacy_compat/tsunami_legacy_taxonomy.ts +197 -0
  14. package/src/memory_audit.ts +32 -0
  15. package/src/memory_conflict_resolver.ts +14 -0
  16. package/src/memory_fabric.ts +31 -0
  17. package/src/memory_manager.ts +10 -0
  18. package/src/memory_promotion.ts +22 -0
  19. package/src/memory_recovery.ts +14 -0
  20. package/src/memory_runtime.ts +7 -0
  21. package/src/migration.ts +163 -0
  22. package/src/provider.ts +68 -0
  23. package/src/runtime/checkpoints/durable_recovery.ts +24 -0
  24. package/src/runtime/paths.ts +11 -0
  25. package/src/storm/basins.ts +57 -0
  26. package/src/storm/boundary.ts +52 -0
  27. package/src/storm/budget.ts +42 -0
  28. package/src/storm/center.ts +396 -0
  29. package/src/storm/confidence.ts +88 -0
  30. package/src/storm/coverage.ts +44 -0
  31. package/src/storm/directive.ts +94 -0
  32. package/src/storm/gate.ts +43 -0
  33. package/src/storm/helpers.ts +172 -0
  34. package/src/storm/horizon.ts +52 -0
  35. package/src/storm/intake.ts +80 -0
  36. package/src/storm/mode.ts +21 -0
  37. package/src/storm/pressure.ts +56 -0
  38. package/src/storm/readiness.ts +29 -0
  39. package/src/storm/saturation.ts +45 -0
  40. package/src/storm/selection.ts +49 -0
  41. package/src/storm/signals.ts +105 -0
  42. package/src/storm/types.ts +216 -0
  43. package/src/tsunami_bun_backend.ts +705 -0
  44. package/src/tsunami_chinese_dialect.ts +19 -0
  45. package/src/tsunami_classifier.ts +137 -0
  46. package/src/tsunami_client.ts +710 -0
  47. package/src/tsunami_execution_gate.ts +232 -0
  48. package/src/tsunami_graph_runtime.ts +359 -0
  49. package/src/tsunami_identity.ts +35 -0
  50. package/src/tsunami_routing.ts +169 -0
  51. package/src/tsunami_runtime_graph_sync.ts +17 -0
  52. package/src/tsunami_schema.ts +403 -0
  53. package/src/tsunami_storage_paths.ts +8 -0
  54. package/src/tsunami_storm_center.ts +53 -0
@@ -0,0 +1,19 @@
1
+ /**
2
+ * TSUNAMI Chinese dialect utilities
3
+ *
4
+ * Provides Chinese text detection and AAAK (adaptive abbreviation amplification knowledge) indexing.
5
+ * These utilities help the memory system handle mixed-language content correctly.
6
+ */
7
+
8
+ export function isChineseHeavyText(text: string): boolean {
9
+ const chineseChars = (text.match(/[\u4e00-\u9fa5]/g) ?? []).length;
10
+ return chineseChars > text.length * 0.3;
11
+ }
12
+
13
+ export function buildChineseIndexedContent(text: string, _scope: Record<string, unknown>): string {
14
+ return text;
15
+ }
16
+
17
+ export function getTsunamiAaakSpec(): Record<string, unknown> {
18
+ return { version: '1.0.0', description: 'TSUNAMI AAAK indexing spec' };
19
+ }
@@ -0,0 +1,137 @@
1
+ /**
2
+ * TSUNAMI Memory Classifier — keyword-based text classification
3
+ *
4
+ * Maps memory content to wing/room/basin for automatic routing.
5
+ * Keyword data lives in classifier_keywords.ts for maintainability.
6
+ */
7
+
8
+ import { WING_KEYWORDS, ROOM_KEYWORDS } from './classifier_keywords';
9
+ import type { KeywordPair } from './classifier_keywords';
10
+
11
+ export type { KeywordPair };
12
+
13
+ export type StructuredClassification = {
14
+ wing: string;
15
+ room: string;
16
+ basin: string;
17
+ current: string;
18
+ confidence: number;
19
+ };
20
+
21
+ /** Score a set of keywords against a text, returning matched weight total and max possible. */
22
+ function scoreKeywords(text: string, keywords: KeywordPair[]): { score: number; hits: number } {
23
+ const lower = text.toLowerCase();
24
+ let score = 0;
25
+ let hits = 0;
26
+ for (const [kw, weight] of keywords) {
27
+ if (lower.includes(kw.toLowerCase())) {
28
+ score += weight;
29
+ hits += 1;
30
+ }
31
+ }
32
+ return { score, hits };
33
+ }
34
+
35
+ /**
36
+ * Normalize a raw keyword score into a [0, 1] confidence value.
37
+ * Uses a sigmoid-ish curve: confidence = 1 - 1/(1 + score/FACTOR)
38
+ * where FACTOR is the expected "strong match" total score threshold.
39
+ */
40
+ function normalizeConfidence(rawScore: number): number {
41
+ const FACTOR = 10; // calibrated so score=10 → ~0.5, score=25 → ~0.71, score=40 → ~0.8
42
+ const confidence = 1 - 1 / (1 + rawScore / FACTOR);
43
+ return Math.min(1.0, Math.round(confidence * 100) / 100);
44
+ }
45
+
46
+ const MIN_WING_SCORE = 1.5; // minimum score to classify — catches room-cascaded weak signals
47
+
48
+ /** Classify text into a single wing/room pair. Returns null if no wing exceeds minimum threshold. */
49
+ export function classifyMemory(text: string): StructuredClassification | null {
50
+ const lower = text.toLowerCase();
51
+
52
+ // Find best wing — cascade room keywords into wing scoring
53
+ // so terms that only appear at the room level still contribute
54
+ let bestWing = '';
55
+ let bestScore = 0;
56
+ for (const [wing, keywords] of Object.entries(WING_KEYWORDS)) {
57
+ let { score } = scoreKeywords(lower, keywords);
58
+ // Boost wing score from room-level matches within this wing
59
+ const rooms = ROOM_KEYWORDS[wing];
60
+ if (rooms) {
61
+ for (const [, roomKws] of Object.entries(rooms)) {
62
+ score += scoreKeywords(lower, roomKws).score * 0.6; // room hits are weaker wing signals
63
+ }
64
+ }
65
+ if (score > bestScore) { bestWing = wing; bestScore = score; }
66
+ }
67
+ if (!bestWing || bestScore < MIN_WING_SCORE) return null;
68
+
69
+ // Find best room within that wing
70
+ const rooms = ROOM_KEYWORDS[bestWing];
71
+ let bestRoom = 'general';
72
+ let bestRoomScore = 0;
73
+ if (rooms) {
74
+ for (const [room, keywords] of Object.entries(rooms)) {
75
+ const { score } = scoreKeywords(lower, keywords);
76
+ if (score > bestRoomScore) { bestRoom = room; bestRoomScore = score; }
77
+ }
78
+ }
79
+
80
+ return {
81
+ wing: bestWing,
82
+ room: bestRoom,
83
+ basin: bestWing,
84
+ current: bestRoom,
85
+ confidence: normalizeConfidence(bestScore),
86
+ };
87
+ }
88
+
89
+ /** classifyTsunamiText — simpler API returning basin/current/confidence. */
90
+ export function classifyTsunamiText(text: string): {
91
+ basin: string; current: string; confidence: number;
92
+ } {
93
+ const result = classifyMemory(text);
94
+ if (!result) return { basin: 'surface', current: 'surface/bridge', confidence: 0.2 };
95
+ return { basin: result.basin, current: result.current, confidence: result.confidence };
96
+ }
97
+
98
+ /** classifyTsunamiTextMulti — top-N classifications with proper room matching per wing. */
99
+ export function classifyTsunamiTextMulti(
100
+ text: string,
101
+ top = 3,
102
+ ): Array<{ basin: string; current: string; confidence: number }> {
103
+ const lower = text.toLowerCase();
104
+
105
+ // Score each wing independently, cascading room keywords in
106
+ const wingScores: Array<{ wing: string; score: number }> = [];
107
+ for (const [wing, keywords] of Object.entries(WING_KEYWORDS)) {
108
+ let { score } = scoreKeywords(lower, keywords);
109
+ const rooms = ROOM_KEYWORDS[wing];
110
+ if (rooms) {
111
+ for (const [, roomKws] of Object.entries(rooms)) {
112
+ score += scoreKeywords(lower, roomKws).score * 0.6;
113
+ }
114
+ }
115
+ if (score > 0) wingScores.push({ wing, score });
116
+ }
117
+ wingScores.sort((a, b) => b.score - a.score);
118
+
119
+ return wingScores.slice(0, Math.max(1, top)).map((entry) => {
120
+ // Find best room within this wing
121
+ const rooms = ROOM_KEYWORDS[entry.wing];
122
+ let bestRoom = entry.wing;
123
+ let bestRoomScore = 0;
124
+ if (rooms) {
125
+ for (const [room, keywords] of Object.entries(rooms)) {
126
+ const { score } = scoreKeywords(lower, keywords);
127
+ if (score > bestRoomScore) { bestRoom = room; bestRoomScore = score; }
128
+ }
129
+ }
130
+ const current = bestRoomScore > 0 ? bestRoom : entry.wing;
131
+ return {
132
+ basin: entry.wing,
133
+ current,
134
+ confidence: normalizeConfidence(entry.score),
135
+ };
136
+ });
137
+ }