@jagilber-org/index-server 1.22.0 → 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 +83 -20
  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 +173 -54
  15. package/dist/dashboard/client/css/admin.css +151 -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 +25 -6
  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 +267 -82
  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 +4 -4
  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
@@ -18,6 +18,7 @@ exports.__resetGraphCache = __resetGraphCache;
18
18
  const registry_1 = require("../server/registry");
19
19
  const indexContext_1 = require("./indexContext");
20
20
  const runtimeConfig_1 = require("../config/runtimeConfig");
21
+ const logger_js_1 = require("./logger.js");
21
22
  // NOTE: For enriched responses we bump schema version to 2 (only when enrich=true)
22
23
  const GRAPH_SCHEMA_VERSION_V1 = 1;
23
24
  const GRAPH_SCHEMA_VERSION_V2 = 2;
@@ -216,8 +217,7 @@ function buildGraph(params, graphCfg = (0, runtimeConfig_1.getRuntimeConfig)().g
216
217
  const graph = buildGraph(p, graphCfg);
217
218
  // Defensive: unexpected undefined safeguard (should never happen). Emit diagnostic once.
218
219
  if (!graph) {
219
- // eslint-disable-next-line no-console
220
- console.error('[graph_export] buildGraph returned undefined - returning empty graph (diagnostic)');
220
+ (0, logger_js_1.logError)('[graph_export] buildGraph returned undefined - returning empty graph (diagnostic)');
221
221
  return { meta: { graphSchemaVersion: 1, nodeCount: 0, edgeCount: 0 }, nodes: [], edges: [] };
222
222
  }
223
223
  if (cacheEligible && !envExplicit) {
@@ -7,7 +7,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  * onboarding guidance so a naive / first-time agent can self-bootstrap:
8
8
  * - Discover tools & their purpose
9
9
  * - Understand local (P0) vs indexed (P1+) lifecycle & promotion workflow
10
- * - Learn safe mutation enabling pattern (INDEX_SERVER_MUTATION)
10
+ * - Learn safe mutation override pattern (INDEX_SERVER_MUTATION)
11
11
  * - Follow a deterministic promotion checklist
12
12
  * - Avoid governance/spec recursion (documents intentionally NOT ingested)
13
13
  */
@@ -64,7 +64,7 @@ function buildSections() {
64
64
  {
65
65
  id: 'mutation-safety',
66
66
  title: 'Safe Mutation',
67
- content: 'All write operations require INDEX_SERVER_MUTATION=1. Without it, mutation tools return disabled errors ensuring read-only safety by default.'
67
+ content: 'Write operations are enabled by default, but bootstrap confirmation and reference mode still gate risky changes. Set INDEX_SERVER_MUTATION=0 when you need an explicit read-only runtime.'
68
68
  },
69
69
  {
70
70
  id: 'recursion-safeguards',
@@ -48,6 +48,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
48
48
  const registry_1 = require("../server/registry");
49
49
  const fs = __importStar(require("fs"));
50
50
  const path = __importStar(require("path"));
51
+ const runtimeConfig_1 = require("../config/runtimeConfig");
51
52
  const SCHEMA_VERSION = '1.0.0';
52
53
  /**
53
54
  * Load the canonical instruction.schema.json from disk
@@ -158,11 +159,12 @@ function definePromotionWorkflow() {
158
159
  * Define key validation rules
159
160
  */
160
161
  function defineValidationRules() {
162
+ const bodyWarnLength = (0, runtimeConfig_1.getRuntimeConfig)().index.bodyWarnLength.toLocaleString('en-US');
161
163
  return [
162
164
  { field: 'id', rule: 'Pattern', constraint: '^[a-z0-9](?:[a-z0-9-_]{0,118}[a-z0-9])?$ (120 chars max, lowercase, no leading/trailing hyphen/underscore)' },
163
165
  { field: 'id', rule: 'Uniqueness', constraint: 'Must be unique across the index' },
164
166
  { field: 'title', rule: 'Length', constraint: '1-200 characters, non-empty' },
165
- { field: 'body', rule: 'Length', constraint: '1-20,000 characters, markdown supported' },
167
+ { field: 'body', rule: 'Length', constraint: `1-${bodyWarnLength} characters on add/import writes (INDEX_SERVER_BODY_WARN_LENGTH), markdown supported` },
166
168
  { field: 'priority', rule: 'Range', constraint: '1-100 (lower number = higher priority)' },
167
169
  { field: 'audience', rule: 'Enum', constraint: 'One of: individual, group, all' },
168
170
  { field: 'requirement', rule: 'Enum', constraint: 'One of: mandatory, critical, recommended, optional, deprecated' },
@@ -216,7 +218,7 @@ function defineValidationRules() {
216
218
  '1. Review the minimalExample for required fields',
217
219
  '2. Study promotionWorkflow stages (P0 → P1 → P2+)',
218
220
  '3. Validate against validationRules before submission',
219
- '4. Use index_dispatch {action: "add"} to create (requires INDEX_SERVER_MUTATION=1)',
221
+ '4. Use index_dispatch {action: "add"} to create (writes are enabled by default; set INDEX_SERVER_MUTATION=0 for read-only mode)',
220
222
  '5. Monitor index_health for recursionRisk and drift',
221
223
  '6. Track usage with usage_track',
222
224
  '7. Iterate via index_dispatch {action: "update"} as needed',
@@ -9,27 +9,47 @@ const path_1 = __importDefault(require("path"));
9
9
  const registry_1 = require("../server/registry");
10
10
  const indexContext_1 = require("./indexContext");
11
11
  const features_1 = require("./features");
12
- (0, registry_1.registerHandler)('integrity_verify', () => { const st = (0, indexContext_1.ensureLoaded)(); const issues = []; for (const e of st.list) {
13
- const actual = crypto_1.default.createHash('sha256').update(e.body, 'utf8').digest('hex');
14
- if (actual !== e.sourceHash)
15
- issues.push({ id: e.id, expected: e.sourceHash, actual });
16
- } return { hash: st.hash, count: st.list.length, issues, issueCount: issues.length }; });
17
- (0, registry_1.registerHandler)('integrity_manifest', () => { const manifestPath = path_1.default.join(process.cwd(), 'snapshots', 'index-manifest.json'); if (!fs_1.default.existsSync(manifestPath))
18
- return { manifest: 'missing' }; let manifest; try {
19
- manifest = JSON.parse(fs_1.default.readFileSync(manifestPath, 'utf8'));
20
- }
21
- catch (e) {
22
- return { manifest: 'invalid', error: e instanceof Error ? e.message : String(e) };
23
- } const entries = Array.isArray(manifest.entries) ? manifest.entries : []; const map = new Map(entries.map(e => [e.id, e])); const st = (0, indexContext_1.ensureLoaded)(); const drift = []; for (const e of st.list) {
24
- const entry = map.get(e.id);
25
- const bodyHash = crypto_1.default.createHash('sha256').update(e.body, 'utf8').digest('hex');
26
- if (!entry)
27
- drift.push({ id: e.id, change: 'added' });
28
- else if (entry.sourceHash !== e.sourceHash || entry.bodyHash !== bodyHash)
29
- drift.push({ id: e.id, change: 'hash-mismatch' });
30
- } for (const id of map.keys()) {
31
- if (!st.byId.has(id))
32
- drift.push({ id, change: 'removed' });
33
- } return { manifest: 'present', drift: drift.length, details: drift }; });
12
+ (0, registry_1.registerHandler)('integrity_verify', () => {
13
+ const st = (0, indexContext_1.ensureLoaded)();
14
+ const issues = [];
15
+ for (const e of st.list) {
16
+ const actual = crypto_1.default.createHash('sha256').update(e.body, 'utf8').digest('hex');
17
+ if (actual !== e.sourceHash) {
18
+ issues.push({ id: e.id, expected: e.sourceHash, actual });
19
+ }
20
+ }
21
+ return { hash: st.hash, count: st.list.length, issues, issueCount: issues.length };
22
+ });
23
+ (0, registry_1.registerHandler)('integrity_manifest', () => {
24
+ const manifestPath = path_1.default.join(process.cwd(), 'snapshots', 'index-manifest.json');
25
+ if (!fs_1.default.existsSync(manifestPath))
26
+ return { manifest: 'missing' };
27
+ let manifest;
28
+ try {
29
+ manifest = JSON.parse(fs_1.default.readFileSync(manifestPath, 'utf8'));
30
+ }
31
+ catch (e) {
32
+ return { manifest: 'invalid', error: e instanceof Error ? e.message : String(e) };
33
+ }
34
+ const entries = Array.isArray(manifest.entries) ? manifest.entries : [];
35
+ const map = new Map(entries.map(e => [e.id, e]));
36
+ const st = (0, indexContext_1.ensureLoaded)();
37
+ const drift = [];
38
+ for (const e of st.list) {
39
+ const entry = map.get(e.id);
40
+ const bodyHash = crypto_1.default.createHash('sha256').update(e.body, 'utf8').digest('hex');
41
+ if (!entry) {
42
+ drift.push({ id: e.id, change: 'added' });
43
+ }
44
+ else if (entry.sourceHash !== e.sourceHash || entry.bodyHash !== bodyHash) {
45
+ drift.push({ id: e.id, change: 'hash-mismatch' });
46
+ }
47
+ }
48
+ for (const id of map.keys()) {
49
+ if (!st.byId.has(id))
50
+ drift.push({ id, change: 'removed' });
51
+ }
52
+ return { manifest: 'present', drift: drift.length, details: drift };
53
+ });
34
54
  // Phase 0: feature flags status
35
55
  (0, registry_1.registerHandler)('feature_status', () => (0, features_1.featureStatus)());
@@ -138,7 +138,7 @@ function _resetMailbox() {
138
138
  (0, registry_1.registerHandler)('messaging_purge', (params = {}) => {
139
139
  const mailbox = getMailbox();
140
140
  let removed;
141
- let action = 'purge_all';
141
+ let action;
142
142
  if (params.all) {
143
143
  removed = mailbox.purgeAll();
144
144
  action = 'purge_all';
@@ -10,7 +10,22 @@ const InstanceManager_1 = require("../dashboard/server/InstanceManager");
10
10
  const fs_1 = __importDefault(require("fs"));
11
11
  const path_1 = __importDefault(require("path"));
12
12
  const validationService_1 = require("./validationService");
13
- (0, registry_1.registerHandler)('metrics_snapshot', () => { const raw = (0, registry_1.getMetricsRaw)(); const methods = Object.entries(raw).map(([method, rec]) => ({ method, count: rec.count, avgMs: rec.count ? +(rec.totalMs / rec.count).toFixed(2) : 0, maxMs: +rec.maxMs.toFixed(2) })).sort((a, b) => a.method.localeCompare(b.method)); const features = (0, features_1.featureStatus)(); const validation = (0, validationService_1.getValidationMetrics)(); return { generatedAt: new Date().toISOString(), methods, features, validation }; });
13
+ const auditLog_1 = require("./auditLog");
14
+ const features_2 = require("./features");
15
+ (0, registry_1.registerHandler)('metrics_snapshot', () => {
16
+ const raw = (0, registry_1.getMetricsRaw)();
17
+ const methods = Object.entries(raw)
18
+ .map(([method, rec]) => ({
19
+ method,
20
+ count: rec.count,
21
+ avgMs: rec.count ? +(rec.totalMs / rec.count).toFixed(2) : 0,
22
+ maxMs: +rec.maxMs.toFixed(2),
23
+ }))
24
+ .sort((a, b) => a.method.localeCompare(b.method));
25
+ const features = (0, features_1.featureStatus)();
26
+ const validation = (0, validationService_1.getValidationMetrics)();
27
+ return { generatedAt: new Date().toISOString(), methods, features, validation };
28
+ });
14
29
  // health_check retained here (meta_tools provided by shim for rich output)
15
30
  // Resolve version locally (mirrors transport logic) to avoid import cycles
16
31
  let VERSION = '0.0.0';
@@ -23,21 +38,51 @@ try {
23
38
  }
24
39
  }
25
40
  catch { /* ignore */ }
26
- (0, registry_1.registerHandler)('health_check', () => {
41
+ (0, registry_1.registerHandler)('health_check', async () => {
27
42
  let summary;
28
43
  try {
29
- const st = (0, indexContext_1.getIndexState)();
44
+ const st = await (0, indexContext_1.getIndexStateAsync)();
30
45
  if (st.loadSummary) {
31
46
  const s = st.loadSummary;
32
- summary = { scanned: s.scanned, accepted: s.accepted, skipped: s.skipped, reasons: s.reasons, salvage: s.salvage, softWarnings: s.softWarnings };
47
+ summary = {
48
+ scanned: s.scanned,
49
+ accepted: s.accepted,
50
+ skipped: s.skipped,
51
+ reasons: s.reasons,
52
+ salvage: s.salvage,
53
+ softWarnings: s.softWarnings,
54
+ };
33
55
  }
34
56
  }
35
57
  catch { /* swallow to keep health resilient */ }
36
58
  // Instance discovery — resilient: never fail health due to port-file read errors
37
59
  let instances = [];
38
60
  try {
39
- instances = (0, InstanceManager_1.getActiveInstances)().map(i => ({ pid: i.pid, port: i.port, host: i.host, startedAt: i.startedAt, current: i.current }));
61
+ instances = (0, InstanceManager_1.getActiveInstances)().map(i => ({
62
+ pid: i.pid,
63
+ port: i.port,
64
+ host: i.host,
65
+ startedAt: i.startedAt,
66
+ current: i.current,
67
+ }));
40
68
  }
41
69
  catch { /* swallow */ }
42
- return { status: 'ok', timestamp: new Date().toISOString(), version: VERSION, pid: process.pid, uptime: Math.round(process.uptime()), index: summary, instances };
70
+ // Expose usage invariant repair counters so operators can see silent data reconstruction
71
+ const allCounters = (0, features_2.getCounters)();
72
+ const repairCounters = {};
73
+ for (const [key, val] of Object.entries(allCounters)) {
74
+ if (key.startsWith('usage:') && val > 0)
75
+ repairCounters[key] = val;
76
+ }
77
+ return {
78
+ status: 'ok',
79
+ timestamp: new Date().toISOString(),
80
+ version: VERSION,
81
+ pid: process.pid,
82
+ uptime: Math.round(process.uptime()),
83
+ index: summary,
84
+ instances,
85
+ audit: (0, auditLog_1.getAuditLogHealth)(),
86
+ ...(Object.keys(repairCounters).length ? { usageRepairs: repairCounters } : {}),
87
+ };
43
88
  });
@@ -3,5 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const registry_1 = require("../server/registry");
4
4
  const promptReviewService_1 = require("./promptReviewService");
5
5
  const promptService = new promptReviewService_1.PromptReviewService();
6
- (0, registry_1.registerHandler)('prompt_review', (p) => { const raw = p.prompt || ''; const MAX = 10_000; if (raw.length > MAX)
7
- return { truncated: true, message: 'prompt too large', max: MAX }; const sanitized = raw.replace(/\0/g, ''); const issues = promptService.review(sanitized); const summary = (0, promptReviewService_1.summarizeIssues)(issues); return { issues, summary, length: sanitized.length }; });
6
+ (0, registry_1.registerHandler)('prompt_review', (p) => {
7
+ const raw = p.prompt || '';
8
+ const MAX = 10_000;
9
+ if (raw.length > MAX)
10
+ return { truncated: true, message: 'prompt too large', max: MAX };
11
+ const sanitized = raw.replace(/\0/g, '');
12
+ const issues = promptService.review(sanitized);
13
+ const summary = (0, promptReviewService_1.summarizeIssues)(issues);
14
+ return { issues, summary, length: sanitized.length };
15
+ });
@@ -19,6 +19,9 @@
19
19
  * - Proper tool registration
20
20
  * - Input sanitization and limits
21
21
  */
22
+ var __importDefault = (this && this.__importDefault) || function (mod) {
23
+ return (mod && mod.__esModule) ? mod : { "default": mod };
24
+ };
22
25
  Object.defineProperty(exports, "__esModule", { value: true });
23
26
  exports.handleInstructionsSearch = handleInstructionsSearch;
24
27
  exports.buildAfterRetrievalMeta = buildAfterRetrievalMeta;
@@ -28,6 +31,7 @@ const indexContext_1 = require("./indexContext");
28
31
  const errors_1 = require("./errors");
29
32
  const runtimeConfig_1 = require("../config/runtimeConfig");
30
33
  const embeddingService_1 = require("./embeddingService");
34
+ const safe_regex2_1 = __importDefault(require("safe-regex2"));
31
35
  const SEARCH_SCHEMA = {
32
36
  type: 'object',
33
37
  required: ['keywords'],
@@ -42,6 +46,7 @@ const SEARCH_SCHEMA = {
42
46
  example: { keywords: ['build', 'validate', 'discipline'], limit: 10, includeCategories: true }
43
47
  };
44
48
  const VALID_MODES = ['keyword', 'regex', 'semantic'];
49
+ const MAX_REGEX_PATTERN_LENGTH = 200;
45
50
  function normalizeSearchText(text, caseSensitive) {
46
51
  const normalized = text
47
52
  .normalize('NFD')
@@ -123,18 +128,32 @@ function buildKeywordSearchContext(instructions, keywords, caseSensitive, includ
123
128
  /**
124
129
  * Calculate relevance score for an instruction based on keyword matches
125
130
  */
126
- function calculateRelevance(instruction, keywords, caseSensitive, includeCategories, mode = 'keyword', keywordContext) {
131
+ function calculateRelevance(instruction, keywords, caseSensitive, includeCategories, mode = 'keyword', keywordContext, regexKeywords) {
127
132
  let score = 0;
128
133
  const matchedFieldSet = new Set();
129
134
  const prepareText = (text) => caseSensitive ? text : text.toLowerCase();
130
135
  const preparedKeywords = keywords.map(k => mode === 'keyword' ? prepareText(k) : k);
131
136
  const regexFlags = caseSensitive ? 'g' : 'gi';
137
+ const compiledRegexKeywords = mode === 'regex'
138
+ ? (regexKeywords ?? keywords.map((keyword) => {
139
+ // Defense-in-depth: production callers route through compileRegexKeywords →
140
+ // validateRegexKeyword. Re-validate here in the fallback path so direct
141
+ // invocations (tests, future handlers) cannot bypass ReDoS / unsupported-
142
+ // construct rejection. Throws on invalid pattern.
143
+ return {
144
+ source: keyword,
145
+ testRegex: compileSafeUserRegex(keyword, caseSensitive ? '' : 'i'),
146
+ countRegex: compileSafeUserRegex(keyword, regexFlags),
147
+ };
148
+ }))
149
+ : [];
132
150
  // Build matchers: regex mode uses raw patterns, keyword mode uses substring/escaped regex
133
151
  const testMatch = (text, keyword) => {
134
152
  if (mode === 'regex') {
135
153
  try {
136
- // Regex patterns are pre-validated in the handler; try-catch is defense-in-depth
137
- return new RegExp(keyword, caseSensitive ? '' : 'i').test(text); // lgtm[js/regex-injection] — patterns pre-validated at handler entry
154
+ const compiled = keyword;
155
+ compiled.testRegex.lastIndex = 0;
156
+ return compiled.testRegex.test(text); // lgtm[js/regex-injection] — patterns pre-validated and pre-compiled at handler entry
138
157
  }
139
158
  catch {
140
159
  return false;
@@ -228,7 +247,9 @@ function calculateRelevance(instruction, keywords, caseSensitive, includeCategor
228
247
  const countMatches = (text, keyword) => {
229
248
  if (mode === 'regex') {
230
249
  try {
231
- return (text.match(new RegExp(keyword, regexFlags)) || []).length; // lgtm[js/regex-injection] — patterns pre-validated at handler entry
250
+ const compiled = keyword;
251
+ compiled.countRegex.lastIndex = 0;
252
+ return (text.match(compiled.countRegex) || []).length; // lgtm[js/regex-injection] — patterns pre-validated and pre-compiled at handler entry
232
253
  }
233
254
  catch {
234
255
  return 0;
@@ -237,7 +258,7 @@ function calculateRelevance(instruction, keywords, caseSensitive, includeCategor
237
258
  return (text.match(new RegExp(escapeRegex(keyword), regexFlags)) || []).length;
238
259
  };
239
260
  let idMatches = 0;
240
- for (const keyword of preparedKeywords) {
261
+ for (const keyword of mode === 'regex' ? compiledRegexKeywords : preparedKeywords) {
241
262
  if (testMatch(instruction.id, keyword)) {
242
263
  idMatches += countMatches(instruction.id, keyword);
243
264
  }
@@ -247,7 +268,7 @@ function calculateRelevance(instruction, keywords, caseSensitive, includeCategor
247
268
  matchedFieldSet.add('id');
248
269
  }
249
270
  let titleMatches = 0;
250
- for (const keyword of preparedKeywords) {
271
+ for (const keyword of mode === 'regex' ? compiledRegexKeywords : preparedKeywords) {
251
272
  titleMatches += countMatches(instruction.title, keyword);
252
273
  }
253
274
  if (titleMatches > 0) {
@@ -255,7 +276,7 @@ function calculateRelevance(instruction, keywords, caseSensitive, includeCategor
255
276
  matchedFieldSet.add('title');
256
277
  }
257
278
  let summaryMatches = 0;
258
- for (const keyword of preparedKeywords) {
279
+ for (const keyword of mode === 'regex' ? compiledRegexKeywords : preparedKeywords) {
259
280
  summaryMatches += countMatches(instruction.semanticSummary || '', keyword);
260
281
  }
261
282
  if (summaryMatches > 0) {
@@ -263,7 +284,7 @@ function calculateRelevance(instruction, keywords, caseSensitive, includeCategor
263
284
  matchedFieldSet.add('semanticSummary');
264
285
  }
265
286
  let bodyMatches = 0;
266
- for (const keyword of preparedKeywords) {
287
+ for (const keyword of mode === 'regex' ? compiledRegexKeywords : preparedKeywords) {
267
288
  bodyMatches += countMatches(instruction.body, keyword);
268
289
  }
269
290
  if (bodyMatches > 0) {
@@ -273,7 +294,7 @@ function calculateRelevance(instruction, keywords, caseSensitive, includeCategor
273
294
  if (includeCategories && instruction.categories?.length) {
274
295
  const categoryText = instruction.categories.join(' ');
275
296
  let categoryMatches = 0;
276
- for (const keyword of preparedKeywords) {
297
+ for (const keyword of mode === 'regex' ? compiledRegexKeywords : preparedKeywords) {
277
298
  categoryMatches += countMatches(categoryText, keyword);
278
299
  }
279
300
  if (categoryMatches > 0) {
@@ -282,13 +303,14 @@ function calculateRelevance(instruction, keywords, caseSensitive, includeCategor
282
303
  }
283
304
  }
284
305
  const uniqueMatches = new Set();
285
- for (const keyword of preparedKeywords) {
306
+ for (const keyword of mode === 'regex' ? compiledRegexKeywords : preparedKeywords) {
307
+ const keywordSource = mode === 'regex' ? keyword.source : keyword;
286
308
  if (testMatch(instruction.id, keyword) ||
287
309
  testMatch(instruction.title, keyword) ||
288
310
  testMatch(instruction.semanticSummary || '', keyword) ||
289
311
  testMatch(instruction.body, keyword) ||
290
312
  (includeCategories && instruction.categories?.some((cat) => testMatch(cat, keyword)))) {
291
- uniqueMatches.add(keyword);
313
+ uniqueMatches.add(keywordSource);
292
314
  }
293
315
  }
294
316
  if (uniqueMatches.size > 1) {
@@ -302,6 +324,58 @@ function calculateRelevance(instruction, keywords, caseSensitive, includeCategor
302
324
  function escapeRegex(string) {
303
325
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
304
326
  }
327
+ function sanitizeKeywords(keywords) {
328
+ return keywords
329
+ .filter(k => typeof k === 'string' && k.trim().length > 0)
330
+ .map(k => k.trim())
331
+ .slice(0, 10);
332
+ }
333
+ function validateRegexKeyword(keyword) {
334
+ if (keyword.length > MAX_REGEX_PATTERN_LENGTH) {
335
+ throw new Error(`Regex patterns must not exceed ${MAX_REGEX_PATTERN_LENGTH} characters to prevent ReDoS`);
336
+ }
337
+ try {
338
+ new RegExp(keyword); // lgtm[js/regex-injection] — this IS the syntax validation step
339
+ }
340
+ catch {
341
+ throw new Error(`Invalid regex pattern "${keyword}": check syntax and try again`);
342
+ }
343
+ if (/\(\?(?:[=!]|<[=!])/.test(keyword)) {
344
+ throw new Error('Regex pattern rejected: lookaround assertions are not supported in regex search mode');
345
+ }
346
+ if (/\\[1-9]/.test(keyword)) {
347
+ throw new Error('Regex pattern rejected: backreferences are not supported in regex search mode');
348
+ }
349
+ if (/\([^)]*[+*?}]\)[+*?{]/.test(keyword)) {
350
+ throw new Error('Regex pattern rejected: nested quantifiers can cause catastrophic backtracking');
351
+ }
352
+ if (/\)[+*?}][^(]*\)[+*?{]/.test(keyword)) {
353
+ throw new Error('Regex pattern rejected: nested quantifiers can cause catastrophic backtracking');
354
+ }
355
+ if (/\([^)]*\|[^)]*\)[+*?{]/.test(keyword)) {
356
+ throw new Error('Regex pattern rejected: alternation with quantifiers can cause catastrophic backtracking');
357
+ }
358
+ if (!(0, safe_regex2_1.default)(keyword)) {
359
+ throw new Error('Regex pattern rejected: potentially catastrophic backtracking detected');
360
+ }
361
+ }
362
+ /**
363
+ * Compile a user-supplied regex pattern after running ReDoS / unsupported-
364
+ * construct validation. This is the single trusted construction site for
365
+ * `new RegExp(<user input>)` in the search pipeline; all callers must route
366
+ * through here so the validation step is provably adjacent to construction.
367
+ */
368
+ function compileSafeUserRegex(pattern, flags) {
369
+ validateRegexKeyword(pattern);
370
+ return new RegExp(pattern, flags); // lgtm[js/regex-injection] — pattern validated by validateRegexKeyword above
371
+ }
372
+ function compileRegexKeywords(keywords, caseSensitive) {
373
+ return keywords.map((keyword) => ({
374
+ source: keyword,
375
+ testRegex: compileSafeUserRegex(keyword, caseSensitive ? '' : 'i'),
376
+ countRegex: compileSafeUserRegex(keyword, caseSensitive ? 'g' : 'gi'),
377
+ }));
378
+ }
305
379
  /**
306
380
  * Load and search instructions from the index
307
381
  */
@@ -325,10 +399,7 @@ function performSearch(params) {
325
399
  throw new Error(`Invalid contentType: must be one of ${validContentTypes.join(', ')}`);
326
400
  }
327
401
  // Validate and sanitize keywords
328
- const sanitizedKeywords = keywords
329
- .filter(k => typeof k === 'string' && k.trim().length > 0)
330
- .map(k => k.trim())
331
- .slice(0, 10); // Enforce max 10 keywords
402
+ const sanitizedKeywords = sanitizeKeywords(keywords);
332
403
  if (sanitizedKeywords.length === 0) {
333
404
  throw new Error('At least one valid keyword is required');
334
405
  }
@@ -351,7 +422,7 @@ function performSearch(params) {
351
422
  continue; // Skip instructions that don't match the contentType filter
352
423
  }
353
424
  }
354
- const { score, matchedFields } = calculateRelevance(instruction, sanitizedKeywords, caseSensitive, includeCategories, mode, keywordContext);
425
+ const { score, matchedFields } = calculateRelevance(instruction, sanitizedKeywords, caseSensitive, includeCategories, mode, keywordContext, params.compiledRegexKeywords);
355
426
  if (score > 0) {
356
427
  results.push({
357
428
  instructionId: instruction.id,
@@ -496,33 +567,11 @@ async function handleInstructionsSearch(params) {
496
567
  throw new Error(`Invalid mode: must be one of ${VALID_MODES.join(', ')}`);
497
568
  }
498
569
  (0, logger_1.logInfo)(`[search] Search request: mode=${mode}, keywords=[${params.keywords.join(', ')}], limit=${params.limit ?? 50}, contentType=${params.contentType ?? 'any'}`);
570
+ const sanitizedKeywords = sanitizeKeywords(params.keywords);
499
571
  // Regex mode validation: pattern safety checks
500
- if (mode === 'regex') {
501
- for (const keyword of params.keywords) {
502
- if (keyword.length > 200) {
503
- throw new Error('Regex patterns must not exceed 200 characters to prevent ReDoS');
504
- }
505
- // Reject patterns with nested quantifiers that cause catastrophic backtracking
506
- if (/\([^)]*[+*}]\)[+*{]/.test(keyword)) {
507
- throw new Error('Regex pattern rejected: nested quantifiers can cause catastrophic backtracking');
508
- }
509
- // Also catch double-nested quantified groups like ((a)+)+ where inner )+
510
- // is followed by more chars and another )+
511
- if (/\)[+*}][^(]*\)[+*{]/.test(keyword)) {
512
- throw new Error('Regex pattern rejected: nested quantifiers can cause catastrophic backtracking');
513
- }
514
- // Reject overlapping alternations with quantifiers: (a|a)+
515
- if (/\([^)]*\|[^)]*\)[+*]{1,}/.test(keyword)) {
516
- throw new Error('Regex pattern rejected: alternation with quantifiers can cause catastrophic backtracking');
517
- }
518
- try {
519
- new RegExp(keyword); // lgtm[js/regex-injection] — this IS the validation code
520
- }
521
- catch {
522
- throw new Error(`Invalid regex pattern "${keyword}": check syntax and try again`);
523
- }
524
- }
525
- }
572
+ const compiledRegexKeywords = mode === 'regex'
573
+ ? compileRegexKeywords(sanitizedKeywords, params.caseSensitive ?? false)
574
+ : undefined;
526
575
  // Semantic mode: check feature flag
527
576
  if (mode === 'semantic') {
528
577
  const cfg = (0, runtimeConfig_1.getRuntimeConfig)().semantic;
@@ -534,12 +583,13 @@ async function handleInstructionsSearch(params) {
534
583
  }
535
584
  // Ensure case-insensitive search by default
536
585
  const searchParams = {
537
- keywords: params.keywords,
586
+ keywords: sanitizedKeywords,
538
587
  mode: mode,
539
588
  limit: params.limit,
540
589
  includeCategories: params.includeCategories,
541
590
  caseSensitive: params.caseSensitive ?? false, // Explicit default to false for case-insensitive search
542
- contentType: params.contentType
591
+ contentType: params.contentType,
592
+ compiledRegexKeywords,
543
593
  };
544
594
  // Semantic mode: embedding-based similarity search
545
595
  if (mode === 'semantic') {
@@ -30,6 +30,6 @@ const fs_1 = __importDefault(require("fs"));
30
30
  if (raw && Array.isArray(raw.records))
31
31
  size = raw.records.length;
32
32
  }
33
- catch { /* ignore */ }
33
+ catch { /* ignore */ } // lgtm[js/file-system-race] — file path containment to cwd validated above
34
34
  return { dumped: true, file, records: size, bytes, env: (0, tracing_1.summarizeTraceEnv)(), bufferEnabled: (0, tracing_1.getTraceBuffer)().length > 0 };
35
35
  });
@@ -2,10 +2,41 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const registry_1 = require("../server/registry");
4
4
  const indexContext_1 = require("./indexContext");
5
- (0, registry_1.registerHandler)('usage_track', (p) => { if (!p.id)
6
- return { error: 'missing id' }; const opts = { action: p.action, signal: p.signal, comment: p.comment }; const r = (0, indexContext_1.incrementUsage)(p.id, opts); if (!r)
7
- return { notFound: true }; return r; });
8
- (0, registry_1.registerHandler)('usage_hotset', (p) => { const st = (0, indexContext_1.ensureLoaded)(); const snap = (0, indexContext_1.loadUsageSnapshot)(); const limit = Math.max(1, Math.min(p.limit ?? 10, 100)); const items = [...st.list].filter(e => (e.usageCount ?? 0) > 0).sort((a, b) => { const ua = a.usageCount ?? 0, ub = b.usageCount ?? 0; if (ub !== ua)
9
- return ub - ua; return (b.lastUsedAt || '').localeCompare(a.lastUsedAt || ''); }).slice(0, limit).map(e => { const rec = snap[e.id] || {}; const item = { id: e.id, usageCount: e.usageCount, lastUsedAt: e.lastUsedAt }; if (rec.lastSignal)
10
- item.lastSignal = rec.lastSignal; if (rec.lastComment)
11
- item.lastComment = rec.lastComment; return item; }); return { hash: st.hash, count: items.length, items, limit }; });
5
+ (0, registry_1.registerHandler)('usage_track', (p) => {
6
+ if (!p.id)
7
+ return { error: 'missing id' };
8
+ const opts = { action: p.action, signal: p.signal, comment: p.comment };
9
+ const r = (0, indexContext_1.incrementUsage)(p.id, opts);
10
+ if (!r)
11
+ return { notFound: true };
12
+ return r;
13
+ });
14
+ (0, registry_1.registerHandler)('usage_hotset', (p) => {
15
+ const st = (0, indexContext_1.ensureLoaded)();
16
+ const snap = (0, indexContext_1.loadUsageSnapshot)();
17
+ const limit = Math.max(1, Math.min(p.limit ?? 10, 100));
18
+ const items = [...st.list]
19
+ .filter(e => (e.usageCount ?? 0) > 0)
20
+ .sort((a, b) => {
21
+ const ua = a.usageCount ?? 0;
22
+ const ub = b.usageCount ?? 0;
23
+ if (ub !== ua)
24
+ return ub - ua;
25
+ return (b.lastUsedAt || '').localeCompare(a.lastUsedAt || '');
26
+ })
27
+ .slice(0, limit)
28
+ .map(e => {
29
+ const rec = snap[e.id] || {};
30
+ const item = {
31
+ id: e.id,
32
+ usageCount: e.usageCount,
33
+ lastUsedAt: e.lastUsedAt,
34
+ };
35
+ if (rec.lastSignal)
36
+ item.lastSignal = rec.lastSignal;
37
+ if (rec.lastComment)
38
+ item.lastComment = rec.lastComment;
39
+ return item;
40
+ });
41
+ return { hash: st.hash, count: items.length, items, limit };
42
+ });
@@ -43,6 +43,17 @@ export interface UsageTrackOptions {
43
43
  signal?: string;
44
44
  comment?: string;
45
45
  }
46
+ declare const invariantRepairLog: {
47
+ ts: string;
48
+ id: string;
49
+ field: string;
50
+ source: string;
51
+ }[];
52
+ /** Returns a summary of invariant repairs for health check visibility. */
53
+ export declare function getInvariantRepairSummary(): {
54
+ totalRepairs: number;
55
+ recentRepairs: typeof invariantRepairLog;
56
+ };
46
57
  export declare function clearUsageRateLimit(id?: string): void;
47
58
  export declare function loadUsageSnapshot(): Record<string, UsagePersistRecord>;
48
59
  export declare function getInstructionsDir(): string;
@@ -55,6 +66,7 @@ export declare function diagnoseInstructionsDir(): {
55
66
  export declare function touchIndexVersion(): void;
56
67
  export declare function markindexDirty(): void;
57
68
  export declare function ensureLoaded(): IndexState;
69
+ export declare function ensureLoadedAsync(): Promise<IndexState>;
58
70
  export interface IndexPollerOptions {
59
71
  intervalMs?: number;
60
72
  proactive?: boolean;
@@ -63,6 +75,7 @@ export declare function startIndexVersionPoller(opts?: IndexPollerOptions): void
63
75
  export declare function stopIndexVersionPoller(): void;
64
76
  export declare function invalidate(): void;
65
77
  export declare function getIndexState(): IndexState;
78
+ export declare function getIndexStateAsync(): Promise<IndexState>;
66
79
  export declare function getDebugIndexSnapshot(): {
67
80
  dir: string;
68
81
  fileCountOnDisk: number;
@@ -110,8 +123,15 @@ export declare function projectGovernance(e: InstructionEntry): {
110
123
  changeLogLength: number;
111
124
  };
112
125
  export declare function computeGovernanceHash(entries: InstructionEntry[]): string;
113
- export declare function writeEntry(entry: InstructionEntry): void;
126
+ export declare function isDuplicateInstructionWriteError(error: unknown): boolean;
127
+ export declare function writeEntry(entry: InstructionEntry, opts?: {
128
+ createOnly?: boolean;
129
+ }): void;
130
+ export declare function writeEntryAsync(entry: InstructionEntry, opts?: {
131
+ createOnly?: boolean;
132
+ }): Promise<void>;
114
133
  export declare function removeEntry(id: string): void;
115
134
  export declare function scheduleUsagePersist(): void;
116
135
  export declare function incrementUsage(id: string, opts?: UsageTrackOptions): Record<string, unknown> | null;
117
136
  export declare function __testResetUsageState(): void;
137
+ export {};