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,345 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [WHO]: DeveloperPersona, HumanInsightsReport, generateHumanInsights
|
|
3
|
+
* [FROM]: Depends on ./llm-json.js, ./types.js
|
|
4
|
+
* [TO]: Consumed by packages/mem-core/src/index.ts
|
|
5
|
+
* [HERE]: packages/mem-core/src/human-insights.ts - LLM-powered usage review generation with developer persona analysis
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
import type {
|
|
10
|
+
DeveloperPersona,
|
|
11
|
+
EnhancedInsightsReport,
|
|
12
|
+
HumanInsight,
|
|
13
|
+
LlmFn,
|
|
14
|
+
MemoryEntry,
|
|
15
|
+
Episode,
|
|
16
|
+
RootCauseInsight,
|
|
17
|
+
WorkEntry,
|
|
18
|
+
} from "./types.js";
|
|
19
|
+
import { parseLlmJson } from "./llm-json.js";
|
|
20
|
+
|
|
21
|
+
interface ExportAllResult {
|
|
22
|
+
knowledge: MemoryEntry[];
|
|
23
|
+
lessons: MemoryEntry[];
|
|
24
|
+
preferences: MemoryEntry[];
|
|
25
|
+
facets: MemoryEntry[];
|
|
26
|
+
work: WorkEntry[];
|
|
27
|
+
episodes: Episode[];
|
|
28
|
+
meta: { totalSessions: number; lastConsolidation?: string; version: number };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const HUMAN_INSIGHTS_SYSTEM_PROMPT = `You are an elite AI product analyst and developer workflow coach.
|
|
32
|
+
|
|
33
|
+
You are reviewing one specific user's real usage history over time.
|
|
34
|
+
Write like a warm, observant expert who deeply understands how experienced AI users actually work.
|
|
35
|
+
|
|
36
|
+
Goals:
|
|
37
|
+
- Sound human, perceptive, and respectful rather than robotic or generic
|
|
38
|
+
- Give clear corrections when the user's habits are inefficient
|
|
39
|
+
- Back every major conclusion with concrete evidence from the supplied data
|
|
40
|
+
- Prefer precise language, plain English, and practical recommendations
|
|
41
|
+
- Explain what the user is doing well, where they are losing time, and what they should change next
|
|
42
|
+
|
|
43
|
+
Output requirements:
|
|
44
|
+
- Output ONLY valid JSON
|
|
45
|
+
- Do not use markdown or code fences
|
|
46
|
+
- Use the supplied data only
|
|
47
|
+
- Be specific, not motivational fluff
|
|
48
|
+
- Each insight should feel like part of a thoughtful performance review
|
|
49
|
+
- Recommendations should be direct, concrete, and easy to act on
|
|
50
|
+
- Evidence should reference counts, repeated behaviors, or recurring issues when possible
|
|
51
|
+
- If locale is "zh", write the JSON string values in Simplified Chinese; otherwise write in English
|
|
52
|
+
|
|
53
|
+
Return JSON matching this schema:
|
|
54
|
+
{
|
|
55
|
+
"persona": {
|
|
56
|
+
"whatTheyDo": "1-2 sentences",
|
|
57
|
+
"experienceLevel": "1 sentence",
|
|
58
|
+
"superpowers": ["...", "..."],
|
|
59
|
+
"painPoints": ["...", "..."],
|
|
60
|
+
"workStyle": "1-2 sentences",
|
|
61
|
+
"summary": "1 sentence"
|
|
62
|
+
},
|
|
63
|
+
"insights": [
|
|
64
|
+
{
|
|
65
|
+
"title": "short title",
|
|
66
|
+
"content": "3-5 sentences combining observation, evidence, correction, and advice",
|
|
67
|
+
"icon": "emoji",
|
|
68
|
+
"utility": "high|medium|low",
|
|
69
|
+
"tags": ["tag1", "tag2"]
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
"rootCauses": [
|
|
73
|
+
{
|
|
74
|
+
"symptom": "what keeps happening",
|
|
75
|
+
"rootCause": "why it likely happens",
|
|
76
|
+
"evidence": ["fact 1", "fact 2"],
|
|
77
|
+
"suggestion": "what to change next"
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
}`.trim();
|
|
81
|
+
|
|
82
|
+
function summarizeCounts(rows: Array<[string, number]>, formatter: (label: string, value: number) => string): string[] {
|
|
83
|
+
return rows.map(([label, value]) => formatter(label, value));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function buildHumanInsightsData(all: ExportAllResult) {
|
|
87
|
+
const totalToolUses = all.episodes.reduce(
|
|
88
|
+
(total, episode) =>
|
|
89
|
+
total +
|
|
90
|
+
Object.values(episode.toolsUsed ?? {}).reduce<number>((sum, count) => sum + count, 0),
|
|
91
|
+
0,
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const toolCounts = all.episodes.reduce(
|
|
95
|
+
(acc, episode) => {
|
|
96
|
+
for (const [tool, count] of Object.entries(episode.toolsUsed ?? {})) {
|
|
97
|
+
acc[tool] = (acc[tool] ?? 0) + count;
|
|
98
|
+
}
|
|
99
|
+
return acc;
|
|
100
|
+
},
|
|
101
|
+
{} as Record<string, number>,
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
const languageCounts = all.episodes.reduce(
|
|
105
|
+
(acc, episode) => {
|
|
106
|
+
for (const file of episode.filesModified ?? []) {
|
|
107
|
+
const ext = file.includes(".") ? file.split(".").pop()?.toLowerCase() ?? "other" : "other";
|
|
108
|
+
acc[ext] = (acc[ext] ?? 0) + 1;
|
|
109
|
+
}
|
|
110
|
+
return acc;
|
|
111
|
+
},
|
|
112
|
+
{} as Record<string, number>,
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
const errorCounts = all.episodes.reduce(
|
|
116
|
+
(acc, episode) => {
|
|
117
|
+
for (const error of episode.errors ?? []) {
|
|
118
|
+
const key = error.replace(/\s+/g, " ").trim().slice(0, 120);
|
|
119
|
+
if (!key) continue;
|
|
120
|
+
acc[key] = (acc[key] ?? 0) + 1;
|
|
121
|
+
}
|
|
122
|
+
return acc;
|
|
123
|
+
},
|
|
124
|
+
{} as Record<string, number>,
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const resolvedStruggles = all.facets.filter(
|
|
128
|
+
(entry) => entry.type === "struggle" && entry.facetData?.kind === "struggle" && !!entry.facetData.solution,
|
|
129
|
+
);
|
|
130
|
+
const unresolvedStruggles = all.facets.filter(
|
|
131
|
+
(entry) => entry.type === "struggle" && entry.facetData?.kind === "struggle" && !entry.facetData.solution,
|
|
132
|
+
);
|
|
133
|
+
const patternEntries = all.facets.filter(
|
|
134
|
+
(entry) => entry.type === "pattern" && entry.facetData?.kind === "pattern",
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
const topTools = Object.entries(toolCounts)
|
|
138
|
+
.sort((a, b) => b[1] - a[1])
|
|
139
|
+
.slice(0, 8);
|
|
140
|
+
const topLanguages = Object.entries(languageCounts)
|
|
141
|
+
.sort((a, b) => b[1] - a[1])
|
|
142
|
+
.slice(0, 8);
|
|
143
|
+
const topErrors = Object.entries(errorCounts)
|
|
144
|
+
.sort((a, b) => b[1] - a[1])
|
|
145
|
+
.slice(0, 8);
|
|
146
|
+
const topPatterns = patternEntries
|
|
147
|
+
.slice()
|
|
148
|
+
.sort((a, b) => (b.accessCount + 1) * b.importance - (a.accessCount + 1) * a.importance)
|
|
149
|
+
.slice(0, 6)
|
|
150
|
+
.map((entry) => ({
|
|
151
|
+
trigger: entry.facetData?.kind === "pattern" ? entry.facetData.trigger : "",
|
|
152
|
+
behavior: entry.facetData?.kind === "pattern" ? entry.facetData.behavior : "",
|
|
153
|
+
importance: entry.importance,
|
|
154
|
+
accessCount: entry.accessCount,
|
|
155
|
+
}));
|
|
156
|
+
|
|
157
|
+
const notableWins = resolvedStruggles.slice(0, 6).map((entry) => ({
|
|
158
|
+
problem: entry.facetData?.kind === "struggle" ? entry.facetData.problem : entry.summary || "",
|
|
159
|
+
solution: entry.facetData?.kind === "struggle" ? entry.facetData.solution : "",
|
|
160
|
+
importance: entry.importance,
|
|
161
|
+
}));
|
|
162
|
+
|
|
163
|
+
const notableFrictions = unresolvedStruggles.slice(0, 6).map((entry) => ({
|
|
164
|
+
problem: entry.facetData?.kind === "struggle" ? entry.facetData.problem : entry.summary || "",
|
|
165
|
+
attempts: entry.facetData?.kind === "struggle" ? entry.facetData.attempts : [],
|
|
166
|
+
importance: entry.importance,
|
|
167
|
+
}));
|
|
168
|
+
|
|
169
|
+
const topLessons = all.lessons
|
|
170
|
+
.slice()
|
|
171
|
+
.sort((a, b) => (b.accessCount + 1) * b.importance - (a.accessCount + 1) * a.importance)
|
|
172
|
+
.slice(0, 8)
|
|
173
|
+
.map((entry) => entry.summary || entry.detail || entry.content || "")
|
|
174
|
+
.filter(Boolean);
|
|
175
|
+
|
|
176
|
+
const projectCounts = all.episodes.reduce(
|
|
177
|
+
(acc, episode) => {
|
|
178
|
+
const key = episode.project || "default";
|
|
179
|
+
acc[key] = (acc[key] ?? 0) + 1;
|
|
180
|
+
return acc;
|
|
181
|
+
},
|
|
182
|
+
{} as Record<string, number>,
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
overview: {
|
|
187
|
+
totalSessions: all.meta.totalSessions,
|
|
188
|
+
episodes: all.episodes.length,
|
|
189
|
+
workEntries: all.work.length,
|
|
190
|
+
knowledgeEntries: all.knowledge.length,
|
|
191
|
+
lessonEntries: all.lessons.length,
|
|
192
|
+
preferenceEntries: all.preferences.length,
|
|
193
|
+
facetEntries: all.facets.length,
|
|
194
|
+
totalToolUses,
|
|
195
|
+
resolvedStruggleCount: resolvedStruggles.length,
|
|
196
|
+
unresolvedStruggleCount: unresolvedStruggles.length,
|
|
197
|
+
},
|
|
198
|
+
topTools: topTools.map(([tool, count]) => ({
|
|
199
|
+
tool,
|
|
200
|
+
count,
|
|
201
|
+
share: totalToolUses > 0 ? Number(((count / totalToolUses) * 100).toFixed(1)) : 0,
|
|
202
|
+
})),
|
|
203
|
+
topLanguages: topLanguages.map(([language, fileCount]) => ({ language, fileCount })),
|
|
204
|
+
topErrors: topErrors.map(([error, count]) => ({ error, count })),
|
|
205
|
+
topPatterns,
|
|
206
|
+
notableWins,
|
|
207
|
+
notableFrictions,
|
|
208
|
+
topLessons,
|
|
209
|
+
projectDistribution: Object.entries(projectCounts)
|
|
210
|
+
.sort((a, b) => b[1] - a[1])
|
|
211
|
+
.slice(0, 6)
|
|
212
|
+
.map(([project, sessions]) => ({ project, sessions })),
|
|
213
|
+
evidenceDigest: {
|
|
214
|
+
tools: summarizeCounts(topTools, (tool, count) => `${tool}: ${count} uses`),
|
|
215
|
+
languages: summarizeCounts(topLanguages, (language, count) => `${language}: ${count} files`),
|
|
216
|
+
errors: summarizeCounts(topErrors, (error, count) => `${error}: ${count} times`),
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function parseHumanInsightsResponse(raw: string): {
|
|
222
|
+
persona?: DeveloperPersona;
|
|
223
|
+
insights: HumanInsight[];
|
|
224
|
+
rootCauses: RootCauseInsight[];
|
|
225
|
+
} | null {
|
|
226
|
+
try {
|
|
227
|
+
const parsed = parseLlmJson<{
|
|
228
|
+
persona?: Record<string, unknown>;
|
|
229
|
+
insights?: Array<Record<string, unknown>>;
|
|
230
|
+
rootCauses?: Array<Record<string, unknown>>;
|
|
231
|
+
}>(raw);
|
|
232
|
+
|
|
233
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const persona = parsed.persona
|
|
238
|
+
? {
|
|
239
|
+
whatTheyDo: String(parsed.persona.whatTheyDo || ""),
|
|
240
|
+
experienceLevel: String(parsed.persona.experienceLevel || ""),
|
|
241
|
+
superpowers: Array.isArray(parsed.persona.superpowers) ? parsed.persona.superpowers.map(String) : [],
|
|
242
|
+
painPoints: Array.isArray(parsed.persona.painPoints) ? parsed.persona.painPoints.map(String) : [],
|
|
243
|
+
workStyle: String(parsed.persona.workStyle || ""),
|
|
244
|
+
summary: String(parsed.persona.summary || ""),
|
|
245
|
+
}
|
|
246
|
+
: undefined;
|
|
247
|
+
|
|
248
|
+
const insights: HumanInsight[] = Array.isArray(parsed.insights)
|
|
249
|
+
? parsed.insights
|
|
250
|
+
.map((item) => ({
|
|
251
|
+
title: String(item.title || "").trim(),
|
|
252
|
+
content: String(item.content || "").trim(),
|
|
253
|
+
icon: String(item.icon || "Insight").trim(),
|
|
254
|
+
utility: ["high", "medium", "low"].includes(String(item.utility))
|
|
255
|
+
? (String(item.utility) as "high" | "medium" | "low")
|
|
256
|
+
: "medium",
|
|
257
|
+
tags: Array.isArray(item.tags) ? item.tags.map(String) : [],
|
|
258
|
+
}))
|
|
259
|
+
.filter((item) => item.title && item.content)
|
|
260
|
+
: [];
|
|
261
|
+
|
|
262
|
+
const rootCauses: RootCauseInsight[] = Array.isArray(parsed.rootCauses)
|
|
263
|
+
? parsed.rootCauses
|
|
264
|
+
.map((item) => ({
|
|
265
|
+
symptom: String(item.symptom || "").trim(),
|
|
266
|
+
rootCause: String(item.rootCause || "").trim(),
|
|
267
|
+
evidence: Array.isArray(item.evidence) ? item.evidence.map(String) : [],
|
|
268
|
+
suggestion: String(item.suggestion || "").trim(),
|
|
269
|
+
}))
|
|
270
|
+
.filter((item) => item.symptom && item.rootCause)
|
|
271
|
+
: [];
|
|
272
|
+
|
|
273
|
+
return { persona, insights, rootCauses };
|
|
274
|
+
} catch {
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export async function generateHumanInsights(
|
|
280
|
+
all: ExportAllResult,
|
|
281
|
+
llmFn: LlmFn | undefined,
|
|
282
|
+
locale: string,
|
|
283
|
+
): Promise<{
|
|
284
|
+
persona?: DeveloperPersona;
|
|
285
|
+
humanInsights: HumanInsight[];
|
|
286
|
+
rootCauses: RootCauseInsight[];
|
|
287
|
+
}> {
|
|
288
|
+
if (!llmFn) {
|
|
289
|
+
return { humanInsights: [], rootCauses: [] };
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const data = buildHumanInsightsData(all);
|
|
293
|
+
const userPrompt = JSON.stringify({ locale, reviewData: data });
|
|
294
|
+
|
|
295
|
+
try {
|
|
296
|
+
const raw = await llmFn(HUMAN_INSIGHTS_SYSTEM_PROMPT, userPrompt);
|
|
297
|
+
const parsed = parseHumanInsightsResponse(raw);
|
|
298
|
+
if (parsed) {
|
|
299
|
+
return {
|
|
300
|
+
persona: parsed.persona,
|
|
301
|
+
humanInsights: parsed.insights,
|
|
302
|
+
rootCauses: parsed.rootCauses,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
} catch {
|
|
306
|
+
// Fall back to empty enhanced insights when the LLM path is unavailable.
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return { humanInsights: [], rootCauses: [] };
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export async function buildEnhancedInsightsReport(
|
|
313
|
+
all: ExportAllResult,
|
|
314
|
+
llmFn: LlmFn | undefined,
|
|
315
|
+
locale: string,
|
|
316
|
+
): Promise<EnhancedInsightsReport> {
|
|
317
|
+
const humanData = await generateHumanInsights(all, llmFn, locale);
|
|
318
|
+
|
|
319
|
+
return {
|
|
320
|
+
stats: {
|
|
321
|
+
knowledge: all.knowledge.length,
|
|
322
|
+
lessons: all.lessons.length,
|
|
323
|
+
preferences: all.preferences.length,
|
|
324
|
+
facets: all.facets.length,
|
|
325
|
+
episodes: all.episodes.length,
|
|
326
|
+
work: all.work.length,
|
|
327
|
+
totalSessions: all.meta.totalSessions,
|
|
328
|
+
},
|
|
329
|
+
atAGlance: { working: "", hindering: "", quickWins: "", ambitious: "" },
|
|
330
|
+
projectAreas: [],
|
|
331
|
+
charts: [],
|
|
332
|
+
wins: [],
|
|
333
|
+
frictions: [],
|
|
334
|
+
patterns: [],
|
|
335
|
+
recommendations: [],
|
|
336
|
+
featuresToTry: [],
|
|
337
|
+
usagePatterns: [],
|
|
338
|
+
generatedAt: new Date().toISOString(),
|
|
339
|
+
locale,
|
|
340
|
+
persona: humanData.persona,
|
|
341
|
+
humanInsights: humanData.humanInsights,
|
|
342
|
+
rootCauses: humanData.rootCauses,
|
|
343
|
+
comparisons: [],
|
|
344
|
+
};
|
|
345
|
+
}
|