anno-extension 0.1.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 +7 -0
- package/README.md +30 -0
- package/THIRD_PARTY_NOTICES.md +8 -0
- package/dist/browser-fs.d.ts +14 -0
- package/dist/browser-fs.d.ts.map +1 -0
- package/dist/browser-fs.js +69 -0
- package/dist/browser-fs.js.map +1 -0
- package/dist/config.d.ts +81 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +82 -0
- package/dist/config.js.map +1 -0
- package/dist/consolidate-v2.d.ts +15 -0
- package/dist/consolidate-v2.d.ts.map +1 -0
- package/dist/consolidate-v2.js +125 -0
- package/dist/consolidate-v2.js.map +1 -0
- package/dist/consolidation.d.ts +12 -0
- package/dist/consolidation.d.ts.map +1 -0
- package/dist/consolidation.js +199 -0
- package/dist/consolidation.js.map +1 -0
- package/dist/dedup.d.ts +18 -0
- package/dist/dedup.d.ts.map +1 -0
- package/dist/dedup.js +85 -0
- package/dist/dedup.js.map +1 -0
- package/dist/diagnostics.d.ts +24 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +53 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/embedding-index.d.ts +27 -0
- package/dist/embedding-index.d.ts.map +1 -0
- package/dist/embedding-index.js +111 -0
- package/dist/embedding-index.js.map +1 -0
- package/dist/engine-archive.d.ts +37 -0
- package/dist/engine-archive.d.ts.map +1 -0
- package/dist/engine-archive.js +155 -0
- package/dist/engine-archive.js.map +1 -0
- package/dist/engine-episode-sync.d.ts +19 -0
- package/dist/engine-episode-sync.d.ts.map +1 -0
- package/dist/engine-episode-sync.js +186 -0
- package/dist/engine-episode-sync.js.map +1 -0
- package/dist/engine-injection-text.d.ts +46 -0
- package/dist/engine-injection-text.d.ts.map +1 -0
- package/dist/engine-injection-text.js +271 -0
- package/dist/engine-injection-text.js.map +1 -0
- package/dist/engine-insights.d.ts +22 -0
- package/dist/engine-insights.d.ts.map +1 -0
- package/dist/engine-insights.js +127 -0
- package/dist/engine-insights.js.map +1 -0
- package/dist/engine-links.d.ts +43 -0
- package/dist/engine-links.d.ts.map +1 -0
- package/dist/engine-links.js +223 -0
- package/dist/engine-links.js.map +1 -0
- package/dist/engine-recall-select.d.ts +84 -0
- package/dist/engine-recall-select.d.ts.map +1 -0
- package/dist/engine-recall-select.js +127 -0
- package/dist/engine-recall-select.js.map +1 -0
- package/dist/engine-reinforce.d.ts +18 -0
- package/dist/engine-reinforce.d.ts.map +1 -0
- package/dist/engine-reinforce.js +116 -0
- package/dist/engine-reinforce.js.map +1 -0
- package/dist/engine-scoring-v2.d.ts +35 -0
- package/dist/engine-scoring-v2.d.ts.map +1 -0
- package/dist/engine-scoring-v2.js +178 -0
- package/dist/engine-scoring-v2.js.map +1 -0
- package/dist/engine-v2-mapping.d.ts +17 -0
- package/dist/engine-v2-mapping.d.ts.map +1 -0
- package/dist/engine-v2-mapping.js +190 -0
- package/dist/engine-v2-mapping.js.map +1 -0
- package/dist/engine.d.ts +339 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +1964 -0
- package/dist/engine.js.map +1 -0
- package/dist/eviction.d.ts +14 -0
- package/dist/eviction.d.ts.map +1 -0
- package/dist/eviction.js +26 -0
- package/dist/eviction.js.map +1 -0
- package/dist/extraction.d.ts +11 -0
- package/dist/extraction.d.ts.map +1 -0
- package/dist/extraction.js +261 -0
- package/dist/extraction.js.map +1 -0
- package/dist/full-insights-html.d.ts +9 -0
- package/dist/full-insights-html.d.ts.map +1 -0
- package/dist/full-insights-html.js +422 -0
- package/dist/full-insights-html.js.map +1 -0
- package/dist/full-insights-sections.d.ts +48 -0
- package/dist/full-insights-sections.d.ts.map +1 -0
- package/dist/full-insights-sections.js +231 -0
- package/dist/full-insights-sections.js.map +1 -0
- package/dist/full-insights.d.ts +22 -0
- package/dist/full-insights.d.ts.map +1 -0
- package/dist/full-insights.js +370 -0
- package/dist/full-insights.js.map +1 -0
- package/dist/hash-embedding.d.ts +9 -0
- package/dist/hash-embedding.d.ts.map +1 -0
- package/dist/hash-embedding.js +44 -0
- package/dist/hash-embedding.js.map +1 -0
- package/dist/human-insights.d.ts +28 -0
- package/dist/human-insights.d.ts.map +1 -0
- package/dist/human-insights.js +260 -0
- package/dist/human-insights.js.map +1 -0
- package/dist/i18n.d.ts +64 -0
- package/dist/i18n.d.ts.map +1 -0
- package/dist/i18n.js +367 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/insights-html.d.ts +9 -0
- package/dist/insights-html.d.ts.map +1 -0
- package/dist/insights-html.js +432 -0
- package/dist/insights-html.js.map +1 -0
- package/dist/linking.d.ts +22 -0
- package/dist/linking.d.ts.map +1 -0
- package/dist/linking.js +192 -0
- package/dist/linking.js.map +1 -0
- package/dist/llm-json.d.ts +10 -0
- package/dist/llm-json.d.ts.map +1 -0
- package/dist/llm-json.js +193 -0
- package/dist/llm-json.js.map +1 -0
- package/dist/privacy.d.ts +17 -0
- package/dist/privacy.d.ts.map +1 -0
- package/dist/privacy.js +53 -0
- package/dist/privacy.js.map +1 -0
- package/dist/procedural-v2.d.ts +9 -0
- package/dist/procedural-v2.d.ts.map +1 -0
- package/dist/procedural-v2.js +123 -0
- package/dist/procedural-v2.js.map +1 -0
- package/dist/reconsolidate-v2.d.ts +19 -0
- package/dist/reconsolidate-v2.d.ts.map +1 -0
- package/dist/reconsolidate-v2.js +115 -0
- package/dist/reconsolidate-v2.js.map +1 -0
- package/dist/scoring.d.ts +32 -0
- package/dist/scoring.d.ts.map +1 -0
- package/dist/scoring.js +108 -0
- package/dist/scoring.js.map +1 -0
- package/dist/store-v2.d.ts +35 -0
- package/dist/store-v2.d.ts.map +1 -0
- package/dist/store-v2.js +98 -0
- package/dist/store-v2.js.map +1 -0
- package/dist/store.d.ts +22 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +116 -0
- package/dist/store.js.map +1 -0
- package/dist/turn-context.d.ts +37 -0
- package/dist/turn-context.d.ts.map +1 -0
- package/dist/turn-context.js +31 -0
- package/dist/turn-context.js.map +1 -0
- package/dist/types-v2.d.ts +190 -0
- package/dist/types-v2.d.ts.map +1 -0
- package/dist/types-v2.js +8 -0
- package/dist/types-v2.js.map +1 -0
- package/dist/types.d.ts +330 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/update.d.ts +31 -0
- package/dist/update.d.ts.map +1 -0
- package/dist/update.js +250 -0
- package/dist/update.js.map +1 -0
- package/package.json +34 -0
- package/src/browser-fs.ts +67 -0
- package/src/config.ts +138 -0
- package/src/consolidate-v2.ts +158 -0
- package/src/consolidation.ts +229 -0
- package/src/dedup.ts +91 -0
- package/src/diagnostics.ts +81 -0
- package/src/embedding-index.ts +138 -0
- package/src/engine-archive.ts +167 -0
- package/src/engine-episode-sync.ts +237 -0
- package/src/engine-injection-text.ts +334 -0
- package/src/engine-insights.ts +182 -0
- package/src/engine-links.ts +311 -0
- package/src/engine-recall-select.ts +298 -0
- package/src/engine-reinforce.ts +154 -0
- package/src/engine-scoring-v2.ts +188 -0
- package/src/engine-v2-mapping.ts +204 -0
- package/src/engine.ts +2499 -0
- package/src/eviction.ts +38 -0
- package/src/extraction.ts +300 -0
- package/src/full-insights-html.ts +474 -0
- package/src/full-insights-sections.ts +305 -0
- package/src/full-insights.ts +423 -0
- package/src/hash-embedding.ts +48 -0
- package/src/human-insights.ts +345 -0
- package/src/i18n.ts +455 -0
- package/src/index.ts +117 -0
- package/src/insights-html.ts +478 -0
- package/src/linking.ts +228 -0
- package/src/llm-json.ts +194 -0
- package/src/privacy.ts +54 -0
- package/src/procedural-v2.ts +142 -0
- package/src/reconsolidate-v2.ts +145 -0
- package/src/scoring.ts +156 -0
- package/src/store-v2.ts +138 -0
- package/src/store.ts +137 -0
- package/src/turn-context.ts +56 -0
- package/src/types-v2.ts +244 -0
- package/src/types.ts +372 -0
- package/src/update.ts +271 -0
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [WHO]: FullInsightsReport, generateFullInsights
|
|
3
|
+
* [FROM]: Depends on ./i18n.js, ./full-insights-sections.js, ./types.js
|
|
4
|
+
* [TO]: Consumed by packages/mem-core/src/index.ts
|
|
5
|
+
* [HERE]: packages/mem-core/src/full-insights.ts - aggregation + optional LLM for comprehensive insights report
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
import { PROMPTS } from "./i18n.js";
|
|
10
|
+
import type {
|
|
11
|
+
Episode,
|
|
12
|
+
FullInsightsAtAGlance,
|
|
13
|
+
FullInsightsChart,
|
|
14
|
+
FullInsightsFriction,
|
|
15
|
+
FullInsightsProjectArea,
|
|
16
|
+
FullInsightsReport,
|
|
17
|
+
FullInsightsFeatureToTry,
|
|
18
|
+
FullInsightsUsagePattern,
|
|
19
|
+
FullInsightsWin,
|
|
20
|
+
LlmFn,
|
|
21
|
+
MemoryEntry,
|
|
22
|
+
PatternInsight,
|
|
23
|
+
StruggleInsight,
|
|
24
|
+
WorkEntry,
|
|
25
|
+
} from "./types.js";
|
|
26
|
+
import { generateParallelFullInsightSections } from "./full-insights-sections.js";
|
|
27
|
+
|
|
28
|
+
const EXT_TO_LANG: Record<string, string> = {
|
|
29
|
+
ts: "TypeScript",
|
|
30
|
+
js: "JavaScript",
|
|
31
|
+
jsx: "JSX",
|
|
32
|
+
tsx: "TSX",
|
|
33
|
+
md: "Markdown",
|
|
34
|
+
json: "JSON",
|
|
35
|
+
py: "Python",
|
|
36
|
+
html: "HTML",
|
|
37
|
+
css: "CSS",
|
|
38
|
+
yml: "YAML",
|
|
39
|
+
yaml: "YAML",
|
|
40
|
+
sh: "Shell",
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export interface ExportAllResult {
|
|
44
|
+
knowledge: MemoryEntry[];
|
|
45
|
+
lessons: MemoryEntry[];
|
|
46
|
+
preferences: MemoryEntry[];
|
|
47
|
+
facets: MemoryEntry[];
|
|
48
|
+
work: WorkEntry[];
|
|
49
|
+
episodes: Episode[];
|
|
50
|
+
meta: { totalSessions: number; lastConsolidation?: string; version: number };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function aggregateTools(episodes: Episode[], topN = 10): FullInsightsChart {
|
|
54
|
+
const counts: Record<string, number> = {};
|
|
55
|
+
for (const ep of episodes) {
|
|
56
|
+
for (const [name, n] of Object.entries(ep.toolsUsed ?? {})) {
|
|
57
|
+
counts[name] = (counts[name] ?? 0) + n;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const rows = Object.entries(counts)
|
|
61
|
+
.map(([label, value]) => ({ label, value }))
|
|
62
|
+
.sort((a, b) => b.value - a.value)
|
|
63
|
+
.slice(0, topN);
|
|
64
|
+
return { id: "tools", title: "tools", rows };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function aggregateLanguages(episodes: Episode[], topN = 8): FullInsightsChart {
|
|
68
|
+
const counts: Record<string, number> = {};
|
|
69
|
+
for (const ep of episodes) {
|
|
70
|
+
for (const path of ep.filesModified ?? []) {
|
|
71
|
+
const ext = path.includes(".") ? path.split(".").pop()?.toLowerCase() ?? "other" : "other";
|
|
72
|
+
const label = EXT_TO_LANG[ext] ?? ext;
|
|
73
|
+
counts[label] = (counts[label] ?? 0) + 1;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const rows = Object.entries(counts)
|
|
77
|
+
.filter(([label]) => label !== "other" || (counts[label] ?? 0) > 0)
|
|
78
|
+
.map(([label, value]) => ({ label, value }))
|
|
79
|
+
.sort((a, b) => b.value - a.value)
|
|
80
|
+
.slice(0, topN);
|
|
81
|
+
return { id: "languages", title: "languages", rows };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function aggregateErrors(episodes: Episode[], topN = 8): FullInsightsChart {
|
|
85
|
+
const counts: Record<string, number> = {};
|
|
86
|
+
for (const ep of episodes) {
|
|
87
|
+
for (const err of ep.errors ?? []) {
|
|
88
|
+
const key = err.slice(0, 50).trim();
|
|
89
|
+
counts[key] = (counts[key] ?? 0) + 1;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const rows = Object.entries(counts)
|
|
93
|
+
.map(([label, value]) => ({ label, value }))
|
|
94
|
+
.sort((a, b) => b.value - a.value)
|
|
95
|
+
.slice(0, topN);
|
|
96
|
+
return { id: "errors", title: "errors", rows };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function buildProjectAreas(episodes: Episode[], work: WorkEntry[]): FullInsightsProjectArea[] {
|
|
100
|
+
const byProject = new Map<string, { count: number; summaries: string[]; goals: string[] }>();
|
|
101
|
+
for (const ep of episodes) {
|
|
102
|
+
const p = ep.project || "default";
|
|
103
|
+
if (!byProject.has(p)) byProject.set(p, { count: 0, summaries: [], goals: [] });
|
|
104
|
+
const entry = byProject.get(p)!;
|
|
105
|
+
entry.count++;
|
|
106
|
+
if (ep.summary) entry.summaries.push(ep.summary);
|
|
107
|
+
if (ep.userGoal) entry.goals.push(ep.userGoal);
|
|
108
|
+
}
|
|
109
|
+
for (const w of work) {
|
|
110
|
+
const p = w.project || "default";
|
|
111
|
+
if (!byProject.has(p)) byProject.set(p, { count: 0, summaries: [], goals: [] });
|
|
112
|
+
const entry = byProject.get(p)!;
|
|
113
|
+
if (w.summary) entry.summaries.push(w.summary);
|
|
114
|
+
if (w.goal) entry.goals.push(w.goal);
|
|
115
|
+
}
|
|
116
|
+
return [...byProject.entries()]
|
|
117
|
+
.filter(([, v]) => v.count >= 1 || v.summaries.length + v.goals.length > 0)
|
|
118
|
+
.map(([name, v]) => ({
|
|
119
|
+
name,
|
|
120
|
+
sessionCount: v.count,
|
|
121
|
+
description: [v.summaries[0], v.goals[0]].filter(Boolean).join(" ").slice(0, 120) || "",
|
|
122
|
+
}))
|
|
123
|
+
.sort((a, b) => b.sessionCount - a.sessionCount)
|
|
124
|
+
.slice(0, 12);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function buildPatternsAndStruggles(facets: MemoryEntry[]): { patterns: PatternInsight[]; struggles: StruggleInsight[] } {
|
|
128
|
+
const patternEntries = facets.filter((e) => e.type === "pattern");
|
|
129
|
+
const struggleEntries = facets.filter((e) => e.type === "struggle");
|
|
130
|
+
const calcWeight = (e: MemoryEntry, unresolvedBonus = false): number => {
|
|
131
|
+
const base = (e.accessCount + 1) * (e.importance / 10);
|
|
132
|
+
return unresolvedBonus ? base * 1.5 : base;
|
|
133
|
+
};
|
|
134
|
+
const patterns: PatternInsight[] = patternEntries
|
|
135
|
+
.map((e) => ({
|
|
136
|
+
entry: e,
|
|
137
|
+
weight: calcWeight(e),
|
|
138
|
+
trigger: e.facetData?.kind === "pattern" ? e.facetData.trigger : (e.summary || e.detail || e.content || "").slice(0, 50),
|
|
139
|
+
behavior: e.facetData?.kind === "pattern" ? e.facetData.behavior : (e.summary || e.detail || e.content || ""),
|
|
140
|
+
}))
|
|
141
|
+
.sort((a, b) => b.weight - a.weight);
|
|
142
|
+
const struggles: StruggleInsight[] = struggleEntries
|
|
143
|
+
.map((e) => {
|
|
144
|
+
const isResolved = e.facetData?.kind === "struggle" ? !!e.facetData.solution : false;
|
|
145
|
+
return {
|
|
146
|
+
entry: e,
|
|
147
|
+
weight: calcWeight(e, !isResolved),
|
|
148
|
+
problem: e.facetData?.kind === "struggle" ? e.facetData.problem : (e.summary || e.detail || e.content || ""),
|
|
149
|
+
attempts: e.facetData?.kind === "struggle" ? e.facetData.attempts : [],
|
|
150
|
+
solution: e.facetData?.kind === "struggle" ? e.facetData.solution : "",
|
|
151
|
+
resolved: isResolved,
|
|
152
|
+
};
|
|
153
|
+
})
|
|
154
|
+
.sort((a, b) => (a.resolved !== b.resolved ? (a.resolved ? 1 : -1) : b.weight - a.weight));
|
|
155
|
+
return { patterns, struggles };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function fallbackAtAGlance(
|
|
159
|
+
patterns: PatternInsight[],
|
|
160
|
+
struggles: StruggleInsight[],
|
|
161
|
+
lessons: MemoryEntry[],
|
|
162
|
+
locale: string,
|
|
163
|
+
): FullInsightsAtAGlance {
|
|
164
|
+
const resolved = struggles.filter((s) => s.resolved);
|
|
165
|
+
const unresolved = struggles.filter((s) => !s.resolved);
|
|
166
|
+
const working =
|
|
167
|
+
resolved.length > 0
|
|
168
|
+
? `${resolved.length} struggles resolved; keep reusing those fixes.`
|
|
169
|
+
: lessons.length > 0
|
|
170
|
+
? `${lessons.length} lessons captured; turn the top ones into checklists.`
|
|
171
|
+
: "No strong signal yet.";
|
|
172
|
+
const hindering =
|
|
173
|
+
unresolved.length > 0
|
|
174
|
+
? `${unresolved.length} open struggles remain. Most frequent: "${unresolved[0]?.problem ?? ""}".`
|
|
175
|
+
: "No unresolved struggles visible.";
|
|
176
|
+
const topPattern = patterns[0];
|
|
177
|
+
const quickWins = topPattern
|
|
178
|
+
? `When ${topPattern.trigger}, you often ${topPattern.behavior}. Consider automating.`
|
|
179
|
+
: "Keep using the system; quick wins will appear as data grows.";
|
|
180
|
+
const ambitious = "Consider turning repeat workflows into skills or scripts and using parallel agents.";
|
|
181
|
+
return { working, hindering, quickWins, ambitious };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function fallbackWins(struggles: StruggleInsight[], lessons: MemoryEntry[]): FullInsightsWin[] {
|
|
185
|
+
const wins: FullInsightsWin[] = [];
|
|
186
|
+
for (const s of struggles.filter((x) => x.resolved).slice(0, 5)) {
|
|
187
|
+
wins.push({ title: s.problem.slice(0, 60), description: s.solution || s.problem });
|
|
188
|
+
}
|
|
189
|
+
const sortLessons = [...lessons].sort((a, b) => b.importance * (b.accessCount + 1) - a.importance * (a.accessCount + 1));
|
|
190
|
+
for (const l of sortLessons.slice(0, 3)) {
|
|
191
|
+
const text = l.summary || l.detail || l.content || "";
|
|
192
|
+
wins.push({ title: text.slice(0, 60), description: text });
|
|
193
|
+
}
|
|
194
|
+
return wins.slice(0, 8);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function fallbackFrictions(struggles: StruggleInsight[]): FullInsightsFriction[] {
|
|
198
|
+
return struggles
|
|
199
|
+
.filter((s) => !s.resolved)
|
|
200
|
+
.slice(0, 8)
|
|
201
|
+
.map((s) => ({
|
|
202
|
+
title: s.problem.slice(0, 60),
|
|
203
|
+
description: s.problem,
|
|
204
|
+
examples: s.attempts.length ? s.attempts : undefined,
|
|
205
|
+
}));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function fallbackRecommendations(
|
|
209
|
+
patterns: PatternInsight[],
|
|
210
|
+
struggles: StruggleInsight[],
|
|
211
|
+
lessons: MemoryEntry[],
|
|
212
|
+
locale: string,
|
|
213
|
+
): string[] {
|
|
214
|
+
const recs: string[] = [];
|
|
215
|
+
if (patterns.length > 0) {
|
|
216
|
+
const top = patterns[0]!;
|
|
217
|
+
recs.push(`You consistently ${top.behavior} when ${top.trigger}. Consider automating.`);
|
|
218
|
+
}
|
|
219
|
+
const unresolved = struggles.filter((s) => !s.resolved);
|
|
220
|
+
if (unresolved.length >= 2) {
|
|
221
|
+
recs.push(`You have ${unresolved.length} unresolved issues. Tackle them systematically.`);
|
|
222
|
+
}
|
|
223
|
+
if (lessons.length >= 5) {
|
|
224
|
+
recs.push(`You've accumulated ${lessons.length} lessons. Valuable expertise.`);
|
|
225
|
+
}
|
|
226
|
+
if (recs.length === 0) {
|
|
227
|
+
recs.push("Keep using the system to let it learn your habits.");
|
|
228
|
+
}
|
|
229
|
+
return recs.slice(0, 5);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function fallbackFeaturesAndPatterns(
|
|
233
|
+
toolRows: { label: string; value: number }[],
|
|
234
|
+
struggles: StruggleInsight[],
|
|
235
|
+
locale: string,
|
|
236
|
+
): { featuresToTry: FullInsightsFeatureToTry[]; usagePatterns: FullInsightsUsagePattern[] } {
|
|
237
|
+
const featuresToTry: FullInsightsFeatureToTry[] = [];
|
|
238
|
+
const usagePatterns: FullInsightsUsagePattern[] = [];
|
|
239
|
+
if (toolRows.length > 0) {
|
|
240
|
+
const topTool = toolRows[0]!.label;
|
|
241
|
+
featuresToTry.push({
|
|
242
|
+
title: "Automate frequent operations",
|
|
243
|
+
oneLiner: "Turn repeat workflows into skills or scripts",
|
|
244
|
+
whyForYou: `You use "${topTool}" often; consider wrapping it in a skill or command.`,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
if (struggles.filter((s) => !s.resolved).length >= 2) {
|
|
248
|
+
usagePatterns.push({
|
|
249
|
+
title: "Batch large tasks",
|
|
250
|
+
summary: "Split large files or batches into smaller chunks",
|
|
251
|
+
detail: "Avoid context overflow by validating each batch before continuing.",
|
|
252
|
+
pastePrompt: "Break this into 3-4 steps; show me the result after each step before continuing.",
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
return { featuresToTry, usagePatterns };
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
interface ObservedReportLanguage {
|
|
259
|
+
code: string;
|
|
260
|
+
name: string;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function entryText(entry: MemoryEntry): string {
|
|
264
|
+
return [entry.name, entry.summary, entry.detail, entry.content, entry.tags.join(" ")].filter(Boolean).join("\n");
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function languageFromExplicitPreference(text: string): ObservedReportLanguage | undefined {
|
|
268
|
+
if (/(中文|汉语|Chinese|Mandarin|用中文|中文输出|中文交流)/i.test(text)) return { code: "zh", name: "Chinese" };
|
|
269
|
+
if (/(英文|英语|English|use English|write in English)/i.test(text)) return { code: "en", name: "English" };
|
|
270
|
+
if (/(日文|日语|日本語|Japanese)/i.test(text)) return { code: "ja", name: "Japanese" };
|
|
271
|
+
if (/(韩文|韩语|한국어|Korean)/i.test(text)) return { code: "ko", name: "Korean" };
|
|
272
|
+
if (/(法文|法语|français|French)/i.test(text)) return { code: "fr", name: "French" };
|
|
273
|
+
if (/(西班牙文|西班牙语|español|Spanish)/i.test(text)) return { code: "es", name: "Spanish" };
|
|
274
|
+
if (/(德文|德语|Deutsch|German)/i.test(text)) return { code: "de", name: "German" };
|
|
275
|
+
return undefined;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function languageFromScript(text: string): ObservedReportLanguage | undefined {
|
|
279
|
+
const han = (text.match(/\p{Script=Han}/gu) ?? []).length;
|
|
280
|
+
const kana = (text.match(/[\p{Script=Hiragana}\p{Script=Katakana}]/gu) ?? []).length;
|
|
281
|
+
const hangul = (text.match(/\p{Script=Hangul}/gu) ?? []).length;
|
|
282
|
+
const cyrillic = (text.match(/\p{Script=Cyrillic}/gu) ?? []).length;
|
|
283
|
+
const arabic = (text.match(/\p{Script=Arabic}/gu) ?? []).length;
|
|
284
|
+
const latinWords = (text.match(/[A-Za-z]{3,}/g) ?? []).length;
|
|
285
|
+
if (kana >= 4) return { code: "ja", name: "Japanese" };
|
|
286
|
+
if (hangul >= 4) return { code: "ko", name: "Korean" };
|
|
287
|
+
if (han >= 8 && han >= latinWords) return { code: "zh", name: "Chinese" };
|
|
288
|
+
if (cyrillic >= 8) return { code: "ru", name: "Russian" };
|
|
289
|
+
if (arabic >= 8) return { code: "ar", name: "Arabic" };
|
|
290
|
+
if (latinWords >= 20 && han + kana + hangul + cyrillic + arabic === 0) return { code: "en", name: "English" };
|
|
291
|
+
return undefined;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function fallbackLanguage(locale: string): ObservedReportLanguage {
|
|
295
|
+
if (locale === "zh") return { code: "zh", name: "Chinese" };
|
|
296
|
+
if (locale === "en") return { code: "en", name: "English" };
|
|
297
|
+
return { code: locale, name: locale };
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function inferObservedReportLanguage(all: ExportAllResult, locale: string): ObservedReportLanguage {
|
|
301
|
+
const preferenceText = all.preferences.map(entryText).join("\n");
|
|
302
|
+
const explicit = languageFromExplicitPreference(preferenceText);
|
|
303
|
+
if (explicit) return explicit;
|
|
304
|
+
const observedText = [
|
|
305
|
+
preferenceText,
|
|
306
|
+
...all.episodes.flatMap((episode) => [
|
|
307
|
+
episode.summary,
|
|
308
|
+
episode.userGoal ?? "",
|
|
309
|
+
...episode.keyObservations,
|
|
310
|
+
...episode.tags,
|
|
311
|
+
]),
|
|
312
|
+
...all.work.flatMap((work) => [work.goal, work.summary, work.detail ?? "", ...work.tags]),
|
|
313
|
+
...all.lessons.map(entryText),
|
|
314
|
+
...all.knowledge.map(entryText),
|
|
315
|
+
...all.facets.map(entryText),
|
|
316
|
+
].join("\n");
|
|
317
|
+
return languageFromScript(observedText) ?? fallbackLanguage(locale);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export async function buildFullInsightsReport(
|
|
321
|
+
all: ExportAllResult,
|
|
322
|
+
llmFn: LlmFn | undefined,
|
|
323
|
+
locale: string,
|
|
324
|
+
): Promise<FullInsightsReport> {
|
|
325
|
+
const reportLanguage = inferObservedReportLanguage(all, locale);
|
|
326
|
+
const reportLocale = reportLanguage.code;
|
|
327
|
+
const p = PROMPTS[reportLocale] ?? PROMPTS.en;
|
|
328
|
+
const { patterns, struggles } = buildPatternsAndStruggles(all.facets);
|
|
329
|
+
const sortLessons = [...all.lessons].sort(
|
|
330
|
+
(a, b) => b.importance * (b.accessCount + 1) - a.importance * (a.accessCount + 1),
|
|
331
|
+
);
|
|
332
|
+
const topLessons = sortLessons.slice(0, 10);
|
|
333
|
+
|
|
334
|
+
const charts: FullInsightsChart[] = [];
|
|
335
|
+
const toolsChart = aggregateTools(all.episodes);
|
|
336
|
+
let aggregateToolCount = 0;
|
|
337
|
+
for (const r of toolsChart.rows) aggregateToolCount += r.value;
|
|
338
|
+
if (toolsChart.rows.length > 0) charts.push({ ...toolsChart, title: p.fullInsightsChartTools });
|
|
339
|
+
|
|
340
|
+
const langChart = aggregateLanguages(all.episodes);
|
|
341
|
+
let aggregateFileCount = 0;
|
|
342
|
+
for (const r of langChart.rows) aggregateFileCount += r.value;
|
|
343
|
+
if (langChart.rows.length > 0) charts.push({ ...langChart, title: p.fullInsightsChartLanguages });
|
|
344
|
+
|
|
345
|
+
const errChart = aggregateErrors(all.episodes);
|
|
346
|
+
if (errChart.rows.length > 0) charts.push({ ...errChart, title: p.fullInsightsChartErrors });
|
|
347
|
+
|
|
348
|
+
const projectAreas = buildProjectAreas(all.episodes, all.work);
|
|
349
|
+
|
|
350
|
+
const stats = {
|
|
351
|
+
knowledge: all.knowledge.length,
|
|
352
|
+
lessons: all.lessons.length,
|
|
353
|
+
preferences: all.preferences.length,
|
|
354
|
+
facets: all.facets.length,
|
|
355
|
+
episodes: all.episodes.length,
|
|
356
|
+
work: all.work.length,
|
|
357
|
+
totalSessions: all.meta.totalSessions,
|
|
358
|
+
aggregateToolCount,
|
|
359
|
+
aggregateFileCount,
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
let atAGlance = fallbackAtAGlance(patterns, struggles, topLessons, reportLocale);
|
|
363
|
+
let projectAreaDescriptions: string[] = projectAreas.map((a) => a.description);
|
|
364
|
+
let wins = fallbackWins(struggles, topLessons);
|
|
365
|
+
let frictions = fallbackFrictions(struggles);
|
|
366
|
+
let recommendations = fallbackRecommendations(patterns, struggles, topLessons, reportLocale);
|
|
367
|
+
let { featuresToTry, usagePatterns } = fallbackFeaturesAndPatterns(
|
|
368
|
+
toolsChart.rows,
|
|
369
|
+
struggles,
|
|
370
|
+
reportLocale,
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
if (llmFn) {
|
|
374
|
+
try {
|
|
375
|
+
const payload = await generateParallelFullInsightSections(
|
|
376
|
+
{
|
|
377
|
+
locale: reportLocale,
|
|
378
|
+
outputLanguage: reportLanguage.name,
|
|
379
|
+
stats,
|
|
380
|
+
patterns,
|
|
381
|
+
struggles,
|
|
382
|
+
lessons: topLessons,
|
|
383
|
+
projectAreas,
|
|
384
|
+
topTools: toolsChart.rows,
|
|
385
|
+
topLanguages: langChart.rows,
|
|
386
|
+
topErrors: errChart.rows,
|
|
387
|
+
},
|
|
388
|
+
llmFn,
|
|
389
|
+
);
|
|
390
|
+
if (payload.atAGlance) atAGlance = payload.atAGlance;
|
|
391
|
+
if (Array.isArray(payload.projectAreaDescriptions) && payload.projectAreaDescriptions.length >= projectAreas.length) {
|
|
392
|
+
projectAreaDescriptions = payload.projectAreaDescriptions.slice(0, projectAreas.length);
|
|
393
|
+
}
|
|
394
|
+
if (Array.isArray(payload.wins) && payload.wins.length > 0) wins = payload.wins.slice(0, 8);
|
|
395
|
+
if (Array.isArray(payload.frictions) && payload.frictions.length > 0) frictions = payload.frictions.slice(0, 8);
|
|
396
|
+
if (Array.isArray(payload.recommendations) && payload.recommendations.length > 0) recommendations = payload.recommendations.slice(0, 5);
|
|
397
|
+
if (Array.isArray(payload.featuresToTry) && payload.featuresToTry.length > 0) featuresToTry = payload.featuresToTry.slice(0, 6);
|
|
398
|
+
if (Array.isArray(payload.usagePatterns) && payload.usagePatterns.length > 0) usagePatterns = payload.usagePatterns.slice(0, 6);
|
|
399
|
+
} catch {
|
|
400
|
+
// keep fallbacks
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const projectAreasWithDesc: FullInsightsProjectArea[] = projectAreas.map((a, i) => ({
|
|
405
|
+
...a,
|
|
406
|
+
description: projectAreaDescriptions[i] ?? a.description,
|
|
407
|
+
}));
|
|
408
|
+
|
|
409
|
+
return {
|
|
410
|
+
stats,
|
|
411
|
+
atAGlance,
|
|
412
|
+
projectAreas: projectAreasWithDesc,
|
|
413
|
+
charts,
|
|
414
|
+
wins,
|
|
415
|
+
frictions,
|
|
416
|
+
patterns,
|
|
417
|
+
recommendations,
|
|
418
|
+
featuresToTry,
|
|
419
|
+
usagePatterns,
|
|
420
|
+
generatedAt: new Date().toISOString(),
|
|
421
|
+
locale: reportLocale,
|
|
422
|
+
};
|
|
423
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [WHO]: createHashedEmbeddingFn
|
|
3
|
+
* [FROM]: Depends on ./types-v2.js
|
|
4
|
+
* [TO]: Consumed by packages/mem-core/src/index.ts
|
|
5
|
+
* [HERE]: packages/mem-core/src/hash-embedding.ts - built-in local hashing embedding for zero-dependency semantic recall
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { EmbeddingFn } from "./types-v2.js";
|
|
9
|
+
|
|
10
|
+
function hashToken(token: string): number {
|
|
11
|
+
let h = 2166136261;
|
|
12
|
+
for (let i = 0; i < token.length; i++) {
|
|
13
|
+
h ^= token.charCodeAt(i);
|
|
14
|
+
h = Math.imul(h, 16777619);
|
|
15
|
+
}
|
|
16
|
+
return h >>> 0;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function tokenize(text: string): string[] {
|
|
20
|
+
return text
|
|
21
|
+
.toLowerCase()
|
|
22
|
+
.match(/[a-z0-9\u4e00-\u9fff_.-]{2,}/g)
|
|
23
|
+
?.slice(0, 512) ?? [];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function normalize(vec: number[]): number[] {
|
|
27
|
+
let mag = 0;
|
|
28
|
+
for (const value of vec) mag += value * value;
|
|
29
|
+
if (!mag) return vec;
|
|
30
|
+
const denom = Math.sqrt(mag);
|
|
31
|
+
return vec.map((value) => value / denom);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function createHashedEmbeddingFn(dim = 256): EmbeddingFn {
|
|
35
|
+
return async (texts: string[]) =>
|
|
36
|
+
texts.map((text) => {
|
|
37
|
+
const vec = new Array<number>(dim).fill(0);
|
|
38
|
+
const tokens = tokenize(text);
|
|
39
|
+
for (const token of tokens) {
|
|
40
|
+
const h = hashToken(token);
|
|
41
|
+
const idx = h % dim;
|
|
42
|
+
const sign = (h & 1) === 0 ? 1 : -1;
|
|
43
|
+
const weight = token.length > 6 ? 1.25 : 1;
|
|
44
|
+
vec[idx] += sign * weight;
|
|
45
|
+
}
|
|
46
|
+
return normalize(vec);
|
|
47
|
+
});
|
|
48
|
+
}
|