@jagilber-org/index-server 1.22.1 → 1.26.1

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 (189) hide show
  1. package/CHANGELOG.md +87 -2
  2. package/CODE_OF_CONDUCT.md +2 -0
  3. package/CONTRIBUTING.md +32 -2
  4. package/README.md +82 -19
  5. package/SECURITY.md +17 -5
  6. package/dist/config/dashboardConfig.d.ts +3 -0
  7. package/dist/config/dashboardConfig.js +3 -0
  8. package/dist/config/defaultValues.d.ts +1 -1
  9. package/dist/config/defaultValues.js +1 -1
  10. package/dist/config/featureConfig.d.ts +2 -0
  11. package/dist/config/featureConfig.js +6 -1
  12. package/dist/config/runtimeConfig.d.ts +1 -1
  13. package/dist/config/runtimeConfig.js +8 -9
  14. package/dist/dashboard/client/admin.html +170 -53
  15. package/dist/dashboard/client/css/admin.css +132 -0
  16. package/dist/dashboard/client/js/admin.auth.js +25 -11
  17. package/dist/dashboard/client/js/admin.config.js +1 -1
  18. package/dist/dashboard/client/js/admin.feedback.js +328 -0
  19. package/dist/dashboard/client/js/admin.graph.js +120 -18
  20. package/dist/dashboard/client/js/admin.instructions.js +27 -13
  21. package/dist/dashboard/client/js/admin.logs.js +1 -5
  22. package/dist/dashboard/client/js/admin.maintenance.js +53 -8
  23. package/dist/dashboard/client/js/admin.messaging.js +1 -4
  24. package/dist/dashboard/client/js/admin.overview.js +5 -1
  25. package/dist/dashboard/client/js/admin.sessions.js +1 -1
  26. package/dist/dashboard/client/js/admin.utils.js +43 -1
  27. package/dist/dashboard/client/js/mermaid.min.js +813 -537
  28. package/dist/dashboard/export/DataExporter.js +2 -1
  29. package/dist/dashboard/server/AdminPanel.d.ts +3 -0
  30. package/dist/dashboard/server/AdminPanel.js +132 -35
  31. package/dist/dashboard/server/ApiRoutes.js +40 -9
  32. package/dist/dashboard/server/DashboardServer.js +1 -1
  33. package/dist/dashboard/server/FileMetricsStorage.d.ts +19 -0
  34. package/dist/dashboard/server/FileMetricsStorage.js +52 -5
  35. package/dist/dashboard/server/HttpTransport.js +6 -0
  36. package/dist/dashboard/server/InstanceManager.js +7 -2
  37. package/dist/dashboard/server/KnowledgeStore.js +7 -2
  38. package/dist/dashboard/server/MetricsCollector.d.ts +16 -0
  39. package/dist/dashboard/server/MetricsCollector.js +113 -17
  40. package/dist/dashboard/server/legacyDashboardHtml.js +7 -2
  41. package/dist/dashboard/server/middleware/ensureLoadedMiddleware.d.ts +1 -1
  42. package/dist/dashboard/server/middleware/ensureLoadedMiddleware.js +8 -3
  43. package/dist/dashboard/server/routes/admin.feedback.routes.d.ts +15 -0
  44. package/dist/dashboard/server/routes/admin.feedback.routes.js +188 -0
  45. package/dist/dashboard/server/routes/admin.routes.js +35 -27
  46. package/dist/dashboard/server/routes/alerts.routes.js +4 -3
  47. package/dist/dashboard/server/routes/api.feedback.routes.js +2 -1
  48. package/dist/dashboard/server/routes/api.usage.routes.js +8 -7
  49. package/dist/dashboard/server/routes/embeddings.routes.d.ts +2 -1
  50. package/dist/dashboard/server/routes/embeddings.routes.js +18 -9
  51. package/dist/dashboard/server/routes/graph.routes.js +10 -13
  52. package/dist/dashboard/server/routes/index.d.ts +1 -0
  53. package/dist/dashboard/server/routes/index.js +74 -39
  54. package/dist/dashboard/server/routes/instances.routes.js +2 -1
  55. package/dist/dashboard/server/routes/instructions.routes.js +46 -27
  56. package/dist/dashboard/server/routes/knowledge.routes.js +4 -3
  57. package/dist/dashboard/server/routes/logs.routes.js +5 -4
  58. package/dist/dashboard/server/routes/messaging.routes.js +15 -14
  59. package/dist/dashboard/server/routes/metrics.routes.js +14 -13
  60. package/dist/dashboard/server/routes/scripts.routes.js +6 -3
  61. package/dist/dashboard/server/routes/status.routes.js +5 -4
  62. package/dist/dashboard/server/routes/synthetic.routes.js +3 -2
  63. package/dist/dashboard/server/routes/usage.routes.js +2 -1
  64. package/dist/dashboard/server/utils/escapeHtml.d.ts +1 -0
  65. package/dist/dashboard/server/utils/escapeHtml.js +11 -0
  66. package/dist/dashboard/server/utils/pathContainment.d.ts +1 -0
  67. package/dist/dashboard/server/utils/pathContainment.js +15 -0
  68. package/dist/dashboard/server/wsInit.js +2 -2
  69. package/dist/lib/mcpStdioLogging.d.ts +165 -0
  70. package/dist/lib/mcpStdioLogging.js +287 -0
  71. package/dist/schemas/index.d.ts +37 -2
  72. package/dist/schemas/index.js +27 -3
  73. package/dist/server/backgroundServicesStartup.d.ts +7 -1
  74. package/dist/server/backgroundServicesStartup.js +25 -8
  75. package/dist/server/certInit.d.ts +97 -0
  76. package/dist/server/certInit.js +359 -0
  77. package/dist/server/certInit.types.d.ts +92 -0
  78. package/dist/server/certInit.types.js +34 -0
  79. package/dist/server/handshake/fallbackFrames.d.ts +31 -0
  80. package/dist/server/handshake/fallbackFrames.js +38 -0
  81. package/dist/server/handshake/initializeDetector.d.ts +31 -0
  82. package/dist/server/handshake/initializeDetector.js +88 -0
  83. package/dist/server/handshake/protocol.d.ts +15 -0
  84. package/dist/server/handshake/protocol.js +37 -0
  85. package/dist/server/handshake/readyEmitter.d.ts +6 -0
  86. package/dist/server/handshake/readyEmitter.js +88 -0
  87. package/dist/server/handshake/safetyFallbacks.d.ts +1 -0
  88. package/dist/server/handshake/safetyFallbacks.js +134 -0
  89. package/dist/server/handshake/stdinSniffer.d.ts +1 -0
  90. package/dist/server/handshake/stdinSniffer.js +260 -0
  91. package/dist/server/handshake/tracing.d.ts +16 -0
  92. package/dist/server/handshake/tracing.js +95 -0
  93. package/dist/server/handshakeManager.d.ts +23 -23
  94. package/dist/server/handshakeManager.js +36 -466
  95. package/dist/server/index-server.d.ts +23 -0
  96. package/dist/server/index-server.js +194 -9
  97. package/dist/server/mcpReadOnlySurfaces.d.ts +44 -0
  98. package/dist/server/mcpReadOnlySurfaces.js +297 -0
  99. package/dist/server/sdkServer.js +69 -7
  100. package/dist/server/transport.d.ts +5 -6
  101. package/dist/server/transport.js +46 -64
  102. package/dist/server/transportFactory.d.ts +3 -9
  103. package/dist/server/transportFactory.js +18 -380
  104. package/dist/services/atomicFs.d.ts +3 -0
  105. package/dist/services/atomicFs.js +171 -13
  106. package/dist/services/auditLog.d.ts +17 -2
  107. package/dist/services/auditLog.js +75 -14
  108. package/dist/services/bootstrapGating.js +1 -1
  109. package/dist/services/categoryRules.d.ts +10 -0
  110. package/dist/services/categoryRules.js +17 -0
  111. package/dist/services/classificationService.js +7 -5
  112. package/dist/services/embeddingService.d.ts +27 -11
  113. package/dist/services/embeddingService.js +51 -14
  114. package/dist/services/feedbackStorage.d.ts +39 -0
  115. package/dist/services/feedbackStorage.js +88 -0
  116. package/dist/services/handlers/instructions.add.js +429 -317
  117. package/dist/services/handlers/instructions.groom.js +128 -31
  118. package/dist/services/handlers/instructions.import.js +56 -23
  119. package/dist/services/handlers/instructions.patch.js +43 -32
  120. package/dist/services/handlers/instructions.query.js +20 -29
  121. package/dist/services/handlers/instructions.shared.d.ts +54 -0
  122. package/dist/services/handlers/instructions.shared.js +126 -1
  123. package/dist/services/handlers.activation.js +83 -81
  124. package/dist/services/handlers.dashboardConfig.d.ts +2 -2
  125. package/dist/services/handlers.dashboardConfig.js +1 -2
  126. package/dist/services/handlers.diagnostics.js +75 -54
  127. package/dist/services/handlers.feedback.d.ts +4 -11
  128. package/dist/services/handlers.feedback.js +11 -333
  129. package/dist/services/handlers.gates.js +69 -37
  130. package/dist/services/handlers.graph.js +2 -2
  131. package/dist/services/handlers.help.js +2 -2
  132. package/dist/services/handlers.instructionSchema.js +4 -2
  133. package/dist/services/handlers.integrity.js +42 -22
  134. package/dist/services/handlers.messaging.js +1 -1
  135. package/dist/services/handlers.metrics.js +51 -6
  136. package/dist/services/handlers.prompt.js +10 -2
  137. package/dist/services/handlers.search.js +94 -44
  138. package/dist/services/handlers.trace.js +1 -1
  139. package/dist/services/handlers.usage.js +38 -7
  140. package/dist/services/indexContext.d.ts +21 -1
  141. package/dist/services/indexContext.js +263 -78
  142. package/dist/services/indexLoader.d.ts +1 -0
  143. package/dist/services/indexLoader.js +28 -8
  144. package/dist/services/instructionRecordValidation.d.ts +39 -0
  145. package/dist/services/instructionRecordValidation.js +388 -0
  146. package/dist/services/instructions.dispatcher.js +4 -4
  147. package/dist/services/loaderSchemaValidator.d.ts +15 -0
  148. package/dist/services/loaderSchemaValidator.js +69 -0
  149. package/dist/services/logger.js +11 -2
  150. package/dist/services/mcpLogBridge.d.ts +49 -0
  151. package/dist/services/mcpLogBridge.js +83 -0
  152. package/dist/services/ownershipService.js +18 -8
  153. package/dist/services/performanceBaseline.js +23 -22
  154. package/dist/services/promptReviewService.d.ts +3 -1
  155. package/dist/services/promptReviewService.js +41 -13
  156. package/dist/services/regexSafety.d.ts +6 -0
  157. package/dist/services/regexSafety.js +46 -0
  158. package/dist/services/seedBootstrap.js +1 -1
  159. package/dist/services/storage/factory.d.ts +14 -1
  160. package/dist/services/storage/factory.js +61 -1
  161. package/dist/services/storage/jsonEmbeddingStore.d.ts +15 -0
  162. package/dist/services/storage/jsonEmbeddingStore.js +83 -0
  163. package/dist/services/storage/jsonFileStore.d.ts +3 -1
  164. package/dist/services/storage/jsonFileStore.js +8 -6
  165. package/dist/services/storage/migrationEngine.d.ts +13 -0
  166. package/dist/services/storage/migrationEngine.js +31 -0
  167. package/dist/services/storage/sqliteEmbeddingStore.d.ts +30 -0
  168. package/dist/services/storage/sqliteEmbeddingStore.js +222 -0
  169. package/dist/services/storage/sqliteStore.d.ts +3 -1
  170. package/dist/services/storage/sqliteStore.js +2 -2
  171. package/dist/services/storage/types.d.ts +48 -1
  172. package/dist/services/toolRegistry.js +77 -67
  173. package/dist/services/toolRegistry.zod.js +89 -86
  174. package/dist/services/tracing.js +5 -4
  175. package/dist/utils/envUtils.d.ts +4 -0
  176. package/dist/utils/envUtils.js +7 -0
  177. package/dist/utils/memoryMonitor.js +11 -10
  178. package/package.json +11 -4
  179. package/schemas/instruction.schema.json +38 -1
  180. package/scripts/copy-dashboard-assets.mjs +1 -1
  181. package/scripts/dist/README.md +1 -1
  182. package/scripts/setup-wizard.mjs +781 -0
  183. package/server.json +1 -0
  184. package/dist/externalClientLib.d.ts +0 -1
  185. package/dist/externalClientLib.js +0 -2
  186. package/dist/portableClientWrapper.d.ts +0 -1
  187. package/dist/portableClientWrapper.js +0 -2
  188. package/dist/services/indexingService.d.ts +0 -1
  189. package/dist/services/indexingService.js +0 -2
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getInvariantRepairSummary = getInvariantRepairSummary;
6
7
  exports.clearUsageRateLimit = clearUsageRateLimit;
