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.
- package/LICENSE +21 -0
- package/README.md +501 -0
- package/README.zh-CN.md +485 -0
- package/package.json +46 -0
- package/server/api.ts +125 -0
- package/server/mcp.ts +221 -0
- package/src/bun_memory_store.ts +340 -0
- package/src/classifier_keywords.ts +115 -0
- package/src/core/project_state.ts +88 -0
- package/src/index.ts +54 -0
- package/src/legacy_compat/tsunami_compat.ts +22 -0
- package/src/legacy_compat/tsunami_legacy_identity.ts +13 -0
- package/src/legacy_compat/tsunami_legacy_taxonomy.ts +197 -0
- package/src/memory_audit.ts +32 -0
- package/src/memory_conflict_resolver.ts +14 -0
- package/src/memory_fabric.ts +31 -0
- package/src/memory_manager.ts +10 -0
- package/src/memory_promotion.ts +22 -0
- package/src/memory_recovery.ts +14 -0
- package/src/memory_runtime.ts +7 -0
- package/src/migration.ts +163 -0
- package/src/provider.ts +68 -0
- package/src/runtime/checkpoints/durable_recovery.ts +24 -0
- package/src/runtime/paths.ts +11 -0
- package/src/storm/basins.ts +57 -0
- package/src/storm/boundary.ts +52 -0
- package/src/storm/budget.ts +42 -0
- package/src/storm/center.ts +396 -0
- package/src/storm/confidence.ts +88 -0
- package/src/storm/coverage.ts +44 -0
- package/src/storm/directive.ts +94 -0
- package/src/storm/gate.ts +43 -0
- package/src/storm/helpers.ts +172 -0
- package/src/storm/horizon.ts +52 -0
- package/src/storm/intake.ts +80 -0
- package/src/storm/mode.ts +21 -0
- package/src/storm/pressure.ts +56 -0
- package/src/storm/readiness.ts +29 -0
- package/src/storm/saturation.ts +45 -0
- package/src/storm/selection.ts +49 -0
- package/src/storm/signals.ts +105 -0
- package/src/storm/types.ts +216 -0
- package/src/tsunami_bun_backend.ts +705 -0
- package/src/tsunami_chinese_dialect.ts +19 -0
- package/src/tsunami_classifier.ts +137 -0
- package/src/tsunami_client.ts +710 -0
- package/src/tsunami_execution_gate.ts +232 -0
- package/src/tsunami_graph_runtime.ts +359 -0
- package/src/tsunami_identity.ts +35 -0
- package/src/tsunami_routing.ts +169 -0
- package/src/tsunami_runtime_graph_sync.ts +17 -0
- package/src/tsunami_schema.ts +403 -0
- package/src/tsunami_storage_paths.ts +8 -0
- 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
|
+
}
|