fix: expose SONA stats and normalize ESM exports

This commit is contained in:
Dragan Spiridonov
2026-08-14 12:31:22 +00:00
parent fc5ba6b0d6
commit 0affb3e7a7
11 changed files with 417 additions and 17 deletions
+2 -1
View File
@@ -59,7 +59,8 @@
"scripts": {
"preinstall": "node scripts/preinstall.cjs || true",
"postinstall": "node scripts/postinstall.cjs",
"build": "tsc && npm run build:cli && npm run build:mcp",
"build": "tsc && npm run build:esm && npm run build:cli && npm run build:mcp",
"build:esm": "node scripts/fix-esm-specifiers.mjs",
"build:cli": "node scripts/build-cli.mjs",
"build:mcp": "node scripts/build-mcp.mjs",
"prepublishOnly": "node scripts/sync-agents.cjs && node scripts/prepare-assets.cjs",
+96
View File
@@ -0,0 +1,96 @@
#!/usr/bin/env node
/**
* Normalize relative ESM specifiers in TypeScript's emitted JavaScript.
*
* The source tree historically uses bundler-style extensionless imports. The
* CLI/MCP bundles resolve those, but package exports point at raw `dist/` ESM,
* where Node requires an explicit file or directory index. This post-emit pass
* keeps source churn contained while making the published artifact valid ESM.
*/
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const SPECIFIER_RE = /(from\s*|import\s*\(\s*|import\s+)(['"])(\.{1,2}\/[^'"\n]+)\2/g;
const ALIAS_SPECIFIER_RE = /(from\s*|import\s*\(\s*|import\s+)(['"])(@(shared|kernel|domains|coordination|adapters|integrations)\/[^'"\n]+)\2/g;
export function resolveRelativeSpecifier(importerPath, specifier) {
const suffixIndex = specifier.search(/[?#]/);
const cleanSpecifier = suffixIndex === -1 ? specifier : specifier.slice(0, suffixIndex);
const suffix = suffixIndex === -1 ? '' : specifier.slice(suffixIndex);
if (/\.(?:[cm]?js|json|node)$/.test(cleanSpecifier)) return specifier;
const resolved = path.resolve(path.dirname(importerPath), cleanSpecifier);
if (fs.existsSync(`${resolved}.js`)) return `${cleanSpecifier}.js${suffix}`;
if (fs.existsSync(path.join(resolved, 'index.js'))) return `${cleanSpecifier}/index.js${suffix}`;
if (fs.existsSync(`${resolved}.json`)) return `${cleanSpecifier}.json${suffix}`;
return specifier;
}
export function normalizeEmittedESM(distDir) {
const files = listJavaScriptFiles(distDir);
let rewrittenFiles = 0;
let rewrittenSpecifiers = 0;
for (const file of files) {
const original = fs.readFileSync(file, 'utf8');
let normalized = original.replace(
SPECIFIER_RE,
(match, prefix, quote, specifier) => {
const resolved = resolveRelativeSpecifier(file, specifier);
if (resolved === specifier) return match;
rewrittenSpecifiers++;
return `${prefix}${quote}${resolved}${quote}`;
},
);
normalized = normalized.replace(
ALIAS_SPECIFIER_RE,
(match, prefix, quote, specifier, alias) => {
const target = path.join(distDir, alias, specifier.slice(alias.length + 2));
const targetExists = fs.existsSync(target)
|| fs.existsSync(`${target}.js`)
|| fs.existsSync(path.join(target, 'index.js'))
|| fs.existsSync(`${target}.json`);
if (!targetExists) return match;
let relative = path.relative(path.dirname(file), target).split(path.sep).join('/');
if (!relative.startsWith('.')) relative = `./${relative}`;
const resolved = resolveRelativeSpecifier(file, relative);
rewrittenSpecifiers++;
return `${prefix}${quote}${resolved}${quote}`;
},
);
if (normalized !== original) {
fs.writeFileSync(file, normalized);
rewrittenFiles++;
}
}
return { scannedFiles: files.length, rewrittenFiles, rewrittenSpecifiers };
}
function listJavaScriptFiles(root) {
if (!fs.existsSync(root)) return [];
const files = [];
const pending = [root];
while (pending.length > 0) {
const current = pending.pop();
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
const resolved = path.join(current, entry.name);
if (entry.isDirectory()) pending.push(resolved);
else if (entry.isFile() && entry.name.endsWith('.js')) files.push(resolved);
}
}
return files;
}
const invokedPath = process.argv[1] ? path.resolve(process.argv[1]) : '';
if (invokedPath === fileURLToPath(import.meta.url)) {
const distDir = path.resolve(process.argv[2] ?? 'dist');
const result = normalizeEmittedESM(distDir);
console.log(
`Normalized ESM specifiers: ${result.rewrittenSpecifiers} in `
+ `${result.rewrittenFiles}/${result.scannedFiles} emitted files`,
);
}
@@ -55,6 +55,7 @@ import {
import {
PersistentSONAEngine,
createPersistentSONAEngine,
type PersistedSONAStats,
} from '../../integrations/ruvector/sona-persistence.js';
import {
type QESONAPattern,
@@ -802,6 +803,13 @@ export class LearningOptimizationCoordinator
return this.sona!.getStats();
}
/** Return persisted SONA aggregates without mutating or reconstructing the store. */
async getSONAPersistedStats(): Promise<PersistedSONAStats> {
this.ensureInitialized();
this.ensureSONAAvailable();
return this.sona!.getPersistedStats();
}
/**
* Get all QESONA patterns.
*
@@ -484,6 +484,9 @@ export interface ILearningOptimizationCoordinator {
*/
getLearningDashboard(): Promise<Result<LearningDashboard>>;
/** Return read-only statistics from the persisted SONA pattern store. */
getSONAPersistedStats(): Promise<import('../../integrations/ruvector/sona-persistence.js').PersistedSONAStats>;
/**
* Export learned models
*/
@@ -48,6 +48,7 @@ import {
ProductionIntelService,
ProductionIntelConfig,
} from './services/index.js';
import type { PersistedSONAStats } from '../../integrations/ruvector/sona-persistence.js';
/**
* Plugin configuration options
@@ -67,6 +68,8 @@ export interface LearningOptimizationAPI {
// Availability check
/** Check if SONA pattern engine is available (may be false if init failed) */
isSONAAvailable(): boolean;
/** Read-only persisted SONA statistics; null when SONA initialization failed. */
getSONAPersistedStats(): Promise<PersistedSONAStats | null>;
// Coordinator methods
runLearningCycle(domain: DomainName): Promise<Result<LearningCycleReport>>;
@@ -184,6 +187,10 @@ export class LearningOptimizationPlugin extends BaseDomainPlugin {
const api: LearningOptimizationExtendedAPI = {
// Availability check
isSONAAvailable: () => this.coordinator?.isSONAAvailable() ?? false,
getSONAPersistedStats: async () => {
if (!this.coordinator?.isSONAAvailable()) return null;
return this.coordinator.getSONAPersistedStats();
},
// Coordinator methods
runLearningCycle: this.runLearningCycle.bind(this),
+19 -16
View File
@@ -81,7 +81,7 @@ export const SONA_PATTERNS_SCHEMA = `
/**
* SONA Fisher matrices table schema - stores EWC++ state per domain
*/
export const SONA_FISHER_SCHEMA = `
export const SONA_FISHER_SCHEMA = `
-- SONA Fisher Information Matrices (Task 2.2: EWC++ Persistence)
CREATE TABLE IF NOT EXISTS sona_fisher_matrices (
domain TEXT PRIMARY KEY,
@@ -96,9 +96,22 @@ export const SONA_FISHER_SCHEMA = `
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now'))
);
`;
// ============================================================================
`;
/** Read-only aggregate over the persisted SONA pattern store. */
export interface PersistedSONAStats {
totalPatterns: number;
uniqueTypes: number;
uniqueDomains: number;
avgConfidence: number;
avgUsage: number;
totalSuccesses: number;
totalFailures: number;
byType: Record<string, number>;
byDomain: Record<string, number>;
}
// ============================================================================
// Configuration
// ============================================================================
@@ -196,7 +209,7 @@ interface SONAPatternRow {
*
* Optionally integrates with RuVector server for cross-process pattern sharing.
*/
export class PersistentSONAEngine {
export class PersistentSONAEngine {
private readonly baseEngine: QESONA;
private readonly config: Required<PersistentSONAConfig>;
private persistence: UnifiedPersistenceManager | null = null;
@@ -955,17 +968,7 @@ export class PersistentSONAEngine {
/**
* Get persisted statistics from SQLite
*/
async getPersistedStats(): Promise<{
totalPatterns: number;
uniqueTypes: number;
uniqueDomains: number;
avgConfidence: number;
avgUsage: number;
totalSuccesses: number;
totalFailures: number;
byType: Record<string, number>;
byDomain: Record<string, number>;
}> {
async getPersistedStats(): Promise<PersistedSONAStats> {
this.ensureInitialized();
const statsStmt = this.prepared.get('getStats');
@@ -26,6 +26,8 @@ import {
OptimizationObjective as DomainObjective,
Constraint,
} from '../../../domains/learning-optimization/interfaces.js';
import type { LearningOptimizationAPI } from '../../../domains/learning-optimization/plugin.js';
import type { PersistedSONAStats } from '../../../integrations/ruvector/sona-persistence.js';
// ============================================================================
// Types
@@ -107,6 +109,8 @@ export interface PatternResult {
topPatterns: LearnedPattern[];
avgConfidence: number;
avgSuccessRate: number;
/** Read-only persisted SONA visibility when an initialized fleet is available. */
sona?: SONASnapshot;
}
export interface DashboardResult {
@@ -117,6 +121,13 @@ export interface DashboardResult {
topPerformingDomains: DomainName[];
learningTrend: TrendPoint[];
recentMilestones: Milestone[];
/** Read-only persisted SONA visibility when an initialized fleet is available. */
sona?: SONASnapshot;
}
export interface SONASnapshot {
available: boolean;
stats: PersistedSONAStats | null;
}
export interface TrendPoint {
@@ -522,6 +533,7 @@ export class LearningOptimizeTool extends MCPToolBase<LearningOptimizeParams, Le
context: MCPToolContext
): Promise<PatternResult> {
const { learningCoordinator } = await this.getServices(context);
const sona = await this.getSONASnapshot(context);
this.emitStream(context, {
status: 'analyzing',
@@ -540,6 +552,7 @@ export class LearningOptimizeTool extends MCPToolBase<LearningOptimizeParams, Le
topPatterns: [],
avgConfidence: 0,
avgSuccessRate: 0,
sona,
};
}
@@ -563,11 +576,13 @@ export class LearningOptimizeTool extends MCPToolBase<LearningOptimizeParams, Le
topPatterns,
avgConfidence: stats.avgConfidence,
avgSuccessRate: stats.avgSuccessRate,
sona,
};
}
private async executeDashboard(context: MCPToolContext): Promise<DashboardResult> {
const { learningCoordinator, transferSpecialist } = await this.getServices(context);
const sona = await this.getSONASnapshot(context);
this.emitStream(context, {
status: 'aggregating',
@@ -665,8 +680,25 @@ export class LearningOptimizeTool extends MCPToolBase<LearningOptimizeParams, Le
topPerformingDomains: topPerformingDomains.length > 0 ? topPerformingDomains : ['test-generation'],
learningTrend,
recentMilestones,
sona,
};
}
/** Read SONA through the initialized domain API; never construct a second engine here. */
private async getSONASnapshot(context: MCPToolContext): Promise<SONASnapshot> {
const api = context.kernel?.getDomainAPI<LearningOptimizationAPI>('learning-optimization');
if (!api?.isSONAAvailable()) return { available: false, stats: null };
try {
return { available: true, stats: await api.getSONAPersistedStats() };
} catch (error) {
this.logger.warn('Failed to read persisted SONA statistics', {
requestId: context.requestId,
error: toErrorMessage(error),
});
return { available: false, stats: null };
}
}
}
// ============================================================================
@@ -0,0 +1,70 @@
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { execFileSync, spawnSync } from 'node:child_process';
import fs from 'node:fs';
import path from 'node:path';
import { pathToFileURL } from 'node:url';
describe('packed package native ESM contracts', () => {
const packageRoot = process.cwd();
const tempRoot = path.join(packageRoot, 'node_modules', '.cache', `aqe-pack-esm-${process.pid}`);
const unpackedRoot = path.join(tempRoot, 'package');
let packageJson: {
exports: Record<string, { import?: string }>;
};
beforeAll(() => {
// Arrange a real packed artifact under this checkout so its dependencies
// resolve from the existing node_modules tree without network access.
fs.rmSync(tempRoot, { recursive: true, force: true });
fs.mkdirSync(tempRoot, { recursive: true });
const packOutput = execFileSync(
'npm',
['pack', '--json', '--ignore-scripts', '--pack-destination', tempRoot],
{ cwd: packageRoot, encoding: 'utf8' },
);
const [{ filename }] = JSON.parse(packOutput) as Array<{ filename: string }>;
execFileSync('tar', ['-xzf', path.join(tempRoot, filename), '-C', tempRoot]);
packageJson = JSON.parse(fs.readFileSync(path.join(unpackedRoot, 'package.json'), 'utf8'));
}, 30_000);
afterAll(() => {
fs.rmSync(tempRoot, { recursive: true, force: true });
});
it('should_importEveryDeclaredExport_when_loadedByNativeNodeESM', () => {
for (const [exportName, target] of Object.entries(packageJson.exports)) {
if (!target.import) continue;
expectNativeImport(target.import, `package export ${exportName}`, exportName === './cli');
}
}, 120_000);
it('should_importRuVectorBarrel_when_loadedByNativeNodeESM', () => {
expectNativeImport('./dist/integrations/ruvector/index.js', 'RuVector barrel');
});
it('should_importCoordinatorGNN_when_loadedByNativeNodeESM', () => {
expectNativeImport(
'./dist/domains/code-intelligence/coordinator-gnn.js',
'code-intelligence coordinator GNN',
);
});
function expectNativeImport(
relativeTarget: string,
label: string,
executableEntry = false,
): void {
const url = pathToFileURL(path.resolve(unpackedRoot, relativeTarget)).href;
const result = spawnSync(
process.execPath,
[
'--input-type=module',
'--eval',
`await import(${JSON.stringify(url)});`,
...(executableEntry ? ['--', '--version'] : []),
],
{ cwd: unpackedRoot, encoding: 'utf8', timeout: 30_000 },
);
expect(result.status, `${label}: ${result.stderr || result.stdout}`).toBe(0);
}
});
@@ -32,6 +32,17 @@ vi.mock('../../../../src/integrations/ruvector/sona-persistence', () => ({
createPattern: vi.fn(),
adaptPattern: vi.fn().mockResolvedValue({ patterns: [], adapted: false }),
getStats: vi.fn().mockReturnValue({ totalPatterns: 0, typeBreakdown: {}, domainBreakdown: {} }),
getPersistedStats: vi.fn().mockResolvedValue({
totalPatterns: 3,
uniqueTypes: 2,
uniqueDomains: 2,
avgConfidence: 0.8,
avgUsage: 4,
totalSuccesses: 7,
totalFailures: 1,
byType: { successful: 2, optimization: 1 },
byDomain: { 'test-generation': 2, 'quality-assessment': 1 },
}),
getAllPatterns: vi.fn().mockReturnValue([]),
getPatternsByType: vi.fn().mockReturnValue([]),
getPatternsByDomain: vi.fn().mockReturnValue([]),
@@ -178,6 +189,19 @@ describe('LearningOptimizationPlugin', () => {
expect(api).toHaveProperty('importModels');
});
it('should_returnPersistedSONAStats_when_engineIsAvailable', async () => {
// Arrange
const api = plugin.getAPI<{
getSONAPersistedStats: () => Promise<{ totalPatterns: number } | null>;
}>();
// Act
const stats = await api.getSONAPersistedStats();
// Assert
expect(stats?.totalPatterns).toBe(3);
});
it('should return API with pattern learning methods', () => {
const api = plugin.getAPI<Record<string, unknown>>();
@@ -0,0 +1,66 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { LearningOptimizeTool } from '../../../../src/mcp/tools/learning-optimization/optimize.js';
import type { MemoryBackend, QEKernel } from '../../../../src/kernel/interfaces.js';
const PERSISTED_STATS = {
totalPatterns: 5,
uniqueTypes: 2,
uniqueDomains: 2,
avgConfidence: 0.82,
avgUsage: 3.4,
totalSuccesses: 9,
totalFailures: 2,
byType: { successful: 4, optimization: 1 },
byDomain: { 'test-generation': 3, 'quality-assessment': 2 },
};
describe('LearningOptimizeTool SONA visibility', () => {
let tool: LearningOptimizeTool;
let memory: MemoryBackend;
let kernel: QEKernel;
const getSONAPersistedStats = vi.fn();
beforeEach(() => {
tool = new LearningOptimizeTool();
getSONAPersistedStats.mockReset().mockResolvedValue(PERSISTED_STATS);
memory = {
get: vi.fn().mockResolvedValue(null),
set: vi.fn().mockResolvedValue(undefined),
delete: vi.fn().mockResolvedValue(undefined),
search: vi.fn().mockResolvedValue([]),
list: vi.fn().mockResolvedValue([]),
initialize: vi.fn().mockResolvedValue(undefined),
dispose: vi.fn().mockResolvedValue(undefined),
} as unknown as MemoryBackend;
kernel = {
getDomainAPI: vi.fn().mockReturnValue({
isSONAAvailable: () => true,
getSONAPersistedStats,
}),
} as unknown as QEKernel;
});
it('should_includeReadOnlySONAStats_when_patternsActionUsesInitializedKernel', async () => {
// Act
const result = await tool.invoke({ action: 'patterns' }, { memory, kernel });
// Assert
expect(result.data?.patternResult?.sona).toEqual({ available: true, stats: PERSISTED_STATS });
});
it('should_includeReadOnlySONAStats_when_dashboardActionUsesInitializedKernel', async () => {
// Act
const result = await tool.invoke({ action: 'dashboard' }, { memory, kernel });
// Assert
expect(result.data?.dashboardResult?.sona).toEqual({ available: true, stats: PERSISTED_STATS });
});
it('should_reportSONAUnavailable_when_fleetKernelIsAbsent', async () => {
// Act
const result = await tool.invoke({ action: 'patterns' }, { memory });
// Assert
expect(result.data?.patternResult?.sona).toEqual({ available: false, stats: null });
});
});
@@ -0,0 +1,90 @@
import { afterEach, describe, expect, it } from 'vitest';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { normalizeEmittedESM } from '../../../scripts/fix-esm-specifiers.mjs';
describe('normalizeEmittedESM', () => {
const tempDirs: string[] = [];
afterEach(() => {
for (const dir of tempDirs.splice(0)) fs.rmSync(dir, { recursive: true, force: true });
});
it('should_addExplicitTargets_when_emittedImportsAreExtensionless', () => {
// Arrange
const distDir = fs.mkdtempSync(path.join(os.tmpdir(), 'aqe-esm-specifiers-'));
tempDirs.push(distDir);
fs.mkdirSync(path.join(distDir, 'feature'));
fs.writeFileSync(path.join(distDir, 'dep.js'), 'export const dep = true;\n');
fs.writeFileSync(path.join(distDir, 'feature', 'index.js'), 'export const feature = true;\n');
fs.writeFileSync(
path.join(distDir, 'index.js'),
"export { dep } from './dep';\nexport { feature } from './feature';\n",
);
// Act
normalizeEmittedESM(distDir);
// Assert
expect(fs.readFileSync(path.join(distDir, 'index.js'), 'utf8')).toBe(
"export { dep } from './dep.js';\nexport { feature } from './feature/index.js';\n",
);
});
it('should_preserveSpecifier_when_itAlreadyHasAnExtension', () => {
// Arrange
const distDir = fs.mkdtempSync(path.join(os.tmpdir(), 'aqe-esm-specifiers-'));
tempDirs.push(distDir);
fs.writeFileSync(path.join(distDir, 'dep.js'), 'export const dep = true;\n');
fs.writeFileSync(path.join(distDir, 'index.js'), "export { dep } from './dep.js';\n");
// Act
normalizeEmittedESM(distDir);
// Assert
expect(fs.readFileSync(path.join(distDir, 'index.js'), 'utf8')).toBe(
"export { dep } from './dep.js';\n",
);
});
it('should_appendRuntimeExtension_when_sourceBasenameContainsDots', () => {
// Arrange
const distDir = fs.mkdtempSync(path.join(os.tmpdir(), 'aqe-esm-specifiers-'));
tempDirs.push(distDir);
fs.writeFileSync(path.join(distDir, 'e2e-step.types.js'), 'export const step = true;\n');
fs.writeFileSync(
path.join(distDir, 'index.js'),
"export { step } from './e2e-step.types';\n",
);
// Act
normalizeEmittedESM(distDir);
// Assert
expect(fs.readFileSync(path.join(distDir, 'index.js'), 'utf8')).toBe(
"export { step } from './e2e-step.types.js';\n",
);
});
it('should_rewriteTypeScriptPathAliases_toRelativeRuntimeTargets', () => {
// Arrange
const distDir = fs.mkdtempSync(path.join(os.tmpdir(), 'aqe-esm-specifiers-'));
tempDirs.push(distDir);
fs.mkdirSync(path.join(distDir, 'shared'));
fs.mkdirSync(path.join(distDir, 'domains', 'feature'), { recursive: true });
fs.writeFileSync(path.join(distDir, 'shared', 'error-utils.js'), 'export const ok = true;\n');
fs.writeFileSync(
path.join(distDir, 'domains', 'feature', 'index.js'),
"export { ok } from '@shared/error-utils.js';\n",
);
// Act
normalizeEmittedESM(distDir);
// Assert
expect(fs.readFileSync(path.join(distDir, 'domains', 'feature', 'index.js'), 'utf8')).toBe(
"export { ok } from '../../shared/error-utils.js';\n",
);
});
});