agentic-qe 3.7.21 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/helpers/brain-checkpoint.cjs +4 -1
- package/.claude/helpers/statusline-v3.cjs +3 -1
- package/.claude/skills/skills-manifest.json +1 -1
- package/CHANGELOG.md +45 -0
- package/README.md +2 -14
- package/assets/helpers/statusline-v3.cjs +3 -1
- package/dist/cli/brain-commands.js +6 -10
- package/dist/cli/bundle.js +7441 -4327
- package/dist/cli/commands/audit.d.ts +43 -0
- package/dist/cli/commands/audit.js +125 -0
- package/dist/cli/commands/hooks.js +29 -6
- package/dist/cli/commands/init.js +1 -73
- package/dist/cli/commands/learning.js +270 -13
- package/dist/cli/commands/ruvector-commands.d.ts +15 -0
- package/dist/cli/commands/ruvector-commands.js +271 -0
- package/dist/cli/handlers/init-handler.d.ts +0 -1
- package/dist/cli/handlers/init-handler.js +0 -6
- package/dist/cli/index.js +4 -2
- package/dist/context/sources/defect-source.js +2 -2
- package/dist/context/sources/memory-source.js +2 -2
- package/dist/context/sources/requirements-source.js +2 -2
- package/dist/coordination/behavior-tree/decorators.d.ts +108 -0
- package/dist/coordination/behavior-tree/decorators.js +251 -0
- package/dist/coordination/behavior-tree/index.d.ts +12 -0
- package/dist/coordination/behavior-tree/index.js +15 -0
- package/dist/coordination/behavior-tree/nodes.d.ts +165 -0
- package/dist/coordination/behavior-tree/nodes.js +338 -0
- package/dist/coordination/behavior-tree/qe-trees.d.ts +105 -0
- package/dist/coordination/behavior-tree/qe-trees.js +181 -0
- package/dist/coordination/coherence-action-gate.d.ts +284 -0
- package/dist/coordination/coherence-action-gate.js +512 -0
- package/dist/coordination/index.d.ts +4 -0
- package/dist/coordination/index.js +8 -0
- package/dist/coordination/reasoning-qec.d.ts +315 -0
- package/dist/coordination/reasoning-qec.js +585 -0
- package/dist/coordination/task-executor.d.ts +16 -0
- package/dist/coordination/task-executor.js +99 -0
- package/dist/coordination/workflow-orchestrator.d.ts +29 -0
- package/dist/coordination/workflow-orchestrator.js +42 -0
- package/dist/domains/visual-accessibility/cnn-visual-regression.d.ts +135 -0
- package/dist/domains/visual-accessibility/cnn-visual-regression.js +327 -0
- package/dist/domains/visual-accessibility/index.d.ts +1 -0
- package/dist/domains/visual-accessibility/index.js +4 -0
- package/dist/governance/coherence-validator.d.ts +112 -0
- package/dist/governance/coherence-validator.js +180 -0
- package/dist/governance/index.d.ts +1 -0
- package/dist/governance/index.js +2 -0
- package/dist/governance/witness-chain.d.ts +311 -0
- package/dist/governance/witness-chain.js +509 -0
- package/dist/init/index.d.ts +0 -2
- package/dist/init/index.js +0 -1
- package/dist/init/init-wizard-steps.d.ts +10 -0
- package/dist/init/init-wizard-steps.js +87 -1
- package/dist/init/init-wizard.d.ts +1 -9
- package/dist/init/init-wizard.js +3 -69
- package/dist/init/orchestrator.js +0 -1
- package/dist/init/phases/01-detection.js +0 -27
- package/dist/init/phases/07-hooks.js +6 -4
- package/dist/init/phases/phase-interface.d.ts +0 -1
- package/dist/init/settings-merge.js +1 -1
- package/dist/integrations/browser/qe-dashboard/clustering.d.ts +48 -0
- package/dist/integrations/browser/qe-dashboard/clustering.js +183 -0
- package/dist/integrations/browser/qe-dashboard/index.d.ts +12 -0
- package/dist/integrations/browser/qe-dashboard/index.js +15 -0
- package/dist/integrations/browser/qe-dashboard/pattern-explorer.d.ts +165 -0
- package/dist/integrations/browser/qe-dashboard/pattern-explorer.js +260 -0
- package/dist/integrations/browser/qe-dashboard/wasm-vector-store.d.ts +144 -0
- package/dist/integrations/browser/qe-dashboard/wasm-vector-store.js +277 -0
- package/dist/integrations/ruvector/cognitive-container-codec.d.ts +51 -0
- package/dist/integrations/ruvector/cognitive-container-codec.js +180 -0
- package/dist/integrations/ruvector/cognitive-container.d.ts +125 -0
- package/dist/integrations/ruvector/cognitive-container.js +306 -0
- package/dist/integrations/ruvector/coherence-gate.d.ts +309 -0
- package/dist/integrations/ruvector/coherence-gate.js +631 -0
- package/dist/integrations/ruvector/compressed-hnsw-integration.d.ts +176 -0
- package/dist/integrations/ruvector/compressed-hnsw-integration.js +301 -0
- package/dist/integrations/ruvector/dither-adapter.d.ts +122 -0
- package/dist/integrations/ruvector/dither-adapter.js +295 -0
- package/dist/integrations/ruvector/domain-transfer.d.ts +129 -0
- package/dist/integrations/ruvector/domain-transfer.js +220 -0
- package/dist/integrations/ruvector/feature-flags.d.ts +214 -2
- package/dist/integrations/ruvector/feature-flags.js +167 -2
- package/dist/integrations/ruvector/filter-adapter.d.ts +71 -0
- package/dist/integrations/ruvector/filter-adapter.js +285 -0
- package/dist/integrations/ruvector/gnn-wrapper.d.ts +20 -0
- package/dist/integrations/ruvector/gnn-wrapper.js +40 -0
- package/dist/integrations/ruvector/hnsw-health-monitor.d.ts +237 -0
- package/dist/integrations/ruvector/hnsw-health-monitor.js +394 -0
- package/dist/integrations/ruvector/index.d.ts +8 -2
- package/dist/integrations/ruvector/index.js +18 -2
- package/dist/integrations/ruvector/interfaces.d.ts +40 -0
- package/dist/integrations/ruvector/sona-persistence.d.ts +54 -0
- package/dist/integrations/ruvector/sona-persistence.js +162 -0
- package/dist/integrations/ruvector/sona-three-loop.d.ts +392 -0
- package/dist/integrations/ruvector/sona-three-loop.js +814 -0
- package/dist/integrations/ruvector/sona-wrapper.d.ts +97 -0
- package/dist/integrations/ruvector/sona-wrapper.js +147 -3
- package/dist/integrations/ruvector/spectral-math.d.ts +101 -0
- package/dist/integrations/ruvector/spectral-math.js +254 -0
- package/dist/integrations/ruvector/temporal-compression.d.ts +163 -0
- package/dist/integrations/ruvector/temporal-compression.js +318 -0
- package/dist/integrations/ruvector/thompson-sampler.d.ts +61 -0
- package/dist/integrations/ruvector/thompson-sampler.js +118 -0
- package/dist/integrations/ruvector/transfer-coherence-stub.d.ts +80 -0
- package/dist/integrations/ruvector/transfer-coherence-stub.js +63 -0
- package/dist/integrations/ruvector/transfer-verification.d.ts +119 -0
- package/dist/integrations/ruvector/transfer-verification.js +115 -0
- package/dist/kernel/hnsw-adapter.d.ts +52 -1
- package/dist/kernel/hnsw-adapter.js +139 -4
- package/dist/kernel/hnsw-index-provider.d.ts +5 -0
- package/dist/kernel/native-hnsw-backend.d.ts +110 -0
- package/dist/kernel/native-hnsw-backend.js +408 -0
- package/dist/kernel/unified-memory.js +5 -6
- package/dist/learning/aqe-learning-engine.d.ts +2 -0
- package/dist/learning/aqe-learning-engine.js +65 -0
- package/dist/learning/experience-capture-middleware.js +20 -0
- package/dist/learning/experience-capture.d.ts +10 -0
- package/dist/learning/experience-capture.js +34 -0
- package/dist/learning/index.d.ts +2 -2
- package/dist/learning/index.js +4 -4
- package/dist/learning/metrics-tracker.d.ts +11 -0
- package/dist/learning/metrics-tracker.js +29 -13
- package/dist/learning/pattern-lifecycle.d.ts +30 -1
- package/dist/learning/pattern-lifecycle.js +92 -20
- package/dist/learning/pattern-store.d.ts +8 -0
- package/dist/learning/pattern-store.js +8 -2
- package/dist/learning/qe-unified-memory.js +1 -28
- package/dist/learning/regret-tracker.d.ts +201 -0
- package/dist/learning/regret-tracker.js +361 -0
- package/dist/mcp/bundle.js +5915 -474
- package/dist/routing/index.d.ts +4 -2
- package/dist/routing/index.js +3 -1
- package/dist/routing/neural-tiny-dancer-router.d.ts +268 -0
- package/dist/routing/neural-tiny-dancer-router.js +514 -0
- package/dist/routing/queen-integration.js +5 -5
- package/dist/routing/routing-config.d.ts +6 -0
- package/dist/routing/routing-config.js +1 -0
- package/dist/routing/simple-neural-router.d.ts +76 -0
- package/dist/routing/simple-neural-router.js +202 -0
- package/dist/routing/tiny-dancer-router.d.ts +20 -1
- package/dist/routing/tiny-dancer-router.js +21 -2
- package/dist/test-scheduling/dag-attention-scheduler.d.ts +81 -0
- package/dist/test-scheduling/dag-attention-scheduler.js +358 -0
- package/dist/test-scheduling/dag-attention-types.d.ts +81 -0
- package/dist/test-scheduling/dag-attention-types.js +10 -0
- package/dist/test-scheduling/index.d.ts +1 -0
- package/dist/test-scheduling/index.js +4 -0
- package/dist/test-scheduling/pipeline.d.ts +8 -0
- package/dist/test-scheduling/pipeline.js +28 -0
- package/package.json +6 -2
- package/dist/cli/commands/migrate.d.ts +0 -9
- package/dist/cli/commands/migrate.js +0 -566
- package/dist/init/init-wizard-migration.d.ts +0 -52
- package/dist/init/init-wizard-migration.js +0 -345
- package/dist/init/migration/config-migrator.d.ts +0 -31
- package/dist/init/migration/config-migrator.js +0 -149
- package/dist/init/migration/data-migrator.d.ts +0 -72
- package/dist/init/migration/data-migrator.js +0 -232
- package/dist/init/migration/detector.d.ts +0 -44
- package/dist/init/migration/detector.js +0 -105
- package/dist/init/migration/index.d.ts +0 -8
- package/dist/init/migration/index.js +0 -8
- package/dist/learning/v2-to-v3-migration.d.ts +0 -86
- package/dist/learning/v2-to-v3-migration.js +0 -529
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Neural TinyDancer Router - ADR-082
|
|
3
|
+
* Neural Model Routing via FastGRNN-style Network
|
|
4
|
+
*
|
|
5
|
+
* Replaces rule-based complexity thresholds with a lightweight neural network
|
|
6
|
+
* for routing decisions. Implements shadow mode for safe rollout, circuit
|
|
7
|
+
* breaker for reliability, and online learning from outcome feedback.
|
|
8
|
+
*
|
|
9
|
+
* Design:
|
|
10
|
+
* - Input features: [complexityScore, tokenEstimate, domainIndex, historicalSuccessRate]
|
|
11
|
+
* - Hidden layer: 32 units with ReLU activation
|
|
12
|
+
* - Output: softmax probability distribution over [Tier1/haiku, Tier2/sonnet, Tier3/opus]
|
|
13
|
+
* - Shadow mode: first 1000 decisions run both routers, tracks disagreement
|
|
14
|
+
* - Circuit breaker: falls back to rule-based if error rate exceeds threshold
|
|
15
|
+
*
|
|
16
|
+
* @module routing/neural-tiny-dancer-router
|
|
17
|
+
*/
|
|
18
|
+
import { performance } from 'perf_hooks';
|
|
19
|
+
import { TinyDancerRouter, } from './tiny-dancer-router.js';
|
|
20
|
+
// Re-export SimpleNeuralRouter from its own module for backward compatibility
|
|
21
|
+
export { SimpleNeuralRouter } from './simple-neural-router.js';
|
|
22
|
+
import { SimpleNeuralRouter } from './simple-neural-router.js';
|
|
23
|
+
// ============================================================================
|
|
24
|
+
// Constants
|
|
25
|
+
// ============================================================================
|
|
26
|
+
/** Decisions required before shadow mode can end */
|
|
27
|
+
const SHADOW_MODE_DECISIONS = 1000;
|
|
28
|
+
/** Maximum disagreement rate to exit shadow mode */
|
|
29
|
+
const SHADOW_MODE_MAX_DISAGREEMENT = 0.10;
|
|
30
|
+
/** Default circuit breaker error threshold */
|
|
31
|
+
const DEFAULT_CIRCUIT_BREAKER_THRESHOLD = 0.20;
|
|
32
|
+
/** Window size for circuit breaker error rate calculation */
|
|
33
|
+
const CIRCUIT_BREAKER_WINDOW = 50;
|
|
34
|
+
/** Default learning rate for weight updates */
|
|
35
|
+
const DEFAULT_LEARNING_RATE = 0.01;
|
|
36
|
+
/** Map tier index to ClaudeModel */
|
|
37
|
+
const TIER_INDEX_TO_MODEL = ['haiku', 'sonnet', 'opus'];
|
|
38
|
+
/** Map ClaudeModel to tier index */
|
|
39
|
+
const MODEL_TO_TIER_INDEX = {
|
|
40
|
+
haiku: 0,
|
|
41
|
+
sonnet: 1,
|
|
42
|
+
opus: 2,
|
|
43
|
+
};
|
|
44
|
+
/** Map QEDomain to a normalized domain index (0-1) */
|
|
45
|
+
const DOMAIN_INDEX_MAP = {
|
|
46
|
+
'test-generation': 0.1,
|
|
47
|
+
'test-execution': 0.15,
|
|
48
|
+
'coverage-analysis': 0.2,
|
|
49
|
+
'quality-assessment': 0.3,
|
|
50
|
+
'requirements-validation': 0.35,
|
|
51
|
+
'code-intelligence': 0.4,
|
|
52
|
+
'contract-testing': 0.45,
|
|
53
|
+
'visual-accessibility': 0.5,
|
|
54
|
+
'learning-optimization': 0.55,
|
|
55
|
+
'defect-intelligence': 0.7,
|
|
56
|
+
'chaos-resilience': 0.8,
|
|
57
|
+
'security-compliance': 0.9,
|
|
58
|
+
};
|
|
59
|
+
// ============================================================================
|
|
60
|
+
// Neural TinyDancer Router
|
|
61
|
+
// ============================================================================
|
|
62
|
+
/**
|
|
63
|
+
* Neural-enhanced TinyDancer Router
|
|
64
|
+
*
|
|
65
|
+
* Wraps the rule-based TinyDancerRouter with a lightweight neural network
|
|
66
|
+
* that learns optimal routing decisions from outcomes. Operates in shadow
|
|
67
|
+
* mode initially, then transitions to neural-primary when proven reliable.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* const router = new NeuralTinyDancerRouter({ learningRate: 0.01 });
|
|
72
|
+
* const result = await router.route(task);
|
|
73
|
+
* // Later, record outcome for learning
|
|
74
|
+
* router.recordNeuralOutcome(task, result, true, 0.95);
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export class NeuralTinyDancerRouter {
|
|
78
|
+
/** Rule-based fallback router (always available) */
|
|
79
|
+
ruleRouter;
|
|
80
|
+
/** Neural network for routing decisions */
|
|
81
|
+
neuralNet;
|
|
82
|
+
/** Configuration */
|
|
83
|
+
circuitBreakerThreshold;
|
|
84
|
+
shadowModeDecisionLimit;
|
|
85
|
+
shadowModeMaxDisagreement;
|
|
86
|
+
/** Shadow mode state */
|
|
87
|
+
shadowModeActive;
|
|
88
|
+
shadowDecisions = [];
|
|
89
|
+
shadowDisagreements = 0;
|
|
90
|
+
/** Circuit breaker state */
|
|
91
|
+
circuitBreakerTripped = false;
|
|
92
|
+
recentOutcomes = [];
|
|
93
|
+
/** Tracking */
|
|
94
|
+
totalNeuralDecisions = 0;
|
|
95
|
+
totalWeightUpdates = 0;
|
|
96
|
+
neuralPrimary = false;
|
|
97
|
+
/** Calibration scores for empirical confidence bounds */
|
|
98
|
+
calibrationScores = [];
|
|
99
|
+
maxCalibrationScores = 500;
|
|
100
|
+
/** Historical success rates by domain for feature extraction */
|
|
101
|
+
domainSuccessRates = new Map();
|
|
102
|
+
/** Config passthrough */
|
|
103
|
+
config;
|
|
104
|
+
/** Whether native ruvector module is available */
|
|
105
|
+
nativeRouterAvailable = false;
|
|
106
|
+
constructor(config = {}) {
|
|
107
|
+
this.config = config;
|
|
108
|
+
this.ruleRouter = new TinyDancerRouter(config);
|
|
109
|
+
this.neuralNet = new SimpleNeuralRouter(config.learningRate ?? DEFAULT_LEARNING_RATE);
|
|
110
|
+
this.circuitBreakerThreshold = config.circuitBreakerThreshold ?? DEFAULT_CIRCUIT_BREAKER_THRESHOLD;
|
|
111
|
+
this.shadowModeDecisionLimit = config.shadowModeDecisions ?? SHADOW_MODE_DECISIONS;
|
|
112
|
+
this.shadowModeMaxDisagreement = config.shadowModeMaxDisagreement ?? SHADOW_MODE_MAX_DISAGREEMENT;
|
|
113
|
+
// Determine initial shadow mode state
|
|
114
|
+
if (config.forceShadowMode !== undefined) {
|
|
115
|
+
this.shadowModeActive = config.forceShadowMode;
|
|
116
|
+
this.neuralPrimary = !config.forceShadowMode;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
this.shadowModeActive = true;
|
|
120
|
+
this.neuralPrimary = false;
|
|
121
|
+
}
|
|
122
|
+
// Attempt native ruvector import (non-blocking)
|
|
123
|
+
this.tryLoadNativeRouter();
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Check for native router availability.
|
|
127
|
+
*
|
|
128
|
+
* The `@ruvector/tiny-dancer` NAPI package exists but the ARM64 binary
|
|
129
|
+
* is missing from the published package (packaging bug upstream).
|
|
130
|
+
* The TypeScript `SimpleNeuralRouter` IS the production implementation —
|
|
131
|
+
* its 4→32→3 network is too small to benefit from native acceleration.
|
|
132
|
+
*/
|
|
133
|
+
tryLoadNativeRouter() {
|
|
134
|
+
this.nativeRouterAvailable = false;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Route a task to the optimal Claude model.
|
|
138
|
+
*
|
|
139
|
+
* Behavior depends on current mode:
|
|
140
|
+
* - Shadow mode: uses rule-based result, logs neural decision alongside
|
|
141
|
+
* - Neural primary: uses neural decision with rule-based as validation
|
|
142
|
+
* - Circuit breaker tripped: uses rule-based only
|
|
143
|
+
*
|
|
144
|
+
* @param task - The task to route
|
|
145
|
+
* @returns Routing result with model recommendation and confidence
|
|
146
|
+
*/
|
|
147
|
+
async route(task) {
|
|
148
|
+
const startTime = performance.now();
|
|
149
|
+
// Always get the rule-based result (fallback guarantee)
|
|
150
|
+
const ruleResult = await this.ruleRouter.route(task);
|
|
151
|
+
// If circuit breaker is tripped, use rule-based only
|
|
152
|
+
if (this.circuitBreakerTripped) {
|
|
153
|
+
return ruleResult;
|
|
154
|
+
}
|
|
155
|
+
// Get neural network decision
|
|
156
|
+
const features = this.extractFeatures(task, ruleResult.classification);
|
|
157
|
+
let neuralProbs;
|
|
158
|
+
try {
|
|
159
|
+
neuralProbs = this.neuralNet.forward(features);
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
// Neural network error - fall back to rule-based
|
|
163
|
+
this.tripCircuitBreaker();
|
|
164
|
+
return ruleResult;
|
|
165
|
+
}
|
|
166
|
+
const neuralTierIndex = this.argmax(neuralProbs);
|
|
167
|
+
const neuralModel = TIER_INDEX_TO_MODEL[neuralTierIndex];
|
|
168
|
+
const neuralConfidence = neuralProbs[neuralTierIndex];
|
|
169
|
+
this.totalNeuralDecisions++;
|
|
170
|
+
// Shadow mode: log both decisions, use rule-based result
|
|
171
|
+
if (this.shadowModeActive) {
|
|
172
|
+
const agreed = ruleResult.model === neuralModel;
|
|
173
|
+
if (!agreed) {
|
|
174
|
+
this.shadowDisagreements++;
|
|
175
|
+
}
|
|
176
|
+
this.shadowDecisions.push({
|
|
177
|
+
taskDescription: task.description.slice(0, 100),
|
|
178
|
+
ruleDecision: ruleResult.model,
|
|
179
|
+
neuralDecision: neuralModel,
|
|
180
|
+
agreed,
|
|
181
|
+
neuralConfidence,
|
|
182
|
+
timestamp: new Date(),
|
|
183
|
+
});
|
|
184
|
+
// Check if we should exit shadow mode
|
|
185
|
+
this.evaluateShadowModeExit();
|
|
186
|
+
// In shadow mode, always return the rule-based result
|
|
187
|
+
return ruleResult;
|
|
188
|
+
}
|
|
189
|
+
// Neural-primary mode: use neural decision
|
|
190
|
+
const latencyMs = performance.now() - startTime;
|
|
191
|
+
// Build empirical confidence bounds from historical calibration scores
|
|
192
|
+
const empiricalBounds = this.computeEmpiricalConfidenceBounds(neuralProbs);
|
|
193
|
+
// Determine if we should override neural with rule-based
|
|
194
|
+
// (when neural confidence is very low or calibration score is too high)
|
|
195
|
+
if (neuralConfidence < 0.3 || empiricalBounds.calibrationScore > 0.8) {
|
|
196
|
+
return ruleResult;
|
|
197
|
+
}
|
|
198
|
+
// Build neural route result
|
|
199
|
+
const result = {
|
|
200
|
+
model: neuralModel,
|
|
201
|
+
confidence: neuralConfidence,
|
|
202
|
+
uncertainty: 1 - neuralConfidence,
|
|
203
|
+
triggerMultiModel: ruleResult.triggerMultiModel,
|
|
204
|
+
triggerHumanReview: ruleResult.triggerHumanReview,
|
|
205
|
+
complexity: ruleResult.complexity,
|
|
206
|
+
classification: ruleResult.classification,
|
|
207
|
+
latencyMs,
|
|
208
|
+
reasoning: this.buildNeuralReasoning(neuralModel, neuralConfidence, neuralProbs, ruleResult, empiricalBounds),
|
|
209
|
+
};
|
|
210
|
+
return result;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Extract feature vector from a task for neural network input.
|
|
214
|
+
*
|
|
215
|
+
* Features:
|
|
216
|
+
* [0] complexityScore: normalized classification score (0-1)
|
|
217
|
+
* [1] tokenEstimate: estimated token count, normalized (0-1)
|
|
218
|
+
* [2] domainIndex: domain-based complexity signal (0-1)
|
|
219
|
+
* [3] historicalSuccessRate: domain success rate (0-1)
|
|
220
|
+
*/
|
|
221
|
+
extractFeatures(task, classification) {
|
|
222
|
+
// Feature 1: Normalized complexity score (0-100 -> 0-1)
|
|
223
|
+
const complexityScore = Math.min(1, classification.score / 100);
|
|
224
|
+
// Feature 2: Token estimate (based on description length + context)
|
|
225
|
+
const descLen = task.description.length;
|
|
226
|
+
const contextLen = task.context?.code?.length ?? 0;
|
|
227
|
+
const rawTokenEstimate = (descLen + contextLen) / 4; // rough char-to-token ratio
|
|
228
|
+
const tokenEstimate = Math.min(1, rawTokenEstimate / 10000); // normalize to 0-1
|
|
229
|
+
// Feature 3: Domain index
|
|
230
|
+
const domain = task.domain ?? '';
|
|
231
|
+
const domainIndex = DOMAIN_INDEX_MAP[domain] ?? 0.25;
|
|
232
|
+
// Feature 4: Historical success rate for this domain
|
|
233
|
+
const domainStats = this.domainSuccessRates.get(domain);
|
|
234
|
+
const successRate = domainStats
|
|
235
|
+
? domainStats.success / Math.max(1, domainStats.total)
|
|
236
|
+
: 0.7; // default assumption
|
|
237
|
+
return [complexityScore, tokenEstimate, domainIndex, successRate];
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Record the outcome of a routing decision for neural learning.
|
|
241
|
+
*
|
|
242
|
+
* Updates both the rule-based router (for stats) and the neural network
|
|
243
|
+
* weights (via policy gradient).
|
|
244
|
+
*/
|
|
245
|
+
recordNeuralOutcome(task, routeResult, success, qualityScore = success ? 1.0 : 0.0, actualModelUsed = routeResult.model, durationMs = 0) {
|
|
246
|
+
// Forward to rule-based router for stats
|
|
247
|
+
this.ruleRouter.recordOutcome(task, routeResult, success, qualityScore, actualModelUsed, durationMs);
|
|
248
|
+
// Update domain success rates
|
|
249
|
+
const domain = task.domain ?? 'unknown';
|
|
250
|
+
const stats = this.domainSuccessRates.get(domain) ?? { success: 0, total: 0 };
|
|
251
|
+
stats.total++;
|
|
252
|
+
if (success)
|
|
253
|
+
stats.success++;
|
|
254
|
+
this.domainSuccessRates.set(domain, stats);
|
|
255
|
+
// Track recent outcomes for circuit breaker
|
|
256
|
+
this.recentOutcomes.push(success);
|
|
257
|
+
if (this.recentOutcomes.length > CIRCUIT_BREAKER_WINDOW) {
|
|
258
|
+
this.recentOutcomes.shift();
|
|
259
|
+
}
|
|
260
|
+
// Check circuit breaker
|
|
261
|
+
this.checkCircuitBreaker();
|
|
262
|
+
// Update neural network weights
|
|
263
|
+
const features = this.extractFeatures(task, routeResult.classification);
|
|
264
|
+
const tierIndex = MODEL_TO_TIER_INDEX[actualModelUsed];
|
|
265
|
+
const reward = (qualityScore - 0.5) * 2; // Scale to -1..1
|
|
266
|
+
try {
|
|
267
|
+
this.neuralNet.updateWeights(features, tierIndex, reward);
|
|
268
|
+
this.totalWeightUpdates++;
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
// Weight update failure is non-fatal
|
|
272
|
+
}
|
|
273
|
+
// Update calibration scores for empirical confidence bounds
|
|
274
|
+
const probs = this.neuralNet.forward(features);
|
|
275
|
+
const predictedTier = this.argmax(probs);
|
|
276
|
+
const calibrationScore = 1 - probs[tierIndex];
|
|
277
|
+
this.calibrationScores.push(calibrationScore);
|
|
278
|
+
if (this.calibrationScores.length > this.maxCalibrationScores) {
|
|
279
|
+
this.calibrationScores.shift();
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Compute empirical confidence bounds from historical calibration scores.
|
|
284
|
+
*
|
|
285
|
+
* Note: This is NOT proper conformal prediction (which requires exchangeability
|
|
286
|
+
* guarantees and per-class nonconformity scoring). These are quantile-based
|
|
287
|
+
* intervals from historical routing outcomes.
|
|
288
|
+
*/
|
|
289
|
+
computeEmpiricalConfidenceBounds(probs) {
|
|
290
|
+
const coverageLevel = 0.90;
|
|
291
|
+
const maxProb = Math.max(...probs);
|
|
292
|
+
const calibrationScore = 1 - maxProb;
|
|
293
|
+
if (this.calibrationScores.length < 10) {
|
|
294
|
+
// Insufficient calibration data - return wide bounds
|
|
295
|
+
return {
|
|
296
|
+
lower: 0,
|
|
297
|
+
upper: 1,
|
|
298
|
+
coverageLevel,
|
|
299
|
+
calibrationScore,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
// Compute quantile of calibration scores
|
|
303
|
+
const sorted = [...this.calibrationScores].sort((a, b) => a - b);
|
|
304
|
+
const quantileIndex = Math.ceil(coverageLevel * sorted.length) - 1;
|
|
305
|
+
const threshold = sorted[Math.min(quantileIndex, sorted.length - 1)];
|
|
306
|
+
return {
|
|
307
|
+
lower: Math.max(0, maxProb - threshold),
|
|
308
|
+
upper: Math.min(1, maxProb + threshold),
|
|
309
|
+
coverageLevel,
|
|
310
|
+
calibrationScore,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Backward-compatible alias for computeEmpiricalConfidenceBounds.
|
|
315
|
+
* @deprecated Use computeEmpiricalConfidenceBounds instead.
|
|
316
|
+
*/
|
|
317
|
+
computeConformalBounds(probs) {
|
|
318
|
+
return this.computeEmpiricalConfidenceBounds(probs);
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Check whether shadow mode should end.
|
|
322
|
+
* Exits when enough decisions made AND disagreement rate is acceptable.
|
|
323
|
+
*/
|
|
324
|
+
evaluateShadowModeExit() {
|
|
325
|
+
if (!this.shadowModeActive)
|
|
326
|
+
return;
|
|
327
|
+
if (this.config.forceShadowMode === true)
|
|
328
|
+
return; // Forced on
|
|
329
|
+
const totalShadow = this.shadowDecisions.length;
|
|
330
|
+
if (totalShadow < this.shadowModeDecisionLimit)
|
|
331
|
+
return;
|
|
332
|
+
const disagreementRate = this.shadowDisagreements / totalShadow;
|
|
333
|
+
if (disagreementRate <= this.shadowModeMaxDisagreement) {
|
|
334
|
+
this.shadowModeActive = false;
|
|
335
|
+
this.neuralPrimary = true;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Check circuit breaker and trip if error rate exceeds threshold.
|
|
340
|
+
*/
|
|
341
|
+
checkCircuitBreaker() {
|
|
342
|
+
if (this.recentOutcomes.length < 10)
|
|
343
|
+
return;
|
|
344
|
+
const failures = this.recentOutcomes.filter(s => !s).length;
|
|
345
|
+
const errorRate = failures / this.recentOutcomes.length;
|
|
346
|
+
if (errorRate > this.circuitBreakerThreshold) {
|
|
347
|
+
this.tripCircuitBreaker();
|
|
348
|
+
}
|
|
349
|
+
else if (this.circuitBreakerTripped && errorRate < this.circuitBreakerThreshold * 0.5) {
|
|
350
|
+
// Auto-reset circuit breaker when error rate drops significantly
|
|
351
|
+
this.circuitBreakerTripped = false;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Trip the circuit breaker, falling back to rule-based routing.
|
|
356
|
+
*/
|
|
357
|
+
tripCircuitBreaker() {
|
|
358
|
+
this.circuitBreakerTripped = true;
|
|
359
|
+
this.neuralPrimary = false;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Build reasoning string for neural routing decision
|
|
363
|
+
*/
|
|
364
|
+
buildNeuralReasoning(model, confidence, probs, ruleResult, bounds) {
|
|
365
|
+
const parts = [];
|
|
366
|
+
parts.push(`[Neural] Routing to ${model.toUpperCase()}`);
|
|
367
|
+
parts.push(`with ${(confidence * 100).toFixed(0)}% neural confidence.`);
|
|
368
|
+
parts.push(`Tier probabilities: haiku=${(probs[0] * 100).toFixed(0)}%,`);
|
|
369
|
+
parts.push(`sonnet=${(probs[1] * 100).toFixed(0)}%,`);
|
|
370
|
+
parts.push(`opus=${(probs[2] * 100).toFixed(0)}%.`);
|
|
371
|
+
if (model !== ruleResult.model) {
|
|
372
|
+
parts.push(`Rule-based would choose ${ruleResult.model.toUpperCase()}.`);
|
|
373
|
+
}
|
|
374
|
+
parts.push(`Empirical bounds: [${bounds.lower.toFixed(2)}, ${bounds.upper.toFixed(2)}]`);
|
|
375
|
+
parts.push(`at ${(bounds.coverageLevel * 100).toFixed(0)}% coverage.`);
|
|
376
|
+
return parts.join(' ');
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Get the index of the maximum value in an array.
|
|
380
|
+
*/
|
|
381
|
+
argmax(arr) {
|
|
382
|
+
let maxIdx = 0;
|
|
383
|
+
let maxVal = arr[0];
|
|
384
|
+
for (let i = 1; i < arr.length; i++) {
|
|
385
|
+
if (arr[i] > maxVal) {
|
|
386
|
+
maxVal = arr[i];
|
|
387
|
+
maxIdx = i;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return maxIdx;
|
|
391
|
+
}
|
|
392
|
+
// ============================================================================
|
|
393
|
+
// Public Accessors
|
|
394
|
+
// ============================================================================
|
|
395
|
+
/**
|
|
396
|
+
* Get neural router statistics
|
|
397
|
+
*/
|
|
398
|
+
getNeuralStats() {
|
|
399
|
+
const totalShadow = this.shadowDecisions.length;
|
|
400
|
+
const disagreementRate = totalShadow > 0
|
|
401
|
+
? this.shadowDisagreements / totalShadow
|
|
402
|
+
: 0;
|
|
403
|
+
const failures = this.recentOutcomes.filter(s => !s).length;
|
|
404
|
+
const recentErrorRate = this.recentOutcomes.length > 0
|
|
405
|
+
? failures / this.recentOutcomes.length
|
|
406
|
+
: 0;
|
|
407
|
+
return {
|
|
408
|
+
shadowModeActive: this.shadowModeActive,
|
|
409
|
+
shadowDecisions: totalShadow,
|
|
410
|
+
disagreementRate,
|
|
411
|
+
circuitBreakerTripped: this.circuitBreakerTripped,
|
|
412
|
+
recentErrorRate,
|
|
413
|
+
totalNeuralDecisions: this.totalNeuralDecisions,
|
|
414
|
+
totalWeightUpdates: this.totalWeightUpdates,
|
|
415
|
+
neuralPrimary: this.neuralPrimary,
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Get shadow mode decision logs
|
|
420
|
+
*/
|
|
421
|
+
getShadowDecisionLogs() {
|
|
422
|
+
return this.shadowDecisions;
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Get the underlying rule-based router (for direct access if needed)
|
|
426
|
+
*/
|
|
427
|
+
getRuleRouter() {
|
|
428
|
+
return this.ruleRouter;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Get the underlying neural network (for serialization/deserialization)
|
|
432
|
+
*/
|
|
433
|
+
getNeuralNet() {
|
|
434
|
+
return this.neuralNet;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Check if native ruvector router is available
|
|
438
|
+
*/
|
|
439
|
+
isNativeRouterAvailable() {
|
|
440
|
+
return this.nativeRouterAvailable;
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Check if shadow mode is active
|
|
444
|
+
*/
|
|
445
|
+
isShadowModeActive() {
|
|
446
|
+
return this.shadowModeActive;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Check if neural routing is primary
|
|
450
|
+
*/
|
|
451
|
+
isNeuralPrimary() {
|
|
452
|
+
return this.neuralPrimary;
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Check if circuit breaker is tripped
|
|
456
|
+
*/
|
|
457
|
+
isCircuitBreakerTripped() {
|
|
458
|
+
return this.circuitBreakerTripped;
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Manually reset the circuit breaker
|
|
462
|
+
*/
|
|
463
|
+
resetCircuitBreaker() {
|
|
464
|
+
this.circuitBreakerTripped = false;
|
|
465
|
+
this.recentOutcomes = [];
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Get the rule-based router's stats (delegates)
|
|
469
|
+
*/
|
|
470
|
+
getStats() {
|
|
471
|
+
return this.ruleRouter.getStats();
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Get the rule-based router's config (delegates)
|
|
475
|
+
*/
|
|
476
|
+
getConfig() {
|
|
477
|
+
return this.ruleRouter.getConfig();
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Reset all state (both rule-based and neural)
|
|
481
|
+
*/
|
|
482
|
+
reset() {
|
|
483
|
+
this.ruleRouter.reset();
|
|
484
|
+
this.shadowDecisions = [];
|
|
485
|
+
this.shadowDisagreements = 0;
|
|
486
|
+
this.circuitBreakerTripped = false;
|
|
487
|
+
this.recentOutcomes = [];
|
|
488
|
+
this.totalNeuralDecisions = 0;
|
|
489
|
+
this.totalWeightUpdates = 0;
|
|
490
|
+
this.calibrationScores = [];
|
|
491
|
+
this.domainSuccessRates.clear();
|
|
492
|
+
if (this.config.forceShadowMode !== undefined) {
|
|
493
|
+
this.shadowModeActive = this.config.forceShadowMode;
|
|
494
|
+
this.neuralPrimary = !this.config.forceShadowMode;
|
|
495
|
+
}
|
|
496
|
+
else {
|
|
497
|
+
this.shadowModeActive = true;
|
|
498
|
+
this.neuralPrimary = false;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
// ============================================================================
|
|
503
|
+
// Factory Function
|
|
504
|
+
// ============================================================================
|
|
505
|
+
/**
|
|
506
|
+
* Create a new Neural TinyDancer router instance
|
|
507
|
+
*
|
|
508
|
+
* @param config - Neural router configuration
|
|
509
|
+
* @returns Configured Neural TinyDancer router
|
|
510
|
+
*/
|
|
511
|
+
export function createNeuralTinyDancerRouter(config) {
|
|
512
|
+
return new NeuralTinyDancerRouter(config);
|
|
513
|
+
}
|
|
514
|
+
//# sourceMappingURL=neural-tiny-dancer-router.js.map
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* to agent pool tiers and handles fallback scenarios.
|
|
8
8
|
*/
|
|
9
9
|
import { performance } from 'perf_hooks';
|
|
10
|
-
import {
|
|
10
|
+
import { createSmartTinyDancerRouter } from './tiny-dancer-router.js';
|
|
11
11
|
import { DEFAULT_ROUTING_CONFIG, loadRoutingConfigFromEnv, mapComplexityToTier, getNextFallbackTier, tierToModel, estimateTaskCost, validateRoutingConfig, } from './routing-config.js';
|
|
12
12
|
// ============================================================================
|
|
13
13
|
// Queen Router Adapter Implementation
|
|
@@ -44,8 +44,8 @@ export class QueenRouterAdapter {
|
|
|
44
44
|
outcomes = [];
|
|
45
45
|
maxOutcomes = 1000;
|
|
46
46
|
constructor(config = {}) {
|
|
47
|
-
// Initialize TinyDancer router
|
|
48
|
-
this.tinyDancer =
|
|
47
|
+
// Initialize TinyDancer router (uses neural if useNeuralRouting flag is on)
|
|
48
|
+
this.tinyDancer = createSmartTinyDancerRouter(config.tinyDancer);
|
|
49
49
|
// Deep merge partial routing config with defaults, then apply env overrides
|
|
50
50
|
const mergedConfig = this.mergeRoutingConfig(config.routing);
|
|
51
51
|
this.routingConfig = loadRoutingConfigFromEnv(mergedConfig);
|
|
@@ -141,7 +141,7 @@ export class QueenRouterAdapter {
|
|
|
141
141
|
this.outcomes.shift();
|
|
142
142
|
}
|
|
143
143
|
// Also record outcome in TinyDancer for its learning
|
|
144
|
-
this.tinyDancer.recordOutcome(task, decision.tinyDancerResult, success, qualityScore, tierToModel(usedTier), durationMs);
|
|
144
|
+
this.tinyDancer.recordOutcome?.(task, decision.tinyDancerResult, success, qualityScore, tierToModel(usedTier), durationMs);
|
|
145
145
|
if (this.routingConfig.verbose) {
|
|
146
146
|
console.log(`[QueenRouter] Recorded outcome: tier=${usedTier}, ` +
|
|
147
147
|
`success=${success}, quality=${(qualityScore * 100).toFixed(0)}%, ` +
|
|
@@ -232,7 +232,7 @@ export class QueenRouterAdapter {
|
|
|
232
232
|
this.dailyCost = 0;
|
|
233
233
|
this.lastCostReset = new Date();
|
|
234
234
|
this.outcomes = [];
|
|
235
|
-
this.tinyDancer.reset();
|
|
235
|
+
this.tinyDancer.reset?.();
|
|
236
236
|
}
|
|
237
237
|
// ============================================================================
|
|
238
238
|
// Private Methods
|
|
@@ -87,6 +87,12 @@ export interface RoutingConfig {
|
|
|
87
87
|
enableEMACalibration: boolean;
|
|
88
88
|
/** Enable automatic tier escalation/de-escalation on consecutive outcomes (default: true) */
|
|
89
89
|
enableAutoEscalation: boolean;
|
|
90
|
+
/** Enable neural model routing via NeuralTinyDancerRouter (ADR-082, Task 2.1).
|
|
91
|
+
* When true, createSmartTinyDancerRouter returns a NeuralTinyDancerRouter
|
|
92
|
+
* that runs alongside (shadow mode) or replaces rule-based routing.
|
|
93
|
+
* Actual activation is controlled by the useNeuralRouting feature flag.
|
|
94
|
+
* @default false */
|
|
95
|
+
enableNeuralRouting: boolean;
|
|
90
96
|
}
|
|
91
97
|
/**
|
|
92
98
|
* Default confidence thresholds
|
|
@@ -84,6 +84,7 @@ export const DEFAULT_ROUTING_CONFIG = {
|
|
|
84
84
|
verbose: false,
|
|
85
85
|
enableEMACalibration: true,
|
|
86
86
|
enableAutoEscalation: true,
|
|
87
|
+
enableNeuralRouting: false,
|
|
87
88
|
};
|
|
88
89
|
// ============================================================================
|
|
89
90
|
// Environment Variable Overrides
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple Neural Router - Extracted from neural-tiny-dancer-router.ts
|
|
3
|
+
*
|
|
4
|
+
* Lightweight feedforward neural network for routing decisions.
|
|
5
|
+
* Uses Xavier initialization and simple policy gradient updates.
|
|
6
|
+
*
|
|
7
|
+
* Architecture:
|
|
8
|
+
* Input(4) -> Dense(32, ReLU) -> Dense(3, Softmax) -> [p_haiku, p_sonnet, p_opus]
|
|
9
|
+
*
|
|
10
|
+
* @module routing/simple-neural-router
|
|
11
|
+
*/
|
|
12
|
+
/** Number of input features for the neural network */
|
|
13
|
+
export declare const INPUT_SIZE = 4;
|
|
14
|
+
/** Number of hidden units */
|
|
15
|
+
export declare const HIDDEN_SIZE = 32;
|
|
16
|
+
/** Number of output classes (tier1=haiku, tier2=sonnet, tier3=opus) */
|
|
17
|
+
export declare const OUTPUT_SIZE = 3;
|
|
18
|
+
/** Default learning rate for weight updates */
|
|
19
|
+
export declare const DEFAULT_LEARNING_RATE = 0.01;
|
|
20
|
+
/**
|
|
21
|
+
* Lightweight feedforward neural network for routing decisions.
|
|
22
|
+
* Uses Xavier initialization and simple policy gradient updates.
|
|
23
|
+
*
|
|
24
|
+
* Architecture:
|
|
25
|
+
* Input(4) -> Dense(32, ReLU) -> Dense(3, Softmax) -> [p_haiku, p_sonnet, p_opus]
|
|
26
|
+
*/
|
|
27
|
+
export declare class SimpleNeuralRouter {
|
|
28
|
+
private weightsInputHidden;
|
|
29
|
+
private weightsHiddenOutput;
|
|
30
|
+
private biasHidden;
|
|
31
|
+
private biasOutput;
|
|
32
|
+
private learningRate;
|
|
33
|
+
constructor(learningRate?: number);
|
|
34
|
+
/**
|
|
35
|
+
* Xavier/Glorot initialization for weight matrices
|
|
36
|
+
*/
|
|
37
|
+
private xavierInit;
|
|
38
|
+
/**
|
|
39
|
+
* Forward pass through the network
|
|
40
|
+
*
|
|
41
|
+
* @param features - Input feature vector [complexityScore, tokenEstimate, domainIndex, successRate]
|
|
42
|
+
* @returns Probability distribution over tiers [p_haiku, p_sonnet, p_opus]
|
|
43
|
+
*/
|
|
44
|
+
forward(features: number[]): number[];
|
|
45
|
+
/**
|
|
46
|
+
* Numerically stable softmax
|
|
47
|
+
*/
|
|
48
|
+
private softmax;
|
|
49
|
+
/**
|
|
50
|
+
* Update weights using simple policy gradient (REINFORCE)
|
|
51
|
+
*
|
|
52
|
+
* @param features - Input features used for the decision
|
|
53
|
+
* @param chosenTierIndex - Index of the tier that was chosen (0=haiku, 1=sonnet, 2=opus)
|
|
54
|
+
* @param reward - Reward signal (-1 to 1, where 1 = perfect, -1 = total failure)
|
|
55
|
+
*/
|
|
56
|
+
updateWeights(features: number[], chosenTierIndex: number, reward: number): void;
|
|
57
|
+
/**
|
|
58
|
+
* Serialize weights for persistence
|
|
59
|
+
*/
|
|
60
|
+
serialize(): {
|
|
61
|
+
weightsInputHidden: number[];
|
|
62
|
+
weightsHiddenOutput: number[];
|
|
63
|
+
biasHidden: number[];
|
|
64
|
+
biasOutput: number[];
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Deserialize weights from persistence
|
|
68
|
+
*/
|
|
69
|
+
deserialize(data: {
|
|
70
|
+
weightsInputHidden: number[];
|
|
71
|
+
weightsHiddenOutput: number[];
|
|
72
|
+
biasHidden: number[];
|
|
73
|
+
biasOutput: number[];
|
|
74
|
+
}): void;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=simple-neural-router.d.ts.map
|