7
8
  exports.loadUsageSnapshot = loadUsageSnapshot;
8
9
  exports.getInstructionsDir = getInstructionsDir;
@@ -10,15 +11,19 @@ exports.diagnoseInstructionsDir = diagnoseInstructionsDir;
10
11
  exports.touchIndexVersion = touchIndexVersion;
11
12
  exports.markindexDirty = markindexDirty;
12
13
  exports.ensureLoaded = ensureLoaded;
14
+ exports.ensureLoadedAsync = ensureLoadedAsync;
13
15
  exports.startIndexVersionPoller = startIndexVersionPoller;
14
16
  exports.stopIndexVersionPoller = stopIndexVersionPoller;
15
17
  exports.invalidate = invalidate;
16
18
  exports.getIndexState = getIndexState;
19
+ exports.getIndexStateAsync = getIndexStateAsync;
17
20
  exports.getDebugIndexSnapshot = getDebugIndexSnapshot;
18
21
  exports.getIndexDiagnostics = getIndexDiagnostics;
19
22
  exports.projectGovernance = projectGovernance;
20
23
  exports.computeGovernanceHash = computeGovernanceHash;
24
+ exports.isDuplicateInstructionWriteError = isDuplicateInstructionWriteError;
21
25
  exports.writeEntry = writeEntry;
