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.
Files changed (199) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +30 -0
  3. package/THIRD_PARTY_NOTICES.md +8 -0
  4. package/dist/browser-fs.d.ts +14 -0
  5. package/dist/browser-fs.d.ts.map +1 -0
  6. package/dist/browser-fs.js +69 -0
  7. package/dist/browser-fs.js.map +1 -0
  8. package/dist/config.d.ts +81 -0
  9. package/dist/config.d.ts.map +1 -0
  10. package/dist/config.js +82 -0
  11. package/dist/config.js.map +1 -0
  12. package/dist/consolidate-v2.d.ts +15 -0
  13. package/dist/consolidate-v2.d.ts.map +1 -0
  14. package/dist/consolidate-v2.js +125 -0
  15. package/dist/consolidate-v2.js.map +1 -0
  16. package/dist/consolidation.d.ts +12 -0
  17. package/dist/consolidation.d.ts.map +1 -0
  18. package/dist/consolidation.js +199 -0
  19. package/dist/consolidation.js.map +1 -0
  20. package/dist/dedup.d.ts +18 -0
  21. package/dist/dedup.d.ts.map +1 -0
  22. package/dist/dedup.js +85 -0
  23. package/dist/dedup.js.map +1 -0
  24. package/dist/diagnostics.d.ts +24 -0
  25. package/dist/diagnostics.d.ts.map +1 -0
  26. package/dist/diagnostics.js +53 -0
  27. package/dist/diagnostics.js.map +1 -0
  28. package/dist/embedding-index.d.ts +27 -0
  29. package/dist/embedding-index.d.ts.map +1 -0
  30. package/dist/embedding-index.js +111 -0
  31. package/dist/embedding-index.js.map +1 -0
  32. package/dist/engine-archive.d.ts +37 -0
  33. package/dist/engine-archive.d.ts.map +1 -0
  34. package/dist/engine-archive.js +155 -0
  35. package/dist/engine-archive.js.map +1 -0
  36. package/dist/engine-episode-sync.d.ts +19 -0
  37. package/dist/engine-episode-sync.d.ts.map +1 -0
  38. package/dist/engine-episode-sync.js +186 -0
  39. package/dist/engine-episode-sync.js.map +1 -0
  40. package/dist/engine-injection-text.d.ts +46 -0
  41. package/dist/engine-injection-text.d.ts.map +1 -0
  42. package/dist/engine-injection-text.js +271 -0
  43. package/dist/engine-injection-text.js.map +1 -0
  44. package/dist/engine-insights.d.ts +22 -0
  45. package/dist/engine-insights.d.ts.map +1 -0
  46. package/dist/engine-insights.js +127 -0
  47. package/dist/engine-insights.js.map +1 -0
  48. package/dist/engine-links.d.ts +43 -0
  49. package/dist/engine-links.d.ts.map +1 -0
  50. package/dist/engine-links.js +223 -0
  51. package/dist/engine-links.js.map +1 -0
  52. package/dist/engine-recall-select.d.ts +84 -0
  53. package/dist/engine-recall-select.d.ts.map +1 -0
  54. package/dist/engine-recall-select.js +127 -0
  55. package/dist/engine-recall-select.js.map +1 -0
  56. package/dist/engine-reinforce.d.ts +18 -0
  57. package/dist/engine-reinforce.d.ts.map +1 -0
  58. package/dist/engine-reinforce.js +116 -0
  59. package/dist/engine-reinforce.js.map +1 -0
  60. package/dist/engine-scoring-v2.d.ts +35 -0
  61. package/dist/engine-scoring-v2.d.ts.map +1 -0
  62. package/dist/engine-scoring-v2.js +178 -0
  63. package/dist/engine-scoring-v2.js.map +1 -0
  64. package/dist/engine-v2-mapping.d.ts +17 -0
  65. package/dist/engine-v2-mapping.d.ts.map +1 -0
  66. package/dist/engine-v2-mapping.js +190 -0
  67. package/dist/engine-v2-mapping.js.map +1 -0
  68. package/dist/engine.d.ts +339 -0
  69. package/dist/engine.d.ts.map +1 -0
  70. package/dist/engine.js +1964 -0
  71. package/dist/engine.js.map +1 -0
  72. package/dist/eviction.d.ts +14 -0
  73. package/dist/eviction.d.ts.map +1 -0
  74. package/dist/eviction.js +26 -0
  75. package/dist/eviction.js.map +1 -0
  76. package/dist/extraction.d.ts +11 -0
  77. package/dist/extraction.d.ts.map +1 -0
  78. package/dist/extraction.js +261 -0
  79. package/dist/extraction.js.map +1 -0
  80. package/dist/full-insights-html.d.ts +9 -0
  81. package/dist/full-insights-html.d.ts.map +1 -0
  82. package/dist/full-insights-html.js +422 -0
  83. package/dist/full-insights-html.js.map +1 -0
  84. package/dist/full-insights-sections.d.ts +48 -0
  85. package/dist/full-insights-sections.d.ts.map +1 -0
  86. package/dist/full-insights-sections.js +231 -0
  87. package/dist/full-insights-sections.js.map +1 -0
  88. package/dist/full-insights.d.ts +22 -0
  89. package/dist/full-insights.d.ts.map +1 -0
  90. package/dist/full-insights.js +370 -0
  91. package/dist/full-insights.js.map +1 -0
  92. package/dist/hash-embedding.d.ts +9 -0
  93. package/dist/hash-embedding.d.ts.map +1 -0
  94. package/dist/hash-embedding.js +44 -0
  95. package/dist/hash-embedding.js.map +1 -0
  96. package/dist/human-insights.d.ts +28 -0
  97. package/dist/human-insights.d.ts.map +1 -0
  98. package/dist/human-insights.js +260 -0
  99. package/dist/human-insights.js.map +1 -0
  100. package/dist/i18n.d.ts +64 -0
  101. package/dist/i18n.d.ts.map +1 -0
  102. package/dist/i18n.js +367 -0
  103. package/dist/i18n.js.map +1 -0
  104. package/dist/index.d.ts +27 -0
  105. package/dist/index.d.ts.map +1 -0
  106. package/dist/index.js +21 -0
  107. package/dist/index.js.map +1 -0
  108. package/dist/insights-html.d.ts +9 -0
  109. package/dist/insights-html.d.ts.map +1 -0
  110. package/dist/insights-html.js +432 -0
  111. package/dist/insights-html.js.map +1 -0
  112. package/dist/linking.d.ts +22 -0
  113. package/dist/linking.d.ts.map +1 -0
  114. package/dist/linking.js +192 -0
  115. package/dist/linking.js.map +1 -0
  116. package/dist/llm-json.d.ts +10 -0
  117. package/dist/llm-json.d.ts.map +1 -0
  118. package/dist/llm-json.js +193 -0
  119. package/dist/llm-json.js.map +1 -0
  120. package/dist/privacy.d.ts +17 -0
  121. package/dist/privacy.d.ts.map +1 -0
  122. package/dist/privacy.js +53 -0
  123. package/dist/privacy.js.map +1 -0
  124. package/dist/procedural-v2.d.ts +9 -0
  125. package/dist/procedural-v2.d.ts.map +1 -0
  126. package/dist/procedural-v2.js +123 -0
  127. package/dist/procedural-v2.js.map +1 -0
  128. package/dist/reconsolidate-v2.d.ts +19 -0
  129. package/dist/reconsolidate-v2.d.ts.map +1 -0
  130. package/dist/reconsolidate-v2.js +115 -0
  131. package/dist/reconsolidate-v2.js.map +1 -0
  132. package/dist/scoring.d.ts +32 -0
  133. package/dist/scoring.d.ts.map +1 -0
  134. package/dist/scoring.js +108 -0
  135. package/dist/scoring.js.map +1 -0
  136. package/dist/store-v2.d.ts +35 -0
  137. package/dist/store-v2.d.ts.map +1 -0
  138. package/dist/store-v2.js +98 -0
  139. package/dist/store-v2.js.map +1 -0
  140. package/dist/store.d.ts +22 -0
  141. package/dist/store.d.ts.map +1 -0
  142. package/dist/store.js +116 -0
  143. package/dist/store.js.map +1 -0
  144. package/dist/turn-context.d.ts +37 -0
  145. package/dist/turn-context.d.ts.map +1 -0
  146. package/dist/turn-context.js +31 -0
  147. package/dist/turn-context.js.map +1 -0
  148. package/dist/types-v2.d.ts +190 -0
  149. package/dist/types-v2.d.ts.map +1 -0
  150. package/dist/types-v2.js +8 -0
  151. package/dist/types-v2.js.map +1 -0
  152. package/dist/types.d.ts +330 -0
  153. package/dist/types.d.ts.map +1 -0
  154. package/dist/types.js +8 -0
  155. package/dist/types.js.map +1 -0
  156. package/dist/update.d.ts +31 -0
  157. package/dist/update.d.ts.map +1 -0
  158. package/dist/update.js +250 -0
  159. package/dist/update.js.map +1 -0
  160. package/package.json +34 -0
  161. package/src/browser-fs.ts +67 -0
  162. package/src/config.ts +138 -0
  163. package/src/consolidate-v2.ts +158 -0
  164. package/src/consolidation.ts +229 -0
  165. package/src/dedup.ts +91 -0
  166. package/src/diagnostics.ts +81 -0
  167. package/src/embedding-index.ts +138 -0
  168. package/src/engine-archive.ts +167 -0
  169. package/src/engine-episode-sync.ts +237 -0
  170. package/src/engine-injection-text.ts +334 -0
  171. package/src/engine-insights.ts +182 -0
  172. package/src/engine-links.ts +311 -0
  173. package/src/engine-recall-select.ts +298 -0
  174. package/src/engine-reinforce.ts +154 -0
  175. package/src/engine-scoring-v2.ts +188 -0
  176. package/src/engine-v2-mapping.ts +204 -0
  177. package/src/engine.ts +2499 -0
  178. package/src/eviction.ts +38 -0
  179. package/src/extraction.ts +300 -0
  180. package/src/full-insights-html.ts +474 -0
  181. package/src/full-insights-sections.ts +305 -0
  182. package/src/full-insights.ts +423 -0
  183. package/src/hash-embedding.ts +48 -0
  184. package/src/human-insights.ts +345 -0
  185. package/src/i18n.ts +455 -0
  186. package/src/index.ts +117 -0
  187. package/src/insights-html.ts +478 -0
  188. package/src/linking.ts +228 -0
  189. package/src/llm-json.ts +194 -0
  190. package/src/privacy.ts +54 -0
  191. package/src/procedural-v2.ts +142 -0
  192. package/src/reconsolidate-v2.ts +145 -0
  193. package/src/scoring.ts +156 -0
  194. package/src/store-v2.ts +138 -0
  195. package/src/store.ts +137 -0
  196. package/src/turn-context.ts +56 -0
  197. package/src/types-v2.ts +244 -0
  198. package/src/types.ts +372 -0
  199. package/src/update.ts +271 -0
