noteconnection 1.7.0 → 1.8.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 (211) hide show
  1. package/LICENSE +674 -21
  2. package/README.md +242 -62
  3. package/dist/src/agent_workspace.contract.parity.test.js +475 -0
  4. package/dist/src/agent_workspace.frontend.test.js +9989 -0
  5. package/dist/src/agent_workspace.locale.contract.test.js +95 -0
  6. package/dist/src/agent_workspace.runtime.behavior.test.js +5072 -0
  7. package/dist/src/copy.assets.contract.test.js +130 -0
  8. package/dist/src/core/PathBridge.js +28 -3
  9. package/dist/src/export/WorkspaceExportBundle.js +1130 -0
  10. package/dist/src/export/WorkspaceExportBundle.test.js +1480 -0
  11. package/dist/src/export/index.js +18 -0
  12. package/dist/src/export/types.js +2 -0
  13. package/dist/src/fixrisk.issue.verifier.contract.test.js +3 -1
  14. package/dist/src/foundation.ann.runtime.contract.test.js +81 -0
  15. package/dist/src/foundation.release.evidence.contract.test.js +406 -0
  16. package/dist/src/foundation.sqlite.runtime.contract.test.js +82 -0
  17. package/dist/src/frontend/README.md +242 -62
  18. package/dist/src/frontend/agent_workspace.js +5353 -0
  19. package/dist/src/frontend/agent_workspace_runtime.js +4434 -0
  20. package/dist/src/frontend/analysis.js +31 -4
  21. package/dist/src/frontend/app.js +2178 -62
  22. package/dist/src/frontend/focus_mode_interactions.js +33 -0
  23. package/dist/src/frontend/godot_future_path_renderer.js +540 -0
  24. package/dist/src/frontend/godot_tree_interactions.js +178 -0
  25. package/dist/src/frontend/graph_state.mjs +105 -0
  26. package/dist/src/frontend/hosted_future_path_runtime.js +157 -0
  27. package/dist/src/frontend/i18n.mjs +186 -0
  28. package/dist/src/frontend/index.html +641 -266
  29. package/dist/src/frontend/layout_gpu.js +12 -4
  30. package/dist/src/frontend/locales/en.json +721 -5
  31. package/dist/src/frontend/locales/zh.json +721 -5
  32. package/dist/src/frontend/main.mjs +60 -0
  33. package/dist/src/frontend/markdown_runtime.js +827 -0
  34. package/dist/src/frontend/notemd.css +49 -0
  35. package/dist/src/frontend/notemd.html +7 -1
  36. package/dist/src/frontend/notemd.js +64 -0
  37. package/dist/src/frontend/path.html +107 -0
  38. package/dist/src/frontend/path_app.js +2108 -130
  39. package/dist/src/frontend/path_layout.mjs +143 -0
  40. package/dist/src/frontend/path_mermaid_utils.mjs +108 -0
  41. package/dist/src/frontend/path_modules_bridge.js +486 -0
  42. package/dist/src/frontend/path_state.mjs +118 -0
  43. package/dist/src/frontend/path_styles.css +146 -0
  44. package/dist/src/frontend/path_worker_bridge.mjs +85 -0
  45. package/dist/src/frontend/reader.js +393 -33
  46. package/dist/src/frontend/runtime_bridge.js +67 -54
  47. package/dist/src/frontend/runtime_bridge.mjs +279 -0
  48. package/dist/src/frontend/settings.js +130 -12
  49. package/dist/src/frontend/source_manager.js +190 -21
  50. package/dist/src/frontend/styles.css +2720 -72
  51. package/dist/src/frontend/workbench_state.mjs +101 -0
  52. package/dist/src/frontend/workspace_panes.js +10168 -0
  53. package/dist/src/frontend.locale.contract.test.js +62 -0
  54. package/dist/src/godot.sidecar.bootstrap.contract.test.js +244 -0
  55. package/dist/src/indexing/IndexLifecycle.js +195 -0
  56. package/dist/src/indexing/IndexLifecycle.test.js +49 -0
  57. package/dist/src/indexing/SegmentBuilder.js +64 -0
  58. package/dist/src/indexing/UnitBuilder.js +48 -0
  59. package/dist/src/indexing/types.js +2 -0
  60. package/dist/src/knowledge.api.contract.test.js +170 -0
  61. package/dist/src/learning/KnowledgeLearningPlatform.js +10386 -0
  62. package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +327 -0
  63. package/dist/src/learning/KnowledgeLearningPlatform.program-f.test.js +99 -0
  64. package/dist/src/learning/KnowledgeLearningPlatform.test.js +2971 -0
  65. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +2974 -0
  66. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +3928 -0
  67. package/dist/src/learning/answerReleaseReview.js +4319 -0
  68. package/dist/src/learning/answerReleaseReview.test.js +2888 -0
  69. package/dist/src/learning/api.js +2 -0
  70. package/dist/src/learning/conversationComposer.js +1480 -0
  71. package/dist/src/learning/conversationComposer.test.js +1817 -0
  72. package/dist/src/learning/domains/ConversationManager.js +53 -0
  73. package/dist/src/learning/domains/KnowledgeIngestor.js +238 -0
  74. package/dist/src/learning/domains/KnowledgeQuerier.js +187 -0
  75. package/dist/src/learning/domains/MasteryEngine.js +387 -0
  76. package/dist/src/learning/domains/MemoryPolicyManager.js +408 -0
  77. package/dist/src/learning/domains/QualityEvaluator.js +307 -0
  78. package/dist/src/learning/domains/TutorRouter.js +313 -0
  79. package/dist/src/learning/domains/index.js +33 -0
  80. package/dist/src/learning/domains/types.js +7 -0
  81. package/dist/src/learning/errors.js +29 -0
  82. package/dist/src/learning/evidenceContextAssembler.js +1176 -0
  83. package/dist/src/learning/evidenceContextAssembler.test.js +6332 -0
  84. package/dist/src/learning/graphContextAssembler.js +870 -0
  85. package/dist/src/learning/graphContextAssembler.test.js +1033 -0
  86. package/dist/src/learning/index.js +28 -0
  87. package/dist/src/learning/queryBackend.js +1898 -0
  88. package/dist/src/learning/queryBackend.test.js +955 -0
  89. package/dist/src/learning/ragContextPack.js +257 -0
  90. package/dist/src/learning/ragContextPack.test.js +160 -0
  91. package/dist/src/learning/ragPublicText.js +38 -0
  92. package/dist/src/learning/ragSufficiencyJudge.js +161 -0
  93. package/dist/src/learning/ragSufficiencyJudge.test.js +177 -0
  94. package/dist/src/learning/ragSufficiencyProviderJudge.js +227 -0
  95. package/dist/src/learning/ragSufficiencyProviderJudge.test.js +156 -0
  96. package/dist/src/learning/requestNormalization.js +198 -0
  97. package/dist/src/learning/runtimeCapability.js +4677 -0
  98. package/dist/src/learning/runtimeCapability.test.js +3635 -0
  99. package/dist/src/learning/store.js +1240 -0
  100. package/dist/src/learning/store.test.js +1126 -0
  101. package/dist/src/learning/tutorAdapter.js +2 -0
  102. package/dist/src/learning/types.js +2 -0
  103. package/dist/src/learning/vectorAccelerationAdapter.js +942 -0
  104. package/dist/src/learning/vectorAccelerationAdapter.test.js +382 -0
  105. package/dist/src/lfs.asset.policy.contract.test.js +153 -0
  106. package/dist/src/license.policy.contract.test.js +66 -0
  107. package/dist/src/memory/MemoryGovernance.js +74 -0
  108. package/dist/src/memory/MemoryGovernance.test.js +46 -0
  109. package/dist/src/memory/types.js +2 -0
  110. package/dist/src/mermaid.frontend.guard.contract.test.js +77 -0
  111. package/dist/src/middleware/auth.js +17 -0
  112. package/dist/src/middleware/body-parser.js +45 -0
  113. package/dist/src/middleware/cors.js +44 -0
  114. package/dist/src/middleware/index.js +21 -0
  115. package/dist/src/middleware/request-trace.js +96 -0
  116. package/dist/src/notemd/AppConfigToml.js +6 -4
  117. package/dist/src/notemd/MermaidProcessor.js +400 -50
  118. package/dist/src/notemd/NotemdService.js +498 -13
  119. package/dist/src/notemd/PromptManager.js +15 -0
  120. package/dist/src/notemd/cli/commands.js +357 -0
  121. package/dist/src/notemd/cli/dispatcher.js +225 -0
  122. package/dist/src/notemd/cli/index.js +169 -0
  123. package/dist/src/notemd/cli/parser.js +68 -0
  124. package/dist/src/notemd/cli/types.js +2 -0
  125. package/dist/src/notemd/constants.js +43 -0
  126. package/dist/src/notemd/diagram/diagramGenerationService.js +78 -0
  127. package/dist/src/notemd/diagram/diagramSpec.js +79 -0
  128. package/dist/src/notemd/diagram/diagramSpecResponseParser.js +131 -0
  129. package/dist/src/notemd/diagram/intent.js +95 -0
  130. package/dist/src/notemd/diagram/planner.js +71 -0
  131. package/dist/src/notemd/diagram/prompts/diagramSpecPrompt.js +42 -0
  132. package/dist/src/notemd/diagram/types.js +18 -0
  133. package/dist/src/notemd/index.js +26 -0
  134. package/dist/src/notemd/operations/capabilityManifest.js +23 -0
  135. package/dist/src/notemd/operations/cliContracts.js +17 -0
  136. package/dist/src/notemd/operations/configProfileCommands.js +85 -0
  137. package/dist/src/notemd/operations/registry.contract.test.js +95 -0
  138. package/dist/src/notemd/operations/registry.js +991 -0
  139. package/dist/src/notemd/operations/types.js +2 -0
  140. package/dist/src/notemd/providerDiagnostics.js +220 -0
  141. package/dist/src/notemd/providerProfiles.js +42 -0
  142. package/dist/src/notemd/providerTemplates.js +231 -0
  143. package/dist/src/notemd/search/DuckDuckGoProvider.js +39 -0
  144. package/dist/src/notemd/search/SearchManager.js +13 -0
  145. package/dist/src/notemd/search/SearchProvider.js +2 -0
  146. package/dist/src/notemd/search/TavilyProvider.js +44 -0
  147. package/dist/src/notemd.agent.manifest.test.js +85 -0
  148. package/dist/src/notemd.api.contract.test.js +14 -1
  149. package/dist/src/notemd.app_config_toml.test.js +2 -1
  150. package/dist/src/notemd.batch.workflow.test.js +117 -0
  151. package/dist/src/notemd.cli.e2e.test.js +136 -0
  152. package/dist/src/notemd.core.test.js +51 -0
  153. package/dist/src/notemd.diagram.pipeline.test.js +233 -0
  154. package/dist/src/notemd.providerTemplates.test.js +34 -0
  155. package/dist/src/notemd.server.integration.test.js +143 -35
  156. package/dist/src/notemd.workflow.pipeline.test.js +162 -0
  157. package/dist/src/pathbridge.handshake.contract.test.js +16 -2
  158. package/dist/src/pathmode.background.contract.test.js +69 -0
  159. package/dist/src/pathmode.settings.api.contract.test.js +9 -0
  160. package/dist/src/pkg.sidecar.contract.test.js +9 -3
  161. package/dist/src/platform/ExportProfile.js +58 -0
  162. package/dist/src/platform/PlatformCapabilities.js +45 -0
  163. package/dist/src/platform/PlatformCapabilities.test.js +30 -0
  164. package/dist/src/platform/RenderMaterializer.js +33 -0
  165. package/dist/src/platform/RenderMaterializer.test.js +32 -0
  166. package/dist/src/query_backend.external_http.integration.test.js +410 -0
  167. package/dist/src/reader_renderer.js +404 -3
  168. package/dist/src/reader_renderer.test.js +87 -0
  169. package/dist/src/release.godot.mirror.contract.test.js +73 -0
  170. package/dist/src/resources/ResourceRegistry.js +223 -0
  171. package/dist/src/resources/ResourceRegistry.test.js +61 -0
  172. package/dist/src/resources/types.js +2 -0
  173. package/dist/src/routes/agentWorkspaceDiagnostics.js +173 -0
  174. package/dist/src/routes/data.js +267 -0
  175. package/dist/src/routes/diagnostics.js +51 -0
  176. package/dist/src/routes/index.js +23 -0
  177. package/dist/src/routes/knowledge.js +968 -0
  178. package/dist/src/routes/markdown.js +287 -0
  179. package/dist/src/routes/notemd.js +565 -0
  180. package/dist/src/routes/registry.contract.test.js +130 -0
  181. package/dist/src/routes/render.js +285 -0
  182. package/dist/src/routes/runtimeRunbookRouteOps.js +149 -0
  183. package/dist/src/routes/runtimeRunbookRouteOps.test.js +194 -0
  184. package/dist/src/routes/settings.js +6 -0
  185. package/dist/src/routes/staticFiles.js +94 -0
  186. package/dist/src/routes/types.js +2 -0
  187. package/dist/src/runtime.transport.adapter.contract.test.js +81 -0
  188. package/dist/src/server.js +11090 -1549
  189. package/dist/src/server.migration.test.js +193 -21
  190. package/dist/src/server.port.fallback.contract.test.js +63 -0
  191. package/dist/src/session/SessionStateStore.js +81 -0
  192. package/dist/src/session/SessionStateStore.test.js +58 -0
  193. package/dist/src/session/types.js +2 -0
  194. package/dist/src/settings.runtime.contract.test.js +50 -0
  195. package/dist/src/shared/types.contract.test.js +107 -0
  196. package/dist/src/shared/types.js +22 -0
  197. package/dist/src/sidecar.replacement.boundary.contract.test.js +128 -0
  198. package/dist/src/sidecar.supply.readiness.contract.test.js +144 -0
  199. package/dist/src/source_manager.loadflow.test.js +46 -0
  200. package/dist/src/startup.layout.snapshot.contract.test.js +57 -0
  201. package/dist/src/tauri.frontend.build.contract.test.js +60 -0
  202. package/dist/src/tauri.sidecar.cleanup.contract.test.js +21 -0
  203. package/dist/src/utils/RuntimePaths.js +4 -13
  204. package/dist/src/utils/platform.js +153 -0
  205. package/dist/src/workflows/WorkflowArtifactStore.js +96 -0
  206. package/dist/src/workflows/WorkflowArtifactStore.test.js +80 -0
  207. package/dist/src/workflows/types.js +2 -0
  208. package/dist/src/workspace/WorkspaceRegistry.js +122 -0
  209. package/dist/src/workspace/WorkspaceRegistry.test.js +29 -0
  210. package/dist/src/workspace/types.js +2 -0
  211. package/package.json +53 -10