26
+ exports.writeEntryAsync = writeEntryAsync;
22
27
  exports.removeEntry = removeEntry;
23
28
  exports.scheduleUsagePersist = scheduleUsagePersist;
24
29
  exports.incrementUsage = incrementUsage;
@@ -35,6 +40,9 @@ const envUtils_1 = require("../utils/envUtils");
35
40
  const runtimeConfig_1 = require("../config/runtimeConfig");
36
41
  const factory_1 = require("./storage/factory");
37
42
  const migrationEngine_1 = require("./storage/migrationEngine");
43
+ const instructionRecordValidation_1 = require("./instructionRecordValidation");
44
+ const loaderSchemaValidator_1 = require("./loaderSchemaValidator");
45
+ const schemaVersion_1 = require("../versioning/schemaVersion");
38
46
  let state = null;
39
47
  // Simple reliable invalidation: any mutation sets dirty=true; next ensureLoaded() performs full rescan.
40
48
  let dirty = false;
@@ -79,6 +87,19 @@ const firstSeenAuthority = {};
79
87
  const usageAuthority = {};
80
88
  // Authoritative lastUsedAt map for resilience between reload + snapshot overlay timing.
81
89
  const lastUsedAuthority = {};
90
+ // ── Invariant repair tracking (#131) ─────────────────────────────
91
+ // Accumulates repair events so they can be surfaced via health checks.
92
+ const invariantRepairLog = [];
93
+ const MAX_REPAIR_LOG = 200;
94
+ function trackInvariantRepair(id, field, source) {
95
+ invariantRepairLog.push({ ts: new Date().toISOString(), id, field, source });
96
+ if (invariantRepairLog.length > MAX_REPAIR_LOG)
97
+ invariantRepairLog.shift();
98
+ }
99
+ /** Returns a summary of invariant repairs for health check visibility. */
100
+ function getInvariantRepairSummary() {
101
+ return { totalRepairs: invariantRepairLog.length, recentRepairs: invariantRepairLog.slice(-20) };
102
+ }
82
103
  // Defensive invariant repair: if any code path ever observes an InstructionEntry with a missing
83
104
  // firstSeenTs after it was previously established (should not happen, but flake indicates a very
84
105
  // rare timing or cross-test interaction), we repair it from ephemeral cache or lastGood snapshot.
@@ -89,22 +110,30 @@ function restoreFirstSeenInvariant(e) {
89
110
  if (auth) {
90
111
  e.firstSeenTs = auth;
91
112
  (0, features_1.incrementCounter)('usage:firstSeenAuthorityRepair');
113
+ trackInvariantRepair(e.id, 'firstSeenTs', 'authority');
114
+ (0, logger_js_1.logWarn)(`[invariant-repair] firstSeenTs restored from authority for ${e.id}`);
92
115
  return;
93
116
  }
94
117
  const ep = ephemeralFirstSeen[e.id];
95
118
  if (ep) {
96
119
  e.firstSeenTs = ep;
97
120
  (0, features_1.incrementCounter)('usage:firstSeenInvariantRepair');
121
+ trackInvariantRepair(e.id, 'firstSeenTs', 'ephemeral');
122
+ (0, logger_js_1.logWarn)(`[invariant-repair] firstSeenTs restored from ephemeral cache for ${e.id}`);
98
123
  return;
99
124
  }
100
125
  const snap = lastGoodUsageSnapshot[e.id];
101
126
  if (snap?.firstSeenTs) {
102
127
  e.firstSeenTs = snap.firstSeenTs;
103
128
  (0, features_1.incrementCounter)('usage:firstSeenInvariantRepair');
129
+ trackInvariantRepair(e.id, 'firstSeenTs', 'snapshot');
130
+ (0, logger_js_1.logWarn)(`[invariant-repair] firstSeenTs restored from snapshot for ${e.id}`);
104
131
  }
105
132
  // If still missing after all repair sources, track an exhausted repair attempt (extremely rare diagnostic)
106
133
  if (!e.firstSeenTs) {
107
134
  (0, features_1.incrementCounter)('usage:firstSeenRepairExhausted');
135
+ trackInvariantRepair(e.id, 'firstSeenTs', 'exhausted');
136
+ (0, logger_js_1.logWarn)(`[invariant-repair] firstSeenTs repair exhausted — no source found for ${e.id}`);
108
137
  }
109
138
  }
110
139
  // Usage invariant repair (mirrors firstSeen invariant strategy). Extremely rare reload races in CI produced