@@ -0,0 +1,305 @@
1
+ /**
2
+ * [WHO]: generateParallelFullInsightSections(), FullInsightsSectionPayload
3
+ * [FROM]: Depends on ./llm-json and ./types for structured LLM section generation
4
+ * [TO]: Consumed by full-insights.ts to improve /mem-insights narrative quality
5
+ * [HERE]: packages/mem-core/src/full-insights-sections.ts - Claude-style parallel section generation without copying upstream implementation
6
+ */
7
+
8
+ import { parseLlmJson } from "./llm-json.js";
9
+ import type {
10
+ FullInsightsAtAGlance,
11
+ FullInsightsFeatureToTry,
12
+ FullInsightsFriction,
13
+ FullInsightsProjectArea,
14
+ FullInsightsUsagePattern,
15
+ FullInsightsWin,
16
+ LlmFn,
17
+ MemoryEntry,
18
+ PatternInsight,
19
+ StruggleInsight,
20
+ } from "./types.js";
21
+
22
+ export interface FullInsightsSectionPayload {
23
+ atAGlance?: FullInsightsAtAGlance;
24
+ projectAreaDescriptions?: string[];
25
+ wins?: FullInsightsWin[];
26
+ frictions?: FullInsightsFriction[];
27
+ recommendations?: string[];
28
+ featuresToTry?: FullInsightsFeatureToTry[];
29
+ usagePatterns?: FullInsightsUsagePattern[];
30
+ }
31
+
32
+ export interface FullInsightsSectionContext {
33
+ locale: string;
34
+ outputLanguage: string;
35
+ stats: {
36
+ totalSessions: number;
37
+ episodes: number;
38
+ work: number;
39
+ knowledge: number;
40
+ lessons: number;
41
+ facets: number;
42
+ aggregateToolCount: number;
43
+ aggregateFileCount: number;
44
+ };
45
+ patterns: PatternInsight[];
46
+ struggles: StruggleInsight[];
47
+ lessons: MemoryEntry[];
48
+ projectAreas: FullInsightsProjectArea[];
49
+ topTools: Array<{ label: string; value: number }>;
50
+ topLanguages: Array<{ label: string; value: number }>;
51
+ topErrors: Array<{ label: string; value: number }>;
52
+ }
53
+
54
+ type SectionName =
55
+ | "project_areas"
56
+ | "wins"
57
+ | "frictions"
58
+ | "recommendations"
59
+ | "features"
60
+ | "usage_patterns";
61
+
62
+ interface SectionSpec {
63
+ name: SectionName;
64
+ prompt: string;
65
+ }
66
+
67
+ const OBSERVER_VOICE_RULES = `Voice rules:
68
+ - Be a warm but sharp long-term coding partner: kind, direct, observant.
69
+ - Write like an observant agent keeping a private work diary and a practical field report for the user.
70
+ - Tie every claim to the provided data; if evidence is thin, say the signal is still thin.
71
+ - No generic praise, personality diagnosis, therapy language, motivational fluff, or invented emotion.
72
+ - Each item should name the observed pattern, explain why it matters, and offer one next move when the schema allows.`;
73
+
74
+ function compactContext(context: FullInsightsSectionContext): Record<string, unknown> {
75
+ return {
76
+ locale: context.locale,
77
+ outputLanguage: context.outputLanguage,
78
+ stats: context.stats,
79
+ projectAreas: context.projectAreas.slice(0, 8).map((area) => ({
80
+ name: area.name,
81
+ sessionCount: area.sessionCount,
82
+ description: area.description,
83
+ })),
84
+ patterns: context.patterns.slice(0, 8).map((pattern) => ({
85
+ trigger: pattern.trigger,
86
+ behavior: pattern.behavior,
87
+ weight: Number(pattern.weight.toFixed(2)),
88
+ })),
89
+ struggles: context.struggles.slice(0, 8).map((struggle) => ({
90
+ problem: struggle.problem,
91
+ resolved: struggle.resolved,
92
+ attempts: struggle.attempts.slice(0, 3),
93
+ solution: struggle.solution,
94
+ weight: Number(struggle.weight.toFixed(2)),
95
+ })),
96
+ lessons: context.lessons.slice(0, 8).map((lesson) => lesson.summary || lesson.detail || lesson.content || ""),
97
+ topTools: context.topTools.slice(0, 8),
98
+ topLanguages: context.topLanguages.slice(0, 8),
99
+ topErrors: context.topErrors.slice(0, 8),
100
+ };
101
+ }
102
+
103
+ function sectionSpecs(outputLanguage: string): SectionSpec[] {
104
+ const languageRule = `Write the JSON string values in ${outputLanguage}, matching the user's observed language habits.`;
105
+ return [
106
+ {
107
+ name: "project_areas",
108
+ prompt: `${OBSERVER_VOICE_RULES}
109
+
110
+ Identify the main project/work areas from this developer memory report. Describe each area as something you have been watching the user return to, including what that investment suggests about their current work.
111
+ ${languageRule}
112
+ Return ONLY valid JSON:
113
+ {"descriptions":["one description per provided project area, preserving order"]}`,
114
+ },
115
+ {
116
+ name: "wins",
117
+ prompt: `${OBSERVER_VOICE_RULES}
118
+
119
+ Find concrete workflows that are working well. Make the wins feel seen, not flattered: recognize the user's effective habits with evidence from tools, lessons, resolved struggles, or project areas.
120
+ ${languageRule}
121
+ Return ONLY valid JSON:
122
+ {"wins":[{"title":"3-7 word title","description":"2-3 specific sentences"}]}`,
123
+ },
124
+ {
125
+ name: "frictions",
126
+ prompt: `${OBSERVER_VOICE_RULES}
127
+
128
+ Find recurring friction. Be gentle with the user but incisive about the system of work: prefer root causes over symptoms, avoid blame, and include examples when available.
129
+ ${languageRule}
130
+ Return ONLY valid JSON:
131
+ {"frictions":[{"title":"short category","description":"1-2 sentences","examples":["specific example"]}]}`,
132
+ },
133
+ {
134
+ name: "recommendations",
135
+ prompt: `${OBSERVER_VOICE_RULES}
136
+
137
+ Suggest direct behavior changes. Each recommendation should read like a useful note from a partner who has watched the pattern before: observation, why it matters, next move for the next session.
138
+ ${languageRule}
139
+ Return ONLY valid JSON:
140
+ {"recommendations":["recommendation sentence"]}`,
141
+ },
142
+ {
143
+ name: "features",
144
+ prompt: `${OBSERVER_VOICE_RULES}
145
+
146
+ Suggest Catui features or workflows this user should try based on their actual usage. Prefer skills, MCP, hooks, headless commands, subagents, recap, memory, or token-save only when evidence supports it; explain why it fits this user's observed habits.
147
+ ${languageRule}
148
+ Return ONLY valid JSON:
149
+ {"featuresToTry":[{"title":"feature/workflow","oneLiner":"what it does","whyForYou":"why this user should try it","exampleCode":"optional command or config"}]}`,
150
+ },
151
+ {
152
+ name: "usage_patterns",
153
+ prompt: `${OBSERVER_VOICE_RULES}
154
+
155
+ Suggest reusable prompt/workflow patterns. Make each one copyable and grounded in the reported friction or wins, as if you are leaving the user a practical note for how to begin next time.
156
+ ${languageRule}
157
+ Return ONLY valid JSON:
158
+ {"usagePatterns":[{"title":"pattern","summary":"short summary","detail":"how to apply it","pastePrompt":"copyable prompt"}]}`,
159
+ },
160
+ ];
161
+ }
162
+
163
+ async function generateSection(
164
+ spec: SectionSpec,
165
+ contextJson: string,
166
+ llmFn: LlmFn,
167
+ ): Promise<{ name: SectionName; result: Record<string, unknown> | null }> {
168
+ try {
169
+ const raw = await llmFn(
170
+ "You write structured developer usage insights as a warm but sharp long-term coding partner. Output only valid JSON matching the requested schema.",
171
+ `${spec.prompt}\n\nDATA:\n${contextJson}`,
172
+ );
173
+ const parsed = parseLlmJson<Record<string, unknown>>(raw);
174
+ return { name: spec.name, result: parsed && typeof parsed === "object" ? parsed : null };
175
+ } catch {
176
+ return { name: spec.name, result: null };
177
+ }
178
+ }
179
+
180
+ function asStringArray(value: unknown): string[] | undefined {
181
+ if (!Array.isArray(value)) return undefined;
182
+ const items = value.filter((item): item is string => typeof item === "string" && item.trim().length > 0);
183
+ return items.length ? items : undefined;
184
+ }
185
+
186
+ function asWins(value: unknown): FullInsightsWin[] | undefined {
187
+ if (!Array.isArray(value)) return undefined;
188
+ const wins = value
189
+ .map((item) => item as Partial<FullInsightsWin>)
190
+ .filter((item) => typeof item.title === "string" && typeof item.description === "string")
191
+ .map((item) => ({ title: item.title!, description: item.description! }));
192
+ return wins.length ? wins : undefined;
193
+ }
194
+
195
+ function asFrictions(value: unknown): FullInsightsFriction[] | undefined {
196
+ if (!Array.isArray(value)) return undefined;
197
+ const frictions = value
198
+ .map((item) => item as Partial<FullInsightsFriction>)
199
+ .filter((item) => typeof item.title === "string" && typeof item.description === "string")
200
+ .map((item) => ({
201
+ title: item.title!,
202
+ description: item.description!,
203
+ examples: Array.isArray(item.examples)
204
+ ? item.examples.filter((example): example is string => typeof example === "string")
205
+ : undefined,
206
+ }));
207
+ return frictions.length ? frictions : undefined;
208
+ }
209
+
210
+ function asFeatures(value: unknown): FullInsightsFeatureToTry[] | undefined {
211
+ if (!Array.isArray(value)) return undefined;
212
+ const features = value
213
+ .map((item) => item as Partial<FullInsightsFeatureToTry>)
214
+ .filter((item) => typeof item.title === "string" && typeof item.oneLiner === "string" && typeof item.whyForYou === "string")
215
+ .map((item) => ({
216
+ title: item.title!,
217
+ oneLiner: item.oneLiner!,
218
+ whyForYou: item.whyForYou!,
219
+ exampleCode: typeof item.exampleCode === "string" ? item.exampleCode : undefined,
220
+ }));
221
+ return features.length ? features : undefined;
222
+ }
223
+
224
+ function asUsagePatterns(value: unknown): FullInsightsUsagePattern[] | undefined {
225
+ if (!Array.isArray(value)) return undefined;
226
+ const patterns = value
227
+ .map((item) => item as Partial<FullInsightsUsagePattern>)
228
+ .filter((item) => typeof item.title === "string" && typeof item.summary === "string" && typeof item.detail === "string")
229
+ .map((item) => ({
230
+ title: item.title!,
231
+ summary: item.summary!,
232
+ detail: item.detail!,
233
+ pastePrompt: typeof item.pastePrompt === "string" ? item.pastePrompt : undefined,
234
+ }));
235
+ return patterns.length ? patterns : undefined;
236
+ }
237
+
238
+ function mergeSectionPayload(results: Array<{ name: SectionName; result: Record<string, unknown> | null }>): FullInsightsSectionPayload {
239
+ const payload: FullInsightsSectionPayload = {};
240
+ for (const { name, result } of results) {
241
+ if (!result) continue;
242
+ if (name === "project_areas") payload.projectAreaDescriptions = asStringArray(result.descriptions);
243
+ if (name === "wins") payload.wins = asWins(result.wins);
244
+ if (name === "frictions") payload.frictions = asFrictions(result.frictions);
245
+ if (name === "recommendations") payload.recommendations = asStringArray(result.recommendations);
246
+ if (name === "features") payload.featuresToTry = asFeatures(result.featuresToTry);
247
+ if (name === "usage_patterns") payload.usagePatterns = asUsagePatterns(result.usagePatterns);
248
+ }
249
+ return payload;
250
+ }
251
+
252
+ async function generateAtAGlance(
253
+ contextJson: string,
254
+ sections: FullInsightsSectionPayload,
255
+ llmFn: LlmFn,
256
+ outputLanguage: string,
257
+ ): Promise<FullInsightsAtAGlance | undefined> {
258
+ const languageRule = `Write concise natural ${outputLanguage}, matching the user's observed language habits.`;
259
+ try {
260
+ const raw = await llmFn(
261
+ "You write executive summaries for developer usage reports. Output only valid JSON.",
262
+ `${OBSERVER_VOICE_RULES}
263
+
264
+ ${languageRule}
265
+ Synthesize the section outputs into four short, candid coaching blurbs. Make this read like the opening note of a private diary and field report from a long-term coding partner: intimate enough to feel observed, precise enough to be useful.
266
+ Return ONLY valid JSON:
267
+ {"working":"what is working","hindering":"what is hindering","quickWins":"quick wins to try","ambitious":"ambitious workflows to prepare for"}
268
+
269
+ BASE DATA:
270
+ ${contextJson}
271
+
272
+ SECTION OUTPUTS:
273
+ ${JSON.stringify(sections, null, 2)}`,
274
+ );
275
+ const parsed = parseLlmJson<Partial<FullInsightsAtAGlance>>(raw);
276
+ if (
277
+ typeof parsed?.working === "string" &&
278
+ typeof parsed.hindering === "string" &&
279
+ typeof parsed.quickWins === "string" &&
280
+ typeof parsed.ambitious === "string"
281
+ ) {
282
+ return {
283
+ working: parsed.working,
284
+ hindering: parsed.hindering,
285
+ quickWins: parsed.quickWins,
286
+ ambitious: parsed.ambitious,
287
+ };
288
+ }
289
+ } catch {
290
+ // Keep caller fallbacks.
291
+ }
292
+ return undefined;
293
+ }
294
+
295
+ export async function generateParallelFullInsightSections(
296
+ context: FullInsightsSectionContext,
297
+ llmFn: LlmFn,
298
+ ): Promise<FullInsightsSectionPayload> {
299
+ const contextJson = JSON.stringify(compactContext(context), null, 2);
300
+ const results = await Promise.all(sectionSpecs(context.outputLanguage).map((spec) => generateSection(spec, contextJson, llmFn)));
301
+ const payload = mergeSectionPayload(results);
302
+ const atAGlance = await generateAtAGlance(contextJson, payload, llmFn, context.outputLanguage);
303
+ if (atAGlance) payload.atAGlance = atAGlance;
304
+ return payload;
305
+ }