@@ -0,0 +1,387 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MasteryEngine = void 0;
4
+ class MasteryEngine {
5
+ constructor(platform) {
6
+ this.platform = platform;
7
+ this.pathGenerationCount = 0;
8
+ this.sessionBuildCount = 0;
9
+ this.sessionExecutionCount = 0;
10
+ this.actionExecutionCount = 0;
11
+ this.masteryDiagnosticCount = 0;
12
+ this.lastPathGeneratedAt = null;
13
+ // Domain-level analysis state
14
+ this.masteryTrendSnapshots = [];
15
+ this.misconceptionTrendSnapshots = [];
16
+ this.pathQualityHistory = [];
17
+ this.sessionBudgetViolations = [];
18
+ // Domain budget constraints
19
+ this.budgets = {
20
+ maxActionsPerSession: 60,
21
+ maxEstimatedMinutesPerSession: 480,
22
+ minEvidenceCoveragePct: 30,
23
+ maxActionsPerAtom: 5,
24
+ minActionKindDiversity: 2,
25
+ };
26
+ // ─── Private: atom action budget ────────────────────────────────
27
+ this.atomActionCounts = new Map();
28
+ }
29
+ async diagnoseMastery(r) {
30
+ this.masteryDiagnosticCount++;
31
+ const response = await this.platform.diagnoseMastery(r);
32
+ // Domain-level mastery trend analysis
33
+ this.recordMasteryTrendSnapshot(response);
34
+ const trend = this.computeMasteryTrend(10);
35
+ return {
36
+ ...response,
37
+ _domain: {
38
+ diagnosticNumber: this.masteryDiagnosticCount,
39
+ trend,
40
+ },
41
+ };
42
+ }
43
+ async queryMisconceptions(r) {
44
+ const response = await this.platform.queryMasteryMisconceptions(r);
45
+ // Domain-level misconception trend analysis
46
+ this.recordMisconceptionSnapshot(response);
47
+ const trend = this.computeMisconceptionTrend(8);
48
+ // Augment misconception items with domain-level risk classification
49
+ const items = (response?.items || []).map((item) => ({
50
+ ...item,
51
+ riskLevel: this.classifyMisconceptionRisk(item),
52
+ }));
53
+ return { ...response, items, _domain: { trend } };
54
+ }
55
+ async buildLearningPath(r) {
56
+ this.pathGenerationCount++;
57
+ this.lastPathGeneratedAt = new Date().toISOString();
58
+ this.validatePathRequest(r);
59
+ // Domain-level budget validation for path parameters
60
+ this.validatePathBudgets(r);
61
+ const response = await this.platform.buildLearningPath(r);
62
+ // Record path quality metrics
63
+ this.recordPathQuality(response);
64
+ return this.augmentPathResponse(response);
65
+ }
66
+ async buildStudySession(r) {
67
+ this.sessionBuildCount++;
68
+ // Domain-level session budget validation
69
+ this.validateSessionBudgets(r);
70
+ const response = await this.platform.buildStudySession(r);
71
+ // Domain-level session quality analysis
72
+ const quality = this.analyzeSessionQuality(response);
73
+ return { ...response, _domain: { sessionBuildNumber: this.sessionBuildCount, quality } };
74
+ }
75
+ async querySessionHistory(r) {
76
+ const response = await this.platform.queryStudySessionHistory(r);
77
+ // Domain-level history aggregation
78
+ const records = response?.records || [];
79
+ const aggregated = this.aggregateSessionHistory(records);
80
+ return { ...response, _domain: { aggregated } };
81
+ }
82
+ async executeSessionAction(r) {
83
+ this.actionExecutionCount++;
84
+ // Validate action against per-atom budget
85
+ const atomBudget = this.checkAtomActionBudget(r);
86
+ if (!atomBudget.ok) {
87
+ this.sessionBudgetViolations.push({
88
+ generatedAt: new Date().toISOString(),
89
+ violation: `atom_action_budget:${r?.action?.atomId}`,
90
+ observed: atomBudget.count,
91
+ limit: this.budgets.maxActionsPerAtom,
92
+ });
93
+ this.pruneViolations();
94
+ }
95
+ const response = await this.platform.executeStudySessionAction(r);
96
+ return response;
97
+ }
98
+ async executeSessionPlan(r) {
99
+ this.sessionExecutionCount++;
100
+ // Domain-level plan validation
101
+ this.validatePlanExecution(r);
102
+ const response = await this.platform.executeStudySessionPlan(r);
103
+ // Augment with domain-level execution telemetry
104
+ return {
105
+ ...response,
106
+ _domain: {
107
+ executionNumber: this.sessionExecutionCount,
108
+ planSize: Array.isArray(r?.actions) ? r.actions.length : 0,
109
+ pathStrategy: r?.pathStrategy ?? 'default',
110
+ },
111
+ };
112
+ }
113
+ async updateOrchestrationConfig(r) {
114
+ return this.platform.updateStudySessionOrchestrationConfig(r);
115
+ }
116
+ // ─── Public accessors ───────────────────────────────────────────
117
+ getPathGenerationCount() { return this.pathGenerationCount; }
118
+ getSessionExecutionCount() { return this.sessionExecutionCount; }
119
+ getDiagnosticsSummary() {
120
+ return {
121
+ masteryDiagnosticCount: this.masteryDiagnosticCount,
122
+ pathGenerationCount: this.pathGenerationCount,
123
+ lastPathGeneratedAt: this.lastPathGeneratedAt,
124
+ sessionBuildCount: this.sessionBuildCount,
125
+ sessionExecutionCount: this.sessionExecutionCount,
126
+ actionExecutionCount: this.actionExecutionCount,
127
+ masteryTrend: this.computeMasteryTrend(5),
128
+ misconceptionTrend: this.computeMisconceptionTrend(5),
129
+ budgetViolationCount: this.sessionBudgetViolations.length,
130
+ recentBudgetViolations: this.sessionBudgetViolations.slice(-5),
131
+ };
132
+ }
133
+ getMasteryTrendSnapshots() { return [...this.masteryTrendSnapshots]; }
134
+ getBudgetViolations() { return [...this.sessionBudgetViolations]; }
135
+ // ─── Private: validation ────────────────────────────────────────
136
+ validatePathRequest(r) {
137
+ if (!r)
138
+ throw new Error('Learning path request is required.');
139
+ const targetId = String(r?.targetId ?? '').trim();
140
+ if (!targetId && !r?.targetIds?.length) {
141
+ throw new Error('At least one targetId is required to build a learning path.');
142
+ }
143
+ }
144
+ validatePathBudgets(r) {
145
+ const maxMastery = Number(r?.maxMasteryPaths);
146
+ const maxDivergence = Number(r?.maxDivergencePaths);
147
+ if (maxMastery > 12)
148
+ throw new Error('maxMasteryPaths exceeds domain limit of 12.');
149
+ if (maxDivergence > 12)
150
+ throw new Error('maxDivergencePaths exceeds domain limit of 12.');
151
+ }
152
+ validateSessionBudgets(r) {
153
+ const maxActions = Number(r?.maxActions);
154
+ if (maxActions > this.budgets.maxActionsPerSession) {
155
+ throw new Error(`maxActions (${maxActions}) exceeds domain budget of ${this.budgets.maxActionsPerSession}.`);
156
+ }
157
+ }
158
+ validatePlanExecution(r) {
159
+ if (!r)
160
+ throw new Error('Study session plan execution request is required.');
161
+ if (!r?.userId)
162
+ throw new Error('userId is required for plan execution.');
163
+ const actions = Array.isArray(r?.actions) ? r.actions : [];
164
+ if (actions.length === 0 && !r?.focusAtomIds?.length) {
165
+ throw new Error('Plan must contain at least one action or focusAtomId.');
166
+ }
167
+ if (actions.length > this.budgets.maxActionsPerSession) {
168
+ throw new Error(`Plan action count (${actions.length}) exceeds session budget of ${this.budgets.maxActionsPerSession}.`);
169
+ }
170
+ }
171
+ // ─── Private: augmentation ──────────────────────────────────────
172
+ augmentPathResponse(response) {
173
+ const nodes = Array.isArray(response?.nodes) ? response.nodes : [];
174
+ const masteryPaths = Array.isArray(response?.masteryPaths) ? response.masteryPaths : [];
175
+ const divergencePaths = Array.isArray(response?.divergencePaths) ? response.divergencePaths : [];
176
+ // Compute prerequisite coverage
177
+ const prerequisiteCoveragePct = this.computePrerequisiteCoverage(masteryPaths, divergencePaths);
178
+ return {
179
+ ...response,
180
+ _domain: {
181
+ pathLength: nodes.length,
182
+ generatedAt: this.lastPathGeneratedAt,
183
+ generationNumber: this.pathGenerationCount,
184
+ hasPrerequisites: nodes.some((n) => n?.prerequisites?.length > 0),
185
+ estimatedDurationMinutes: nodes.length * 15,
186
+ prerequisiteCoveragePct,
187
+ masteryPathCount: masteryPaths.length,
188
+ divergencePathCount: divergencePaths.length,
189
+ },
190
+ };
191
+ }
192
+ // ─── Private: mastery trend analysis ────────────────────────────
193
+ recordMasteryTrendSnapshot(response) {
194
+ const summary = response?.summary || {};
195
+ const before = Number(summary.averageMasteryBefore || 0);
196
+ const after = Number(summary.averageMasteryAfter || 0);
197
+ const gainPct = before > 0 ? Number(((after - before) / before * 100).toFixed(2)) : 0;
198
+ this.masteryTrendSnapshots.push({
199
+ generatedAt: new Date().toISOString(),
200
+ averageMasteryBefore: before,
201
+ averageMasteryAfter: after,
202
+ updatedCount: summary.updatedCount || 0,
203
+ gainPct,
204
+ });
205
+ if (this.masteryTrendSnapshots.length > 50)
206
+ this.masteryTrendSnapshots.shift();
207
+ }
208
+ computeMasteryTrend(n) {
209
+ const window = this.masteryTrendSnapshots.slice(-n);
210
+ if (window.length < 2) {
211
+ return {
212
+ direction: 'insufficient_data',
213
+ recentGainPct: window[0]?.gainPct ?? 0,
214
+ windowAvgGainPct: window[0]?.gainPct ?? 0,
215
+ snapshots: window.length,
216
+ };
217
+ }
218
+ const mid = Math.floor(window.length / 2);
219
+ const recentAvg = window.slice(-mid).reduce((s, e) => s + e.gainPct, 0) / mid;
220
+ const earlierAvg = window.slice(0, mid).reduce((s, e) => s + e.gainPct, 0) / mid;
221
+ const delta = recentAvg - earlierAvg;
222
+ return {
223
+ direction: delta > 3 ? 'accelerating' : delta > 0.5 ? 'improving' : delta < -3 ? 'declining' : delta < -0.5 ? 'slowing' : 'stable',
224
+ recentGainPct: window[window.length - 1].gainPct,
225
+ windowAvgGainPct: Number((window.reduce((s, e) => s + e.gainPct, 0) / window.length).toFixed(2)),
226
+ snapshots: window.length,
227
+ };
228
+ }
229
+ // ─── Private: misconception trend analysis ──────────────────────
230
+ recordMisconceptionSnapshot(response) {
231
+ const items = Array.isArray(response?.items) ? response.items : [];
232
+ const highSeverityCount = items.filter((i) => (i.severityScore || 0) >= 0.7).length;
233
+ const averageSeverity = items.length > 0
234
+ ? Number((items.reduce((s, i) => s + (i.severityScore || 0), 0) / items.length).toFixed(4))
235
+ : 0;
236
+ const topItem = items.length > 0 ? items[0] : null;
237
+ this.misconceptionTrendSnapshots.push({
238
+ generatedAt: new Date().toISOString(),
239
+ totalItems: items.length,
240
+ averageSeverity,
241
+ highSeverityCount,
242
+ topErrorTag: topItem?.errorTag ?? null,
243
+ });
244
+ if (this.misconceptionTrendSnapshots.length > 50)
245
+ this.misconceptionTrendSnapshots.shift();
246
+ }
247
+ computeMisconceptionTrend(n) {
248
+ const window = this.misconceptionTrendSnapshots.slice(-n);
249
+ if (window.length < 2) {
250
+ return {
251
+ direction: 'insufficient_data',
252
+ recentAvgSeverity: window[0]?.averageSeverity ?? 0,
253
+ highSeverityTrend: 'stable',
254
+ snapshots: window.length,
255
+ };
256
+ }
257
+ const mid = Math.floor(window.length / 2);
258
+ const recentAvg = window.slice(-mid).reduce((s, e) => s + e.averageSeverity, 0) / mid;
259
+ const earlierAvg = window.slice(0, mid).reduce((s, e) => s + e.averageSeverity, 0) / mid;
260
+ const delta = recentAvg - earlierAvg;
261
+ const recentHigh = window.slice(-mid).reduce((s, e) => s + e.highSeverityCount, 0);
262
+ const earlierHigh = window.slice(0, mid).reduce((s, e) => s + e.highSeverityCount, 0);
263
+ return {
264
+ direction: delta < -0.05 ? 'resolving' : delta > 0.05 ? 'worsening' : 'stable',
265
+ recentAvgSeverity: Number(recentAvg.toFixed(4)),
266
+ highSeverityTrend: recentHigh < earlierHigh ? 'decreasing' : recentHigh > earlierHigh ? 'increasing' : 'stable',
267
+ snapshots: window.length,
268
+ };
269
+ }
270
+ classifyMisconceptionRisk(item) {
271
+ const severity = Number(item?.severityScore || 0);
272
+ const count = Number(item?.count || 0);
273
+ if (severity >= 0.8 && count >= 5)
274
+ return 'critical';
275
+ if (severity >= 0.6)
276
+ return 'high';
277
+ if (severity >= 0.35)
278
+ return 'moderate';
279
+ return 'low';
280
+ }
281
+ // ─── Private: path quality metrics ──────────────────────────────
282
+ recordPathQuality(response) {
283
+ const masteryPaths = Array.isArray(response?.masteryPaths) ? response.masteryPaths : [];
284
+ const divergencePaths = Array.isArray(response?.divergencePaths) ? response.divergencePaths : [];
285
+ const allActions = [...masteryPaths, ...divergencePaths].flatMap((p) => p.actions || []);
286
+ const totalNodes = allActions.length;
287
+ const prerequisiteCoveragePct = this.computePrerequisiteCoverage(masteryPaths, divergencePaths);
288
+ const averagePriority = totalNodes > 0
289
+ ? Number((allActions.reduce((s, a) => s + (a.priority || 0), 0) / totalNodes).toFixed(2))
290
+ : 0;
291
+ const actionKindDistribution = {};
292
+ for (const action of allActions) {
293
+ const kind = String(action.kind || 'unknown');
294
+ actionKindDistribution[kind] = (actionKindDistribution[kind] || 0) + 1;
295
+ }
296
+ this.pathQualityHistory.push({
297
+ generatedAt: new Date().toISOString(),
298
+ totalNodes,
299
+ prerequisiteCoveragePct,
300
+ averagePriority,
301
+ actionKindDistribution,
302
+ });
303
+ if (this.pathQualityHistory.length > 50)
304
+ this.pathQualityHistory.shift();
305
+ }
306
+ computePrerequisiteCoverage(masteryPaths, divergencePaths) {
307
+ const allPaths = [...masteryPaths, ...divergencePaths];
308
+ let totalPrerequisites = 0;
309
+ let coveredPrerequisites = 0;
310
+ for (const path of allPaths) {
311
+ const prerequisites = Array.isArray(path?.prerequisites) ? path.prerequisites : [];
312
+ totalPrerequisites += prerequisites.length;
313
+ for (const prereq of prerequisites) {
314
+ const prereqId = String(prereq?.id || prereq);
315
+ const isCovered = allPaths.some((p) => p.targetAtomId === prereqId ||
316
+ (p.actions || []).some((a) => a.atomId === prereqId));
317
+ if (isCovered)
318
+ coveredPrerequisites++;
319
+ }
320
+ }
321
+ return totalPrerequisites > 0
322
+ ? Number(((coveredPrerequisites / totalPrerequisites) * 100).toFixed(1))
323
+ : 100;
324
+ }
325
+ // ─── Private: session quality analysis ──────────────────────────
326
+ analyzeSessionQuality(response) {
327
+ const actions = Array.isArray(response?.actions) ? response.actions : [];
328
+ const summary = response?.summary || {};
329
+ const kinds = new Set(actions.map((a) => String(a.kind || '')));
330
+ const evidenceCoveragePct = (summary.evidenceCoverageRatio ?? 0) * 100;
331
+ const averagePriority = actions.length > 0
332
+ ? Number((actions.reduce((s, a) => s + (a.priority || 0), 0) / actions.length).toFixed(2))
333
+ : 0;
334
+ const budgetWarnings = [];
335
+ if (actions.length > this.budgets.maxActionsPerSession * 0.8) {
336
+ budgetWarnings.push(`Action count (${actions.length}) near budget limit (${this.budgets.maxActionsPerSession}).`);
337
+ }
338
+ if (evidenceCoveragePct < this.budgets.minEvidenceCoveragePct) {
339
+ budgetWarnings.push(`Evidence coverage (${Number(evidenceCoveragePct).toFixed(1)}%) below minimum (${this.budgets.minEvidenceCoveragePct}%).`);
340
+ }
341
+ if (kinds.size < this.budgets.minActionKindDiversity) {
342
+ budgetWarnings.push(`Action kind diversity (${kinds.size}) below recommended minimum (${this.budgets.minActionKindDiversity}).`);
343
+ }
344
+ return {
345
+ actionKindDiversity: kinds.size,
346
+ evidenceCoveragePct: Number(evidenceCoveragePct.toFixed(1)),
347
+ averagePriority,
348
+ withinBudget: budgetWarnings.length === 0,
349
+ budgetWarnings,
350
+ };
351
+ }
352
+ // ─── Private: session history aggregation ───────────────────────
353
+ aggregateSessionHistory(records) {
354
+ let totalActions = 0;
355
+ let masteryDeltaSum = 0;
356
+ let confidenceSum = 0;
357
+ for (const record of records) {
358
+ totalActions += Math.max(0, Math.floor(Number(record.executedCount || 0)));
359
+ masteryDeltaSum += Number(record.averageMasteryDelta || 0);
360
+ confidenceSum += Number(record.averageTutorConfidence || 0);
361
+ }
362
+ return {
363
+ totalSessions: records.length,
364
+ totalActions,
365
+ averageMasteryDelta: records.length > 0
366
+ ? Number((masteryDeltaSum / records.length).toFixed(6))
367
+ : 0,
368
+ averageTutorConfidence: records.length > 0
369
+ ? Number((confidenceSum / records.length).toFixed(4))
370
+ : 0,
371
+ };
372
+ }
373
+ checkAtomActionBudget(r) {
374
+ const atomId = String(r?.action?.atomId || '');
375
+ if (!atomId)
376
+ return { ok: true, count: 0 };
377
+ const count = (this.atomActionCounts.get(atomId) || 0) + 1;
378
+ this.atomActionCounts.set(atomId, count);
379
+ return { ok: count <= this.budgets.maxActionsPerAtom, count };
380
+ }
381
+ pruneViolations() {
382
+ if (this.sessionBudgetViolations.length > 100) {
383
+ this.sessionBudgetViolations.shift();
384
+ }
385
+ }
386
+ }
387
+ exports.MasteryEngine = MasteryEngine;