@@ -115,26 +144,33 @@ function restoreFirstSeenInvariant(e) {
115
144
  function restoreUsageInvariant(e) {
116
145
  if (e.usageCount != null)
117
146
  return;
118
- // Prefer authoritative value, then observed, then persisted snapshot, else default 0.
119
147
  if (usageAuthority[e.id] != null) {
120
148
  e.usageCount = usageAuthority[e.id];
121
149
  (0, features_1.incrementCounter)('usage:usageInvariantAuthorityRepair');
150
+ trackInvariantRepair(e.id, 'usageCount', 'authority');
151
+ (0, logger_js_1.logWarn)(`[invariant-repair] usageCount restored from authority for ${e.id} (value=${usageAuthority[e.id]})`);
122
152
  return;
123
153
  }
124
154
  if (observedUsage[e.id] != null) {
125
155
  e.usageCount = observedUsage[e.id];
126
156
  (0, features_1.incrementCounter)('usage:usageInvariantObservedRepair');
157
+ trackInvariantRepair(e.id, 'usageCount', 'observed');
158
+ (0, logger_js_1.logWarn)(`[invariant-repair] usageCount restored from observed for ${e.id} (value=${observedUsage[e.id]})`);
127
159
  return;
128
160
  }
129
161
  const snap = lastGoodUsageSnapshot[e.id];
130
162
  if (snap?.usageCount != null) {
131
163
  e.usageCount = snap.usageCount;
132
164
  (0, features_1.incrementCounter)('usage:usageInvariantSnapshotRepair');
165
+ trackInvariantRepair(e.id, 'usageCount', 'snapshot');
166
+ (0, logger_js_1.logWarn)(`[invariant-repair] usageCount restored from snapshot for ${e.id} (value=${snap.usageCount})`);
133
167
  return;
134
168
  }
135
169
  // Fall back to 0 – deterministic floor; next increment will advance.
136
170
  e.usageCount = 0;
137
171
  (0, features_1.incrementCounter)('usage:usageInvariantZeroRepair');
172
+ trackInvariantRepair(e.id, 'usageCount', 'zero-default');
173
+ (0, logger_js_1.logWarn)(`[invariant-repair] usageCount defaulted to 0 for ${e.id} — no repair source found`);
138
174
  }
139
175
  // Repair missing lastUsedAt for entries with usage.
140
176
  function restoreLastUsedInvariant(e) {
@@ -143,17 +179,23 @@ function restoreLastUsedInvariant(e) {
143
179
  if (lastUsedAuthority[e.id]) {
144
180
  e.lastUsedAt = lastUsedAuthority[e.id];
145
181
  (0, features_1.incrementCounter)('usage:lastUsedAuthorityRepair');
182
+ trackInvariantRepair(e.id, 'lastUsedAt', 'authority');
183
+ (0, logger_js_1.logWarn)(`[invariant-repair] lastUsedAt restored from authority for ${e.id}`);
146
184
  return;
147
185
  }
148
186
  const snap = lastGoodUsageSnapshot[e.id];
149
187
  if (snap?.lastUsedAt) {
150
188
  e.lastUsedAt = snap.lastUsedAt;
151
189
  (0, features_1.incrementCounter)('usage:lastUsedSnapshotRepair');
190
+ trackInvariantRepair(e.id, 'lastUsedAt', 'snapshot');
191
+ (0, logger_js_1.logWarn)(`[invariant-repair] lastUsedAt restored from snapshot for ${e.id}`);
152
192
  return;
153
193
  }
154
194
  if ((e.usageCount ?? 0) > 0 && e.firstSeenTs) {
155
195
  e.lastUsedAt = e.firstSeenTs;
156
196
  (0, features_1.incrementCounter)('usage:lastUsedFirstSeenRepair');
197
+ trackInvariantRepair(e.id, 'lastUsedAt', 'firstSeen-approx');
198
+ (0, logger_js_1.logWarn)(`[invariant-repair] lastUsedAt approximated from firstSeenTs for ${e.id}`);
157
199
  }
158
200
  }
159
201
  // Rate limiting for usage increments (Phase 1 requirement)
@@ -214,8 +256,9 @@ function loadUsageSnapshot() {
214
256
  }
215
257
  break; // file not present – exit attempts
216
258
  }
217
- catch {
218
- // swallow and retry (tight loop – extremely rare path)
259
+ catch (err) {
260
+ // Log parse/read error and retry (tight loop – extremely rare path)
261
+ (0, logger_js_1.logWarn)(`[invariant-repair] loadUsageSnapshot attempt ${attempt} failed: ${err.message || String(err)}`);
219
262
  }
220
263
  }
221
264
  // Fallback to last good snapshot (prevents loss of firstSeenTs on rare parse race)
@@ -243,7 +286,7 @@ function flushUsageSnapshot() {
243
286
  for (const e of state.list) {
244
287
  const authoritative = e.firstSeenTs || firstSeenAuthority[e.id];
245
288
  if (authoritative && !firstSeenAuthority[e.id])
246
- firstSeenAuthority[e.id] = authoritative;
289
+ firstSeenAuthority[e.id] = authoritative; // lgtm[js/remote-property-injection] — id is regex-validated by instruction schema (^[a-z0-9](?:[a-z0-9-_]{0,118}[a-z0-9])?$) before reaching index
247
290
  if (e.usageCount || e.lastUsedAt || authoritative) {
248
291
  const rec = { usageCount: e.usageCount, firstSeenTs: authoritative, lastUsedAt: e.lastUsedAt };
249
292
  // Merge signal/comment/action from in-memory cache (last-write-wins from incrementUsage calls)
@@ -256,18 +299,18 @@ function flushUsageSnapshot() {
256
299
  if (cached.lastComment)
257
300
  rec.lastComment = cached.lastComment;
258
301
  }
259
- obj[e.id] = rec;
302
+ obj[e.id] = rec; // lgtm[js/remote-property-injection] — id is schema-validated before reaching index
260
303
  }
261
304
  }
262
305
  // Atomic write: write to temp then rename to avoid readers seeing partial JSON
263
306
  const snapPath = getUsageSnapshotPath();
264
307
  const tmp = snapPath + '.tmp';
265
- fs_1.default.writeFileSync(tmp, JSON.stringify(obj, null, 2));
308
+ fs_1.default.writeFileSync(tmp, JSON.stringify(obj, null, 2)); // lgtm[js/http-to-file-access] — snapPath is config-controlled usage snapshot path
266
309
  try {
267
310
  fs_1.default.renameSync(tmp, snapPath);
268
311
  }
269
312
  catch { /* fallback to direct write if rename fails */
270
- fs_1.default.writeFileSync(snapPath, JSON.stringify(obj, null, 2));
313
+ fs_1.default.writeFileSync(snapPath, JSON.stringify(obj, null, 2)); /* lgtm[js/http-to-file-access] — snapPath is config-controlled usage snapshot path */
271
314
  }
272
315
  lastGoodUsageSnapshot = obj; // update cache
273
316
  }
@@ -278,7 +321,7 @@ function flushUsageSnapshot() {
278
321
  // The guard ensures cleanup runs exactly once even if multiple signals race.
279
322
  try {
280
323
  // Import directly from shutdownGuard module (no circular dependency)
281
- // eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports
324
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
282
325
  const { createShutdownGuard: _createShutdownGuard } = require('../server/shutdownGuard');
283
326
  // Get or create a process-wide singleton via a global symbol
284
327
  const key = Symbol.for('mcp-shutdown-guard');
@@ -336,6 +379,7 @@ function getInstructionsDir() {
336
379
  }
337
380
  // Centralized tracing utilities
338
381
  const tracing_1 = require("./tracing");
382
+ const logger_js_1 = require("./logger.js");
339
383
  // Throttled file trace emission (avoid per-get amplification). We emit per-file decisions only
340
384
  // on true reloads AND if file signature changed OR time since last emission > threshold.
341
385
  // (legacy file-level trace removed in simplified loader)
@@ -394,6 +438,58 @@ function readVersionToken() { try {
394
438
  }
395
439
  catch { /* ignore */ } return ''; }
396
440
  function markindexDirty() { dirty = true; }
441
+ function syncTouchedVersionIntoState() {
442
+ try {
443
+ touchIndexVersion();
444
+ const vfMTime = (function () { try {
445
+ const vf = path_1.default.join(getInstructionsDir(), '.index-version');
446
+ if (fs_1.default.existsSync(vf)) {
447
+ return fs_1.default.statSync(vf).mtimeMs || 0;
448
+ }
449
+ }
450
+ catch { /* ignore */ } return 0; })();
451
+ const vfToken = (function () { try {
452
+ const vf = path_1.default.join(getInstructionsDir(), '.index-version');
453
+ if (fs_1.default.existsSync(vf)) {
454
+ return fs_1.default.readFileSync(vf, 'utf8').trim();
455
+ }
456
+ }
457
+ catch { /* ignore */ } return ''; })();
458
+ if (state) {
459
+ if (vfMTime && state.versionMTime !== vfMTime) {
460
+ state.versionMTime = vfMTime;
461
+ }
462
+ if (vfToken && state.versionToken !== vfToken) {
463
+ state.versionToken = vfToken;
464
+ }
465
+ }
466
+ }
467
+ catch { /* ignore */ }
468
+ }
469
+ function materializeWrittenEntry(record) {
470
+ if (state) {
471
+ const existing = state.byId.get(record.id);
472
+ if (existing) {
473
+ Object.assign(existing, record);
474
+ try {
475
+ (0, features_1.incrementCounter)('index:inMemoryUpdate');
476
+ }
477
+ catch { /* ignore */ }
478
+ }
479
+ else {
480
+ state.list.push(record);
481
+ state.byId.set(record.id, record);
482
+ try {
483
+ (0, features_1.incrementCounter)('index:inMemoryMaterialize');
484
+ }
485
+ catch { /* ignore */ }
486
+ }
487
+ syncTouchedVersionIntoState();
488
+ return;
489
+ }
490
+ markindexDirty();
491
+ syncTouchedVersionIntoState();
492
+ }
397
493
  function ensureLoaded() {
398
494
  const baseDir = getInstructionsDir();
399
495
  // Always reload if no state or dirty or version file changed.
@@ -416,12 +512,12 @@ function ensureLoaded() {
416
512
  const dbPath = (0, runtimeConfig_1.getRuntimeConfig)().storage?.sqlitePath ?? path_1.default.join(process.cwd(), 'data', 'index.db');
417
513
  const mr = (0, migrationEngine_1.migrateJsonToSqlite)(baseDir, dbPath);
418
514
  if (mr.migrated > 0) {
419
- console.log(`[storage] Auto-migrated ${mr.migrated} instruction(s) from JSON → SQLite`);
515
+ (0, logger_js_1.logInfo)(`[storage] Auto-migrated ${mr.migrated} instruction(s) from JSON → SQLite`);
420
516
  result = store.load();
421
517
  }
422
518
  }
423
519
  catch (err) {
424
- console.warn('[storage] Auto-migration failed:', err);
520
+ (0, logger_js_1.logWarn)('[storage] Auto-migration failed:', err);
425
521
  }
426
522
  }
427
523
  }
@@ -445,7 +541,54 @@ function ensureLoaded() {
445
541
  e.firstSeenTs = rec.firstSeenTs;
446
542
  if (!firstSeenAuthority[e.id])
447
543
  firstSeenAuthority[e.id] = rec.firstSeenTs;
448
- }
544
+ } // lgtm[js/remote-property-injection] — id is schema-validated before reaching index
545
+ if (!e.lastUsedAt && rec.lastUsedAt)
546
+ e.lastUsedAt = rec.lastUsedAt;
547
+ }
548
+ }
549
+ }
550
+ }
551
+ catch { /* ignore */ }
552
+ if ((0, tracing_1.traceEnabled)(1)) {
553
+ try {
554
+ (0, tracing_1.emitTrace)('[trace:ensureLoaded:simple-reload]', { dir: baseDir, count: state.list.length });
555
+ }
556
+ catch { /* ignore */ }
557
+ }
558
+ return state;
559
+ }
560
+ async function ensureLoadedAsync() {
561
+ const baseDir = getInstructionsDir();
562
+ const currentVersionMTime = readVersionMTime();
563
+ const currentVersionToken = readVersionToken();
564
+ if (state && !dirty) {
565
+ if (currentVersionMTime && currentVersionMTime === state.versionMTime && currentVersionToken === state.versionToken) {
566
+ return state;
567
+ }
568
+ }
569
+ const backend = (0, runtimeConfig_1.getRuntimeConfig)().storage?.backend ?? 'json';
570
+ if (backend === 'sqlite') {
571
+ return ensureLoaded();
572
+ }
573
+ const result = await new indexLoader_1.IndexLoader(baseDir).loadAsync();
574
+ const byId = new Map();
575
+ result.entries.forEach(e => byId.set(e.id, e));
576
+ const deduplicatedList = Array.from(byId.values());
577
+ state = { loadedAt: new Date().toISOString(), hash: result.hash, byId, list: deduplicatedList, fileCount: deduplicatedList.length, versionMTime: currentVersionMTime, versionToken: currentVersionToken, loadErrors: result.errors, loadDebug: result.debug, loadSummary: result.summary };
578
+ dirty = false;
579
+ try {
580
+ const snap = loadUsageSnapshot();
581
+ if (snap) {
582
+ for (const e of state.list) {
583
+ const rec = snap[e.id];
584
+ if (rec) {
585
+ if (e.usageCount == null && rec.usageCount != null)
586
+ e.usageCount = rec.usageCount;
587
+ if (!e.firstSeenTs && rec.firstSeenTs) {
588
+ e.firstSeenTs = rec.firstSeenTs;
589
+ if (!firstSeenAuthority[e.id])
590
+ firstSeenAuthority[e.id] = rec.firstSeenTs;
591
+ } // lgtm[js/remote-property-injection] — id is schema-validated before reaching index
449
592
  if (!e.lastUsedAt && rec.lastUsedAt)
450
593
  e.lastUsedAt = rec.lastUsedAt;
451
594
  }
@@ -561,6 +704,21 @@ function getIndexState() {
561
704
  }
562
705
  return st;
563
706
  }
707
+ async function getIndexStateAsync() {
708
+ const st = await ensureLoadedAsync();
709
+ for (const e of st.list) {
710
+ if (!e.firstSeenTs) {
711
+ restoreFirstSeenInvariant(e);
712
+ }
713
+ if (e.usageCount == null) {
714
+ restoreUsageInvariant(e);
715
+ }
716
+ if (e.lastUsedAt == null) {
717
+ restoreLastUsedInvariant(e);
718
+ }
719
+ }
720
+ return st;
721
+ }
564
722
  // Lightweight debug snapshot WITHOUT forcing a reload (observes current in-memory view vs disk)
565
723
  function getDebugIndexSnapshot() {
566
724
  const dir = getInstructionsDir();
@@ -658,10 +816,19 @@ function computeGovernanceHash(entries) {
658
816
  return h.digest('hex');
659
817
  }
660
818
  // Mutation helpers (import/add/remove/groom share)
661
- function writeEntry(entry) {
819
+ function isDuplicateInstructionWriteError(error) {
820
+ const code = error?.code;
821
+ if (code === 'EEXIST')
822
+ return true;
823
+ if (!(error instanceof Error))
824
+ return false;
825
+ const message = error.message.toLowerCase();
826
+ return message.includes('unique constraint failed') || message.includes('duplicate key');
827
+ }
828
+ function writeEntry(entry, opts) {
662
829
  const file = path_1.default.join(getInstructionsDir(), `${entry.id}.json`);
663
830
  const classifier = new classificationService_1.ClassificationService();
664
- const record = classifier.normalize(entry);
831
+ let record = classifier.normalize(entry);
665
832
  if (record.owner === 'unowned') {
666
833
  const auto = (0, ownershipService_1.resolveOwner)(record.id);
667
834
  if (auto) {
@@ -669,77 +836,96 @@ function writeEntry(entry) {
669
836
  record.updatedAt = new Date().toISOString();
670
837
  }
671
838
  }
839
+ record = (0, instructionRecordValidation_1.assertValidInstructionRecord)(record);
840
+ // Run the SAME migration the loader runs on read so the write path is
841
+ // symmetric with the read path. This brings legacy in-memory records
842
+ // (carrying old schemaVersion or missing v3+ defaults) up to current
843
+ // schema BEFORE validateForDisk gates them against the loader schema.
844
+ // Without this, callers passing legacy records would be silently rejected
845
+ // by the loader-symmetric validator even though the loader itself would
846
+ // have migrated them transparently.
847
+ (0, schemaVersion_1.migrateInstructionRecord)(record);
848
+ // Validate against the SAME JSON schema the loader uses at reload time.
849
+ // This prevents schema drift from silently dropping entries on reload.
850
+ const diskCheck = (0, loaderSchemaValidator_1.validateForDisk)(record);
851
+ if (!diskCheck.valid) {
852
+ const err = new Error(`Pre-write loader-schema validation failed for '${entry.id}': ${diskCheck.errors?.join('; ')}`);
853
+ err.validationErrors = diskCheck.errors;
854
+ err.isInstructionValidation = true;
855
+ throw err;
856
+ }
672
857
  const store = getStoreForDir(getInstructionsDir());
673
858
  if (store) {
674
- store.write(record);
859
+ store.write(record, opts);
860
+ }
861
+ else if (opts?.createOnly) {
862
+ (0, atomicFs_1.atomicCreateJson)(file, record);
675
863
  }
676
864
  else {
677
865
  (0, atomicFs_1.atomicWriteJson)(file, record);
678
866
  }
679
- // Revised mutation strategy (2025-09-14): Avoid setting dirty=true when we can
680
- // apply the change directly to the in-memory index. Previous implementation
681
- // marked the index dirty before an immediate getIndexState() call in tests,
682
- // forcing a reload that sometimes raced the Windows filesystem directory
683
- // visibility of the new file. That produced a flake where the opportunistic
684
- // materialization guarantee was lost. We now:
685
- // 1. Opportunistically materialize (add or update) the entry in-memory.
686
- // 2. Touch the version file so other processes/pollers observe the change.
687
- // 3. Only mark dirty if no state is currently loaded (so first subsequent
688
- // access triggers a load). Otherwise we keep the current state hot.
689
- if (state) {
690
- const existing = state.byId.get(record.id);
691
- if (existing) {
692
- // Update in-place so references (including any cached projections) see new fields.
693
- Object.assign(existing, record);
694
- try {
695
- (0, features_1.incrementCounter)('index:inMemoryUpdate');
867
+ // Post-write read-back: verify the file on disk passes the loader schema
868
+ if (!store) {
869
+ try {
870
+ const diskRaw = JSON.parse(fs_1.default.readFileSync(file, 'utf8'));
871
+ const postCheck = (0, loaderSchemaValidator_1.validateForDisk)(diskRaw);
872
+ if (!postCheck.valid) {
873
+ (0, logger_js_1.logWarn)(`[writeEntry] Post-write validation FAILED for '${entry.id}': ${postCheck.errors?.join('; ')}`);
696
874
  }
697
- catch { /* ignore */ }
698
875
  }
699
- else {
700
- state.list.push(record);
701
- state.byId.set(record.id, record);
702
- try {
703
- (0, features_1.incrementCounter)('index:inMemoryMaterialize');
704
- }
705
- catch { /* ignore */ }
876
+ catch (readErr) {
877
+ (0, logger_js_1.logWarn)(`[writeEntry] Post-write read-back failed for '${entry.id}': ${readErr.message}`);
706
878
  }
707
- // Signal externally. Then optimistically update in-memory version snapshot so getIndexState()
708
- // does NOT trigger an immediate reload (which can race directory enumeration on Windows).
709
- try {
710
- touchIndexVersion();
711
- // After touching, read back token + mtime to align with ensureLoaded's cache validation logic.
712
- const vfMTime = (function () { try {
713
- const vf = path_1.default.join(getInstructionsDir(), '.index-version');
714
- if (fs_1.default.existsSync(vf)) {
715
- return fs_1.default.statSync(vf).mtimeMs || 0;
716
- }
717
- }
718
- catch { /* ignore */ } return 0; })();
719
- const vfToken = (function () { try {
720
- const vf = path_1.default.join(getInstructionsDir(), '.index-version');
721
- if (fs_1.default.existsSync(vf)) {
722
- return fs_1.default.readFileSync(vf, 'utf8').trim();
723
- }
724
- }
725
- catch { /* ignore */ } return ''; })();
726
- if (vfMTime && state.versionMTime !== vfMTime) {
727
- state.versionMTime = vfMTime;
728
- }
729
- if (vfToken && state.versionToken !== vfToken) {
730
- state.versionToken = vfToken;
731
- }
879
+ }
880
+ materializeWrittenEntry(record);
881
+ }
882
+ async function writeEntryAsync(entry, opts) {
883
+ const file = path_1.default.join(getInstructionsDir(), `${entry.id}.json`);
884
+ const classifier = new classificationService_1.ClassificationService();
885
+ let record = classifier.normalize(entry);
886
+ if (record.owner === 'unowned') {
887
+ const auto = (0, ownershipService_1.resolveOwner)(record.id);
888
+ if (auto) {
889
+ record.owner = auto;
890
+ record.updatedAt = new Date().toISOString();
732
891
  }
733
- catch { /* ignore */ }
892
+ }
893
+ record = (0, instructionRecordValidation_1.assertValidInstructionRecord)(record);
894
+ // Mirror the loader's migration step before validating against the loader
895
+ // schema. See writeEntry for full rationale.
896
+ (0, schemaVersion_1.migrateInstructionRecord)(record);
897
+ // Validate against the SAME JSON schema the loader uses at reload time.
898
+ const diskCheck = (0, loaderSchemaValidator_1.validateForDisk)(record);
899
+ if (!diskCheck.valid) {
900
+ const err = new Error(`Pre-write loader-schema validation failed for '${entry.id}': ${diskCheck.errors?.join('; ')}`);
901
+ err.validationErrors = diskCheck.errors;
902
+ err.isInstructionValidation = true;
903
+ throw err;
904
+ }
905
+ const store = getStoreForDir(getInstructionsDir());
906
+ if (store) {
907
+ store.write(record, opts);
908
+ }
909
+ else if (opts?.createOnly) {
910
+ await (0, atomicFs_1.atomicCreateJsonAsync)(file, record);
734
911
  }
735
912
  else {
736
- // No in-memory state yet; next ensureLoaded should pick up new file.
737
- markindexDirty();
913
+ await (0, atomicFs_1.atomicWriteJsonAsync)(file, record);
914
+ }
915
+ // Post-write read-back: verify the file on disk passes the loader schema
916
+ if (!store) {
738
917
  try {
739
- touchIndexVersion();
918
+ const diskRaw = JSON.parse(fs_1.default.readFileSync(file, 'utf8'));
919
+ const postCheck = (0, loaderSchemaValidator_1.validateForDisk)(diskRaw);
920
+ if (!postCheck.valid) {
921
+ (0, logger_js_1.logWarn)(`[writeEntryAsync] Post-write validation FAILED for '${entry.id}': ${postCheck.errors?.join('; ')}`);
922
+ }
923
+ }
924
+ catch (readErr) {
925
+ (0, logger_js_1.logWarn)(`[writeEntryAsync] Post-write read-back failed for '${entry.id}': ${readErr.message}`);
740
926
  }
741
- catch { /* ignore */ }
742
927
  }
928
+ materializeWrittenEntry(record);
743
929
  }
744
930
  function removeEntry(id) {
745
931
  const store = getStoreForDir(getInstructionsDir());
@@ -866,12 +1052,12 @@ function incrementUsage(id, opts) {
866
1052
  // Atomically establish firstSeenTs if missing (avoid any window where undefined persists after increment)
867
1053
  if (!e.firstSeenTs) {
868
1054
  e.firstSeenTs = nowIso;
869
- ephemeralFirstSeen[e.id] = e.firstSeenTs; // track immediately for reload resilience
1055
+ ephemeralFirstSeen[e.id] = e.firstSeenTs; // track immediately for reload resilience // lgtm[js/remote-property-injection] — id is schema-validated before reaching index
870
1056
  firstSeenAuthority[e.id] = e.firstSeenTs;
871
- (0, features_1.incrementCounter)('usage:firstSeenAuthoritySet');
1057
+ (0, features_1.incrementCounter)('usage:firstSeenAuthoritySet'); // lgtm[js/remote-property-injection] — id is schema-validated before reaching index
872
1058
  }
873
1059
  e.lastUsedAt = nowIso; // always advance lastUsedAt on any increment
874
- lastUsedAuthority[e.id] = e.lastUsedAt;
1060
+ lastUsedAuthority[e.id] = e.lastUsedAt; // lgtm[js/remote-property-injection] — id is schema-validated before reaching index
875
1061
  // For the first usage we force a synchronous flush to guarantee persistence of firstSeenTs quickly;
876
1062
  // subsequent usages can rely on the debounce timer to coalesce writes.
877
1063
  if (e.usageCount <= 2) {
@@ -892,8 +1078,7 @@ function incrementUsage(id, opts) {
892
1078
  // Allow tests (or advanced operators) to disable the protective clamp logic for deterministic expectations.
893
1079
  // Setting INDEX_SERVER_DISABLE_USAGE_CLAMP=1 will let the anomalous >1 initial count pass through for diagnostic visibility.
894
1080
  if (!(0, runtimeConfig_1.getRuntimeConfig)().index.disableUsageClamp) {
895
- // eslint-disable-next-line no-console
896
- console.error('[incrementUsage] anomalous initial usageCount', e.usageCount, 'id', id);
1081
+ (0, logger_js_1.logError)('[incrementUsage] anomalous initial usageCount', { usageCount: e.usageCount, id });
897
1082
  // Clamp to 1 to enforce deterministic semantics for first observed increment. We intentionally
898
1083
  // retain lastUsedAt/firstSeenTs. This guards rare race producing flaky test expectations while
899
1084
  // preserving forward progress for subsequent increments (next call will yield 2).
@@ -969,13 +1154,13 @@ function __testResetUsageState() {
969
1154
  usageRateLimiter.clear();
970
1155
  lastGoodUsageSnapshot = {};
971
1156
  for (const k of Object.keys(ephemeralFirstSeen))
972
- delete ephemeralFirstSeen[k];
1157
+ delete ephemeralFirstSeen[k]; // lgtm[js/remote-property-injection] — k is own-key from internal object reset (test helper)
973
1158
  for (const k of Object.keys(firstSeenAuthority))
974
- delete firstSeenAuthority[k];
1159
+ delete firstSeenAuthority[k]; // lgtm[js/remote-property-injection] — k is own-key from internal object reset (test helper)
975
1160
  for (const k of Object.keys(usageAuthority))
976
- delete usageAuthority[k];
1161
+ delete usageAuthority[k]; // lgtm[js/remote-property-injection] — k is own-key from internal object reset (test helper)
977
1162
  for (const k of Object.keys(lastUsedAuthority))
978
- delete lastUsedAuthority[k];
1163
+ delete lastUsedAuthority[k]; // lgtm[js/remote-property-injection] — k is own-key from internal object reset (test helper)
979
1164
  if (state) {
980
1165
  for (const e of state.list) {
981
1166
  // Reset optional usage-related fields; preserve object identity.
@@ -39,6 +39,7 @@ export declare class IndexLoader {
39
39
  * to momentary locks while another process is atomically renaming/writing.
40
40
  */
41
41
  private readJsonWithRetry;
42
+ loadAsync(): Promise<IndexLoadResult>;
42
43
  load(): IndexLoadResult;
43
44
  private computeindexHash;
44
45
  }
@@ -19,6 +19,16 @@ const instruction_schema_json_1 = __importDefault(require("../../schemas/instruc
19
19
  const tracing_1 = require("./tracing");
20
20
  const runtimeConfig_1 = require("../config/runtimeConfig");
21
21
  const autoSplit_1 = require("./autoSplit");
22
+ const logger_js_1 = require("./logger.js");
23
+ function sleep(ms) {
24
+ return new Promise(resolve => setTimeout(resolve, ms));
25
+ }
26
+ function getRetryBackoffMs(baseBackoff, attempt) {
27
+ return baseBackoff * Math.pow(2, attempt - 1) + Math.floor(Math.random() * baseBackoff);
28
+ }
29
+ function isRetryableLoadError(error) {
30
+ return /empty file transient|unexpected end of json input|eprem|ebusy|eacces|enoent/i.test(error);
31
+ }
22
32
  class IndexLoader {
23
33
  baseDir;
24
34
  classifier;
@@ -32,9 +42,8 @@ class IndexLoader {
32
42
  * to momentary locks while another process is atomically renaming/writing.
33
43
  */
34
44
  readJsonWithRetry(file) {
35
- const { attempts, backoffMs } = (0, runtimeConfig_1.getRuntimeConfig)().index.readRetries;
45
+ const { attempts } = (0, runtimeConfig_1.getRuntimeConfig)().index.readRetries;
36
46
  const maxAttempts = Math.max(1, attempts);
37
- const baseBackoff = Math.max(1, backoffMs);
38
47
  let lastErr = null;
39
48
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
40
49
  try {
@@ -58,15 +67,26 @@ class IndexLoader {
58
67
  break;
59
68
  }
60
69
  lastErr = err;
61
- const sleep = baseBackoff * Math.pow(2, attempt - 1) + Math.floor(Math.random() * baseBackoff);
62
- const start = Date.now();
63
- while (Date.now() - start < sleep) { /* spin tiny backoff (< few ms) */ }
64
70
  }
65
71
  }
66
72
  if (lastErr)
67
73
  throw lastErr instanceof Error ? lastErr : new Error('readJsonWithRetry failed');
68
74
  return {}; // unreachable but satisfies typing
69
75
  }
76
+ async loadAsync() {
77
+ const { attempts, backoffMs } = (0, runtimeConfig_1.getRuntimeConfig)().index.readRetries;
78
+ const maxAttempts = Math.max(1, attempts);
79
+ const baseBackoff = Math.max(1, backoffMs);
80
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
81
+ const result = this.load();
82
+ const retryable = result.errors.length > 0 && result.errors.every(error => isRetryableLoadError(error.error));
83
+ if (!retryable || attempt === maxAttempts) {
84
+ return result;
85
+ }
86
+ await sleep(getRetryBackoffMs(baseBackoff, attempt));
87
+ }
88
+ return this.load();
89
+ }
70
90
  load() {
71
91
  const runtimeConfig = (0, runtimeConfig_1.getRuntimeConfig)();
72
92
  const IndexConfig = runtimeConfig.index;
@@ -126,7 +146,7 @@ class IndexLoader {
126
146
  }
127
147
  catch { /* ignore meta-schema registration issues */ }
128
148
  // Patch schema body maxLength from config before compiling (allows INDEX_SERVER_BODY_WARN_LENGTH override)
129
- const bodyMaxLen = IndexConfig.bodyWarnLength || 100000;
149
+ const bodyMaxLen = IndexConfig.bodyWarnLength || 50000;
130
150
  const autoSplit = IndexConfig.autoSplitOversized === true;
131
151
  const schemaCopy = JSON.parse(JSON.stringify(instruction_schema_json_1.default));
132
152
  try {
@@ -672,7 +692,7 @@ class IndexLoader {
672
692
  trace.push({ file: f, accepted: false, reason });
673
693
  // Log schema rejections at info level for operational visibility
674
694
  try {
675
- console.error(`[index:skip] ${f}: ${reason}`);
695
+ (0, logger_js_1.logError)(`[index:skip] ${f}: ${reason}`);
676
696
  }
677
697
  catch { /* ignore */ }
678
698
  if ((0, tracing_1.traceEnabled)(1)) {
@@ -698,7 +718,7 @@ class IndexLoader {
698
718
  if (trace)
699
719
  trace.push({ file: f, accepted: false, reason });
700
720
  try {
701
- console.error(`[index:skip] ${f}: classification: ${reason}`);
721
+ (0, logger_js_1.logError)(`[index:skip] ${f}: classification: ${reason}`);
702
722
  }
703
723
  catch { /* ignore */ }
704
724
  if ((0, tracing_1.traceEnabled)(1)) {