agentic-qe 3.8.10 → 3.8.12
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/skills/skills-manifest.json +1 -1
- package/CHANGELOG.md +40 -0
- package/dist/cli/bundle.js +1345 -1003
- package/dist/cli/command-registry.js +5 -1
- package/dist/cli/commands/pipeline.d.ts +16 -0
- package/dist/cli/commands/pipeline.js +314 -0
- package/dist/cli/commands/ruvector-commands.js +17 -0
- package/dist/cli/commands/token-usage.js +24 -1
- package/dist/cli/handlers/heartbeat-handler.d.ts +26 -0
- package/dist/cli/handlers/heartbeat-handler.js +382 -0
- package/dist/cli/handlers/index.d.ts +2 -0
- package/dist/cli/handlers/index.js +2 -0
- package/dist/cli/handlers/routing-handler.d.ts +22 -0
- package/dist/cli/handlers/routing-handler.js +227 -0
- package/dist/cli/index.js +2 -0
- package/dist/coordination/deterministic-actions.d.ts +36 -0
- package/dist/coordination/deterministic-actions.js +257 -0
- package/dist/coordination/workflow-orchestrator.d.ts +18 -1
- package/dist/coordination/workflow-orchestrator.js +113 -3
- package/dist/coordination/workflow-types.d.ts +19 -1
- package/dist/coordination/workflow-types.js +3 -0
- package/dist/coordination/yaml-pipeline-loader.d.ts +1 -0
- package/dist/coordination/yaml-pipeline-loader.js +34 -0
- package/dist/domains/code-intelligence/coordinator-gnn.d.ts +21 -0
- package/dist/domains/code-intelligence/coordinator-gnn.js +102 -0
- package/dist/domains/contract-testing/coordinator.js +13 -0
- package/dist/domains/coverage-analysis/coordinator.js +5 -0
- package/dist/domains/defect-intelligence/coordinator.d.ts +1 -0
- package/dist/domains/defect-intelligence/coordinator.js +43 -0
- package/dist/domains/quality-assessment/coordinator.js +26 -0
- package/dist/domains/test-generation/coordinator.js +14 -0
- package/dist/integrations/agentic-flow/reasoning-bank/experience-replay.d.ts +11 -0
- package/dist/integrations/agentic-flow/reasoning-bank/experience-replay.js +44 -1
- package/dist/integrations/rl-suite/algorithms/eprop.d.ts +79 -0
- package/dist/integrations/rl-suite/algorithms/eprop.js +284 -0
- package/dist/integrations/rl-suite/algorithms/index.d.ts +2 -1
- package/dist/integrations/rl-suite/algorithms/index.js +2 -1
- package/dist/integrations/rl-suite/index.d.ts +2 -2
- package/dist/integrations/rl-suite/index.js +2 -2
- package/dist/integrations/rl-suite/interfaces.d.ts +3 -3
- package/dist/integrations/rl-suite/interfaces.js +1 -1
- package/dist/integrations/rl-suite/orchestrator.d.ts +2 -2
- package/dist/integrations/rl-suite/orchestrator.js +3 -2
- package/dist/integrations/rl-suite/reward-signals.d.ts +1 -1
- package/dist/integrations/rl-suite/reward-signals.js +1 -1
- package/dist/integrations/ruvector/coherence-gate-cohomology.d.ts +41 -0
- package/dist/integrations/ruvector/coherence-gate-cohomology.js +47 -0
- package/dist/integrations/ruvector/coherence-gate-core.d.ts +200 -0
- package/dist/integrations/ruvector/coherence-gate-core.js +294 -0
- package/dist/integrations/ruvector/coherence-gate-energy.d.ts +136 -0
- package/dist/integrations/ruvector/coherence-gate-energy.js +373 -0
- package/dist/integrations/ruvector/coherence-gate-vector.d.ts +38 -0
- package/dist/integrations/ruvector/coherence-gate-vector.js +76 -0
- package/dist/integrations/ruvector/coherence-gate.d.ts +10 -311
- package/dist/integrations/ruvector/coherence-gate.js +10 -652
- package/dist/integrations/ruvector/cold-tier-trainer.d.ts +103 -0
- package/dist/integrations/ruvector/cold-tier-trainer.js +377 -0
- package/dist/integrations/ruvector/cusum-detector.d.ts +70 -0
- package/dist/integrations/ruvector/cusum-detector.js +142 -0
- package/dist/integrations/ruvector/delta-tracker.d.ts +122 -0
- package/dist/integrations/ruvector/delta-tracker.js +311 -0
- package/dist/integrations/ruvector/domain-transfer.d.ts +79 -1
- package/dist/integrations/ruvector/domain-transfer.js +158 -2
- package/dist/integrations/ruvector/eprop-learner.d.ts +135 -0
- package/dist/integrations/ruvector/eprop-learner.js +351 -0
- package/dist/integrations/ruvector/feature-flags.d.ts +177 -0
- package/dist/integrations/ruvector/feature-flags.js +145 -0
- package/dist/integrations/ruvector/graphmae-encoder.d.ts +88 -0
- package/dist/integrations/ruvector/graphmae-encoder.js +360 -0
- package/dist/integrations/ruvector/hdc-fingerprint.d.ts +127 -0
- package/dist/integrations/ruvector/hdc-fingerprint.js +222 -0
- package/dist/integrations/ruvector/hopfield-memory.d.ts +97 -0
- package/dist/integrations/ruvector/hopfield-memory.js +238 -0
- package/dist/integrations/ruvector/index.d.ts +13 -2
- package/dist/integrations/ruvector/index.js +46 -2
- package/dist/integrations/ruvector/mincut-wrapper.d.ts +7 -0
- package/dist/integrations/ruvector/mincut-wrapper.js +54 -2
- package/dist/integrations/ruvector/reservoir-replay.d.ts +172 -0
- package/dist/integrations/ruvector/reservoir-replay.js +335 -0
- package/dist/integrations/ruvector/solver-adapter.d.ts +93 -0
- package/dist/integrations/ruvector/solver-adapter.js +299 -0
- package/dist/integrations/ruvector/sona-persistence.d.ts +33 -0
- package/dist/integrations/ruvector/sona-persistence.js +47 -0
- package/dist/integrations/ruvector/spectral-sparsifier.d.ts +154 -0
- package/dist/integrations/ruvector/spectral-sparsifier.js +389 -0
- package/dist/integrations/ruvector/temporal-causality.d.ts +63 -0
- package/dist/integrations/ruvector/temporal-causality.js +317 -0
- package/dist/learning/pattern-promotion.d.ts +63 -0
- package/dist/learning/pattern-promotion.js +235 -1
- package/dist/learning/pattern-store.d.ts +2 -0
- package/dist/learning/pattern-store.js +187 -1
- package/dist/learning/sqlite-persistence.d.ts +2 -0
- package/dist/learning/sqlite-persistence.js +4 -0
- package/dist/mcp/bundle.js +477 -380
- package/dist/mcp/handlers/heartbeat-handlers.d.ts +67 -0
- package/dist/mcp/handlers/heartbeat-handlers.js +180 -0
- package/dist/mcp/handlers/index.d.ts +2 -1
- package/dist/mcp/handlers/index.js +5 -1
- package/dist/mcp/handlers/task-handlers.d.ts +28 -0
- package/dist/mcp/handlers/task-handlers.js +39 -0
- package/dist/mcp/protocol-server.js +45 -1
- package/dist/mcp/server.js +41 -1
- package/dist/optimization/index.d.ts +2 -0
- package/dist/optimization/index.js +1 -0
- package/dist/optimization/session-cache.d.ts +80 -0
- package/dist/optimization/session-cache.js +227 -0
- package/dist/optimization/token-optimizer-service.d.ts +10 -0
- package/dist/optimization/token-optimizer-service.js +51 -0
- package/dist/routing/economic-routing.d.ts +126 -0
- package/dist/routing/economic-routing.js +290 -0
- package/dist/routing/index.d.ts +2 -0
- package/dist/routing/index.js +2 -0
- package/dist/routing/routing-feedback.d.ts +29 -0
- package/dist/routing/routing-feedback.js +75 -0
- package/dist/shared/utils/index.d.ts +1 -0
- package/dist/shared/utils/index.js +1 -0
- package/dist/shared/utils/xorshift128.d.ts +24 -0
- package/dist/shared/utils/xorshift128.js +50 -0
- package/package.json +1 -1
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agentic QE v3 - Heartbeat Command Handler
|
|
3
|
+
* Imp-10: Token-Free Heartbeat Scheduler CLI Integration
|
|
4
|
+
*
|
|
5
|
+
* Handles the 'aqe heartbeat' command with subcommands:
|
|
6
|
+
* status, run-now, history, log, pause, resume
|
|
7
|
+
*/
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
import { formatDuration } from './interfaces.js';
|
|
12
|
+
import { HeartbeatSchedulerWorker } from '../../workers/workers/heartbeat-scheduler.js';
|
|
13
|
+
import { toErrorMessage } from '../../shared/error-utils.js';
|
|
14
|
+
import { findProjectRoot } from '../../kernel/unified-memory.js';
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Heartbeat Handler
|
|
17
|
+
// ============================================================================
|
|
18
|
+
export class HeartbeatHandler {
|
|
19
|
+
name = 'heartbeat';
|
|
20
|
+
description = 'Manage the token-free heartbeat scheduler';
|
|
21
|
+
cleanupAndExit;
|
|
22
|
+
worker;
|
|
23
|
+
constructor(cleanupAndExit) {
|
|
24
|
+
this.cleanupAndExit = cleanupAndExit;
|
|
25
|
+
this.worker = new HeartbeatSchedulerWorker();
|
|
26
|
+
}
|
|
27
|
+
register(program, _context) {
|
|
28
|
+
const heartbeat = program
|
|
29
|
+
.command('heartbeat')
|
|
30
|
+
.description(this.description);
|
|
31
|
+
heartbeat
|
|
32
|
+
.command('status')
|
|
33
|
+
.description('Show heartbeat worker status, health, and schedule')
|
|
34
|
+
.action(async () => {
|
|
35
|
+
await this.executeStatus();
|
|
36
|
+
});
|
|
37
|
+
heartbeat
|
|
38
|
+
.command('run-now')
|
|
39
|
+
.description('Trigger an immediate heartbeat cycle')
|
|
40
|
+
.option('-t, --timeout <ms>', 'Timeout in milliseconds (default: worker built-in 60s)')
|
|
41
|
+
.action(async (options) => {
|
|
42
|
+
const timeout = options.timeout ? parseInt(options.timeout, 10) : undefined;
|
|
43
|
+
await this.executeRunNow(timeout);
|
|
44
|
+
});
|
|
45
|
+
heartbeat
|
|
46
|
+
.command('history')
|
|
47
|
+
.description('Show recent heartbeat results')
|
|
48
|
+
.option('-n, --count <count>', 'Number of entries to show', '10')
|
|
49
|
+
.action(async (options) => {
|
|
50
|
+
await this.executeHistory(parseInt(options.count, 10) || 10);
|
|
51
|
+
});
|
|
52
|
+
heartbeat
|
|
53
|
+
.command('log')
|
|
54
|
+
.description("Show today's daily log entries")
|
|
55
|
+
.option('-d, --date <date>', 'Show log for specific date (YYYY-MM-DD)')
|
|
56
|
+
.action(async (options) => {
|
|
57
|
+
await this.executeLog(options.date);
|
|
58
|
+
});
|
|
59
|
+
heartbeat
|
|
60
|
+
.command('pause')
|
|
61
|
+
.description('Pause the heartbeat worker')
|
|
62
|
+
.action(async () => {
|
|
63
|
+
await this.executePause();
|
|
64
|
+
});
|
|
65
|
+
heartbeat
|
|
66
|
+
.command('resume')
|
|
67
|
+
.description('Resume the heartbeat worker')
|
|
68
|
+
.action(async () => {
|
|
69
|
+
await this.executeResume();
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
// --------------------------------------------------------------------------
|
|
73
|
+
// Subcommand Implementations
|
|
74
|
+
// --------------------------------------------------------------------------
|
|
75
|
+
async executeStatus() {
|
|
76
|
+
try {
|
|
77
|
+
await this.worker.initialize();
|
|
78
|
+
const health = this.worker.getHealth();
|
|
79
|
+
const lastResult = this.worker.lastResult;
|
|
80
|
+
console.log(chalk.blue('\n Heartbeat Scheduler Status'));
|
|
81
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(35)));
|
|
82
|
+
console.log(` Status: ${statusColor(health.status)}`);
|
|
83
|
+
console.log(` Health Score: ${scoreColor(health.healthScore)}${chalk.gray('/100')}`);
|
|
84
|
+
if (this.worker.lastRunAt) {
|
|
85
|
+
const ago = formatRelativeTime(this.worker.lastRunAt);
|
|
86
|
+
console.log(` Last Run: ${chalk.cyan(this.worker.lastRunAt.toISOString().replace('T', ' ').slice(0, 19))} ${chalk.gray(`(${ago})`)}`);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
console.log(` Last Run: ${chalk.gray('never')}`);
|
|
90
|
+
}
|
|
91
|
+
if (this.worker.nextRunAt) {
|
|
92
|
+
const until = formatRelativeTime(this.worker.nextRunAt, true);
|
|
93
|
+
console.log(` Next Run: ${chalk.cyan(this.worker.nextRunAt.toISOString().replace('T', ' ').slice(0, 19))} ${chalk.gray(`(${until})`)}`);
|
|
94
|
+
}
|
|
95
|
+
console.log(` Total Runs: ${chalk.cyan(String(health.totalExecutions))}`);
|
|
96
|
+
const successRate = health.totalExecutions > 0
|
|
97
|
+
? ((health.successfulExecutions / health.totalExecutions) * 100).toFixed(1)
|
|
98
|
+
: '100.0';
|
|
99
|
+
console.log(` Success Rate: ${chalk.cyan(successRate + '%')}`);
|
|
100
|
+
if (lastResult?.metrics?.domainMetrics) {
|
|
101
|
+
const dm = lastResult.metrics.domainMetrics;
|
|
102
|
+
console.log('');
|
|
103
|
+
console.log(chalk.blue(' Last Result:'));
|
|
104
|
+
console.log(` Promoted: ${chalk.cyan(String(dm.promoted ?? 0))} patterns`);
|
|
105
|
+
console.log(` Deprecated: ${chalk.cyan(String(dm.deprecated ?? 0))} patterns`);
|
|
106
|
+
console.log(` Decayed: ${chalk.cyan(String(dm.decayed ?? 0))} patterns`);
|
|
107
|
+
console.log(` Pending Exp: ${chalk.cyan(String(dm.pendingExperiences ?? 0))}`);
|
|
108
|
+
console.log(` Avg Conf: ${chalk.cyan(String(dm.avgConfidence ?? 0))}`);
|
|
109
|
+
}
|
|
110
|
+
console.log('');
|
|
111
|
+
await this.cleanupAndExit(0);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
console.error(chalk.red('\n Failed to get heartbeat status:'), toErrorMessage(error));
|
|
115
|
+
await this.cleanupAndExit(1);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async executeRunNow(timeoutMs) {
|
|
119
|
+
try {
|
|
120
|
+
console.log(chalk.blue('\n Triggering heartbeat cycle...\n'));
|
|
121
|
+
await this.worker.initialize();
|
|
122
|
+
const abortController = new AbortController();
|
|
123
|
+
// Only add external timeout if the user explicitly requests one shorter
|
|
124
|
+
// than the worker's built-in 60s timeout.
|
|
125
|
+
const timeoutHandle = timeoutMs && timeoutMs > 0
|
|
126
|
+
? setTimeout(() => abortController.abort(), timeoutMs)
|
|
127
|
+
: null;
|
|
128
|
+
// Use a real logger that outputs to the console, and a lightweight
|
|
129
|
+
// event bus / memory — the heartbeat worker only needs DB access
|
|
130
|
+
// (which it gets via getUnifiedMemory() internally).
|
|
131
|
+
const result = await this.worker.execute({
|
|
132
|
+
eventBus: { publish: async () => { } },
|
|
133
|
+
memory: {
|
|
134
|
+
get: async () => undefined,
|
|
135
|
+
set: async () => { },
|
|
136
|
+
search: async () => [],
|
|
137
|
+
},
|
|
138
|
+
logger: {
|
|
139
|
+
debug: () => { },
|
|
140
|
+
info: (...args) => console.log(chalk.gray(' [heartbeat]'), ...args),
|
|
141
|
+
warn: (...args) => console.warn(chalk.yellow(' [heartbeat]'), ...args),
|
|
142
|
+
error: (...args) => console.error(chalk.red(' [heartbeat]'), ...args),
|
|
143
|
+
},
|
|
144
|
+
domains: {
|
|
145
|
+
getDomainAPI: () => undefined,
|
|
146
|
+
getDomainHealth: () => ({ status: 'healthy', errors: [] }),
|
|
147
|
+
},
|
|
148
|
+
signal: abortController.signal,
|
|
149
|
+
});
|
|
150
|
+
if (timeoutHandle)
|
|
151
|
+
clearTimeout(timeoutHandle);
|
|
152
|
+
if (result.success) {
|
|
153
|
+
const dm = result.metrics.domainMetrics;
|
|
154
|
+
console.log(chalk.green(' Heartbeat cycle complete.'));
|
|
155
|
+
console.log(` Duration: ${chalk.cyan(formatDuration(result.durationMs))}`);
|
|
156
|
+
console.log(` Health Score: ${scoreColor(result.metrics.healthScore)}${chalk.gray('/100')}`);
|
|
157
|
+
console.log(` Trend: ${trendColor(result.metrics.trend)}`);
|
|
158
|
+
console.log(` Promoted: ${chalk.cyan(String(dm.promoted ?? 0))}`);
|
|
159
|
+
console.log(` Deprecated: ${chalk.cyan(String(dm.deprecated ?? 0))}`);
|
|
160
|
+
console.log(` Decayed: ${chalk.cyan(String(dm.decayed ?? 0))}`);
|
|
161
|
+
console.log(` Findings: ${chalk.cyan(String(result.findings.length))}`);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
console.error(chalk.red(' Heartbeat cycle failed:'), result.error);
|
|
165
|
+
}
|
|
166
|
+
// Store history entry
|
|
167
|
+
storeHistoryEntry(result);
|
|
168
|
+
console.log('');
|
|
169
|
+
await this.cleanupAndExit(result.success ? 0 : 1);
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
console.error(chalk.red('\n Failed to run heartbeat:'), toErrorMessage(error));
|
|
173
|
+
await this.cleanupAndExit(1);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async executeHistory(count) {
|
|
177
|
+
try {
|
|
178
|
+
const entries = loadHistoryEntries(count);
|
|
179
|
+
if (entries.length === 0) {
|
|
180
|
+
console.log(chalk.yellow('\n No heartbeat history found. Run `aqe heartbeat run-now` first.\n'));
|
|
181
|
+
await this.cleanupAndExit(0);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
console.log(chalk.blue(`\n Heartbeat History (last ${entries.length})`));
|
|
185
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(60)));
|
|
186
|
+
for (const entry of entries) {
|
|
187
|
+
const status = entry.success ? chalk.green('OK') : chalk.red('FAIL');
|
|
188
|
+
const ts = entry.timestamp.slice(0, 19).replace('T', ' ');
|
|
189
|
+
const dm = entry.domainMetrics || {};
|
|
190
|
+
console.log(` ${chalk.gray(ts)} ${status} ` +
|
|
191
|
+
`score:${chalk.cyan(String(entry.healthScore))} ` +
|
|
192
|
+
`+${dm.promoted ?? 0}/-${dm.deprecated ?? 0} ` +
|
|
193
|
+
`${chalk.gray(formatDuration(entry.durationMs))}`);
|
|
194
|
+
}
|
|
195
|
+
console.log('');
|
|
196
|
+
await this.cleanupAndExit(0);
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
console.error(chalk.red('\n Failed to load history:'), toErrorMessage(error));
|
|
200
|
+
await this.cleanupAndExit(1);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async executeLog(date) {
|
|
204
|
+
try {
|
|
205
|
+
const targetDate = date || new Date().toISOString().split('T')[0];
|
|
206
|
+
// Validate date format to prevent path traversal (CLI-MCP parity with heartbeat-handlers.ts)
|
|
207
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(targetDate)) {
|
|
208
|
+
console.error(chalk.red(`\n Invalid date format: "${targetDate}". Use YYYY-MM-DD.\n`));
|
|
209
|
+
await this.cleanupAndExit(1);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const logDir = path.join(findProjectRoot(), '.agentic-qe', 'logs');
|
|
213
|
+
const logPath = path.join(logDir, `${targetDate}.md`);
|
|
214
|
+
if (!fs.existsSync(logPath)) {
|
|
215
|
+
console.log(chalk.yellow(`\n No daily log found for ${targetDate}.\n`));
|
|
216
|
+
await this.cleanupAndExit(0);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const content = fs.readFileSync(logPath, 'utf-8');
|
|
220
|
+
console.log(chalk.blue(`\n Daily Log \u2014 ${targetDate}`));
|
|
221
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(40)));
|
|
222
|
+
// Indent and display each line
|
|
223
|
+
for (const line of content.split('\n')) {
|
|
224
|
+
if (line.trim()) {
|
|
225
|
+
console.log(` ${line}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
console.log('');
|
|
229
|
+
await this.cleanupAndExit(0);
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
console.error(chalk.red('\n Failed to read daily log:'), toErrorMessage(error));
|
|
233
|
+
await this.cleanupAndExit(1);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
async executePause() {
|
|
237
|
+
try {
|
|
238
|
+
this.worker.pause();
|
|
239
|
+
console.log(chalk.yellow('\n Heartbeat worker paused.\n'));
|
|
240
|
+
await this.cleanupAndExit(0);
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
console.error(chalk.red('\n Failed to pause heartbeat:'), toErrorMessage(error));
|
|
244
|
+
await this.cleanupAndExit(1);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
async executeResume() {
|
|
248
|
+
try {
|
|
249
|
+
this.worker.resume();
|
|
250
|
+
console.log(chalk.green('\n Heartbeat worker resumed.\n'));
|
|
251
|
+
await this.cleanupAndExit(0);
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
console.error(chalk.red('\n Failed to resume heartbeat:'), toErrorMessage(error));
|
|
255
|
+
await this.cleanupAndExit(1);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
getHelp() {
|
|
259
|
+
return `
|
|
260
|
+
Manage the token-free heartbeat scheduler (Imp-10).
|
|
261
|
+
|
|
262
|
+
The heartbeat runs every 30 minutes performing SQL-only maintenance:
|
|
263
|
+
- Pattern promotion checks
|
|
264
|
+
- Stale pattern deprecation
|
|
265
|
+
- Confidence decay application
|
|
266
|
+
- Experience buffer monitoring
|
|
267
|
+
- Daily Markdown log entries
|
|
268
|
+
|
|
269
|
+
Subcommands:
|
|
270
|
+
status Show heartbeat worker status, health, and schedule
|
|
271
|
+
run-now Trigger an immediate heartbeat cycle
|
|
272
|
+
history Show recent heartbeat results (last 10)
|
|
273
|
+
log Show today's daily log entries
|
|
274
|
+
pause Pause the heartbeat worker
|
|
275
|
+
resume Resume the heartbeat worker
|
|
276
|
+
|
|
277
|
+
Examples:
|
|
278
|
+
aqe heartbeat status
|
|
279
|
+
aqe heartbeat run-now
|
|
280
|
+
aqe heartbeat history -n 5
|
|
281
|
+
aqe heartbeat log
|
|
282
|
+
aqe heartbeat log --date 2026-03-25
|
|
283
|
+
aqe heartbeat pause
|
|
284
|
+
aqe heartbeat resume
|
|
285
|
+
`;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
const MAX_HISTORY_ENTRIES = 100;
|
|
289
|
+
function getHistoryPath() {
|
|
290
|
+
return path.join(findProjectRoot(), '.agentic-qe', 'heartbeat-history.json');
|
|
291
|
+
}
|
|
292
|
+
function storeHistoryEntry(result) {
|
|
293
|
+
try {
|
|
294
|
+
const historyPath = getHistoryPath();
|
|
295
|
+
const dir = path.dirname(historyPath);
|
|
296
|
+
if (!fs.existsSync(dir)) {
|
|
297
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
298
|
+
}
|
|
299
|
+
let entries = [];
|
|
300
|
+
if (fs.existsSync(historyPath)) {
|
|
301
|
+
try {
|
|
302
|
+
entries = JSON.parse(fs.readFileSync(historyPath, 'utf-8'));
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
entries = [];
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
entries.unshift({
|
|
309
|
+
timestamp: result.timestamp.toISOString(),
|
|
310
|
+
success: result.success,
|
|
311
|
+
durationMs: result.durationMs,
|
|
312
|
+
healthScore: result.metrics.healthScore,
|
|
313
|
+
domainMetrics: result.metrics.domainMetrics,
|
|
314
|
+
});
|
|
315
|
+
// Prune to max entries
|
|
316
|
+
if (entries.length > MAX_HISTORY_ENTRIES) {
|
|
317
|
+
entries = entries.slice(0, MAX_HISTORY_ENTRIES);
|
|
318
|
+
}
|
|
319
|
+
fs.writeFileSync(historyPath, JSON.stringify(entries, null, 2));
|
|
320
|
+
}
|
|
321
|
+
catch {
|
|
322
|
+
// Non-critical: don't fail the command if history persistence fails
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
function loadHistoryEntries(count) {
|
|
326
|
+
try {
|
|
327
|
+
const historyPath = getHistoryPath();
|
|
328
|
+
if (!fs.existsSync(historyPath)) {
|
|
329
|
+
return [];
|
|
330
|
+
}
|
|
331
|
+
const entries = JSON.parse(fs.readFileSync(historyPath, 'utf-8'));
|
|
332
|
+
return entries.slice(0, count);
|
|
333
|
+
}
|
|
334
|
+
catch {
|
|
335
|
+
return [];
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
// ============================================================================
|
|
339
|
+
// Display Helpers
|
|
340
|
+
// ============================================================================
|
|
341
|
+
function statusColor(status) {
|
|
342
|
+
switch (status) {
|
|
343
|
+
case 'idle': return chalk.cyan(status);
|
|
344
|
+
case 'running': return chalk.yellow(status);
|
|
345
|
+
case 'paused': return chalk.yellow(status);
|
|
346
|
+
case 'stopped': return chalk.gray(status);
|
|
347
|
+
case 'error': return chalk.red(status);
|
|
348
|
+
default: return chalk.white(status);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
function scoreColor(score) {
|
|
352
|
+
if (score >= 80)
|
|
353
|
+
return chalk.green(String(score));
|
|
354
|
+
if (score >= 50)
|
|
355
|
+
return chalk.yellow(String(score));
|
|
356
|
+
return chalk.red(String(score));
|
|
357
|
+
}
|
|
358
|
+
function trendColor(trend) {
|
|
359
|
+
switch (trend) {
|
|
360
|
+
case 'improving': return chalk.green(trend);
|
|
361
|
+
case 'stable': return chalk.cyan(trend);
|
|
362
|
+
case 'degrading': return chalk.red(trend);
|
|
363
|
+
default: return chalk.gray(trend);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
function formatRelativeTime(date, future = false) {
|
|
367
|
+
const diffMs = future ? date.getTime() - Date.now() : Date.now() - date.getTime();
|
|
368
|
+
if (diffMs < 0)
|
|
369
|
+
return future ? 'now' : 'just now';
|
|
370
|
+
if (diffMs < 60_000)
|
|
371
|
+
return `${Math.floor(diffMs / 1000)}s ${future ? 'from now' : 'ago'}`;
|
|
372
|
+
if (diffMs < 3_600_000)
|
|
373
|
+
return `${Math.floor(diffMs / 60_000)}m ${future ? 'from now' : 'ago'}`;
|
|
374
|
+
return `${Math.floor(diffMs / 3_600_000)}h ${future ? 'from now' : 'ago'}`;
|
|
375
|
+
}
|
|
376
|
+
// ============================================================================
|
|
377
|
+
// Factory
|
|
378
|
+
// ============================================================================
|
|
379
|
+
export function createHeartbeatHandler(cleanupAndExit) {
|
|
380
|
+
return new HeartbeatHandler(cleanupAndExit);
|
|
381
|
+
}
|
|
382
|
+
//# sourceMappingURL=heartbeat-handler.js.map
|
|
@@ -12,4 +12,6 @@ export { DomainHandler, createDomainHandler } from './domain-handler.js';
|
|
|
12
12
|
export { ProtocolHandler, createProtocolHandler } from './protocol-handler.js';
|
|
13
13
|
export { BrainHandler, createBrainHandler } from './brain-handler.js';
|
|
14
14
|
export { HypergraphHandler, createHypergraphHandler } from './hypergraph-handler.js';
|
|
15
|
+
export { HeartbeatHandler, createHeartbeatHandler } from './heartbeat-handler.js';
|
|
16
|
+
export { RoutingHandler, createRoutingHandler } from './routing-handler.js';
|
|
15
17
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -14,4 +14,6 @@ export { DomainHandler, createDomainHandler } from './domain-handler.js';
|
|
|
14
14
|
export { ProtocolHandler, createProtocolHandler } from './protocol-handler.js';
|
|
15
15
|
export { BrainHandler, createBrainHandler } from './brain-handler.js';
|
|
16
16
|
export { HypergraphHandler, createHypergraphHandler } from './hypergraph-handler.js';
|
|
17
|
+
export { HeartbeatHandler, createHeartbeatHandler } from './heartbeat-handler.js';
|
|
18
|
+
export { RoutingHandler, createRoutingHandler } from './routing-handler.js';
|
|
17
19
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agentic QE v3 - Routing Command Handler
|
|
3
|
+
* Imp-18: Economic Routing Model CLI Integration
|
|
4
|
+
*
|
|
5
|
+
* Handles the 'aqe routing' command with subcommands:
|
|
6
|
+
* economics, accuracy, metrics
|
|
7
|
+
*/
|
|
8
|
+
import { Command } from 'commander';
|
|
9
|
+
import { ICommandHandler, CLIContext } from './interfaces.js';
|
|
10
|
+
export declare class RoutingHandler implements ICommandHandler {
|
|
11
|
+
readonly name = "routing";
|
|
12
|
+
readonly description = "View routing performance, economics, and accuracy";
|
|
13
|
+
private cleanupAndExit;
|
|
14
|
+
constructor(cleanupAndExit: (code: number) => Promise<never>);
|
|
15
|
+
getHelp(): string;
|
|
16
|
+
register(program: Command, _context: CLIContext): void;
|
|
17
|
+
private executeEconomics;
|
|
18
|
+
private executeAccuracy;
|
|
19
|
+
private executeMetrics;
|
|
20
|
+
}
|
|
21
|
+
export declare function createRoutingHandler(cleanupAndExit: (code: number) => Promise<never>): RoutingHandler;
|
|
22
|
+
//# sourceMappingURL=routing-handler.d.ts.map
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agentic QE v3 - Routing Command Handler
|
|
3
|
+
* Imp-18: Economic Routing Model CLI Integration
|
|
4
|
+
*
|
|
5
|
+
* Handles the 'aqe routing' command with subcommands:
|
|
6
|
+
* economics, accuracy, metrics
|
|
7
|
+
*/
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import { toErrorMessage } from '../../shared/error-utils.js';
|
|
10
|
+
import { createRoutingFeedbackCollector } from '../../routing/routing-feedback.js';
|
|
11
|
+
import { getGlobalCostTracker } from '../../shared/llm/cost-tracker.js';
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Routing Handler
|
|
14
|
+
// ============================================================================
|
|
15
|
+
export class RoutingHandler {
|
|
16
|
+
name = 'routing';
|
|
17
|
+
description = 'View routing performance, economics, and accuracy';
|
|
18
|
+
cleanupAndExit;
|
|
19
|
+
constructor(cleanupAndExit) {
|
|
20
|
+
this.cleanupAndExit = cleanupAndExit;
|
|
21
|
+
}
|
|
22
|
+
getHelp() {
|
|
23
|
+
return [
|
|
24
|
+
'aqe routing economics [--complexity <0-1>] [--json] Show tier efficiency & budget',
|
|
25
|
+
'aqe routing accuracy [--json] Show routing accuracy analysis',
|
|
26
|
+
'aqe routing metrics [--json] Show per-agent performance',
|
|
27
|
+
].join('\n');
|
|
28
|
+
}
|
|
29
|
+
register(program, _context) {
|
|
30
|
+
const routing = program
|
|
31
|
+
.command('routing')
|
|
32
|
+
.description(this.description);
|
|
33
|
+
routing
|
|
34
|
+
.command('economics')
|
|
35
|
+
.description('Show economic routing report: tier efficiency, budget, savings')
|
|
36
|
+
.option('-c, --complexity <value>', 'Task complexity for scoring (0-1)', '0.5')
|
|
37
|
+
.option('--json', 'Output as JSON')
|
|
38
|
+
.action(async (options) => {
|
|
39
|
+
await this.executeEconomics(parseFloat(options.complexity) || 0.5, !!options.json);
|
|
40
|
+
});
|
|
41
|
+
routing
|
|
42
|
+
.command('accuracy')
|
|
43
|
+
.description('Show routing accuracy analysis')
|
|
44
|
+
.option('--json', 'Output as JSON')
|
|
45
|
+
.action(async (options) => {
|
|
46
|
+
await this.executeAccuracy(!!options.json);
|
|
47
|
+
});
|
|
48
|
+
routing
|
|
49
|
+
.command('metrics')
|
|
50
|
+
.description('Show per-agent performance metrics')
|
|
51
|
+
.option('--json', 'Output as JSON')
|
|
52
|
+
.action(async (options) => {
|
|
53
|
+
await this.executeMetrics(!!options.json);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
// --------------------------------------------------------------------------
|
|
57
|
+
// Economics
|
|
58
|
+
// --------------------------------------------------------------------------
|
|
59
|
+
async executeEconomics(complexity, json) {
|
|
60
|
+
try {
|
|
61
|
+
const collector = createRoutingFeedbackCollector(100);
|
|
62
|
+
await collector.initialize();
|
|
63
|
+
collector.enableEconomicRouting({}, getGlobalCostTracker());
|
|
64
|
+
const report = collector.getEconomicReport();
|
|
65
|
+
if (!report) {
|
|
66
|
+
console.error(chalk.red('\n Economic routing is not available.\n'));
|
|
67
|
+
await this.cleanupAndExit(1);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (json) {
|
|
71
|
+
console.log(JSON.stringify(report, (_k, v) => (v === Infinity ? 'Infinity' : v), 2));
|
|
72
|
+
await this.cleanupAndExit(0);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
console.log(chalk.blue('\n Economic Routing Report'));
|
|
76
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(50)));
|
|
77
|
+
// Tier efficiency table
|
|
78
|
+
console.log(chalk.white('\n Tier Efficiency (complexity=' + complexity.toFixed(1) + '):\n'));
|
|
79
|
+
console.log(chalk.gray(' Tier Quality Cost/Task Q/$ Score'));
|
|
80
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(50)));
|
|
81
|
+
const scores = collector.getEconomicScore(complexity) ?? report.tierEfficiency;
|
|
82
|
+
for (const s of scores) {
|
|
83
|
+
const qpd = isFinite(s.qualityPerDollar) ? s.qualityPerDollar.toFixed(1) : '\u221E';
|
|
84
|
+
console.log(` ${padRight(s.tier, 10)}` +
|
|
85
|
+
`${chalk.cyan(s.qualityScore.toFixed(2))} ` +
|
|
86
|
+
`$${s.estimatedCostUsd.toFixed(4)} ` +
|
|
87
|
+
`${chalk.yellow(padLeft(qpd, 8))} ` +
|
|
88
|
+
`${scoreColor(s.economicScore)}`);
|
|
89
|
+
}
|
|
90
|
+
// Budget
|
|
91
|
+
console.log(chalk.white('\n Budget:'));
|
|
92
|
+
console.log(` Hourly cost: $${report.currentHourlyCostUsd.toFixed(4)}`);
|
|
93
|
+
console.log(` Daily cost: $${report.currentDailyCostUsd.toFixed(4)}`);
|
|
94
|
+
if (report.budgetRemaining.hourly !== null) {
|
|
95
|
+
console.log(` Hourly left: $${report.budgetRemaining.hourly.toFixed(4)}`);
|
|
96
|
+
}
|
|
97
|
+
if (report.budgetRemaining.daily !== null) {
|
|
98
|
+
console.log(` Daily left: $${report.budgetRemaining.daily.toFixed(4)}`);
|
|
99
|
+
}
|
|
100
|
+
// Recommendation
|
|
101
|
+
console.log(chalk.white('\n Recommendation:'));
|
|
102
|
+
console.log(` ${chalk.green(report.recommendation)}`);
|
|
103
|
+
if (report.savingsOpportunity) {
|
|
104
|
+
console.log(chalk.white('\n Savings Opportunity:'));
|
|
105
|
+
console.log(` ${chalk.yellow(report.savingsOpportunity.description)}`);
|
|
106
|
+
}
|
|
107
|
+
console.log('');
|
|
108
|
+
await this.cleanupAndExit(0);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
console.error(chalk.red('\n Failed to get economic report:'), toErrorMessage(error));
|
|
112
|
+
await this.cleanupAndExit(1);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// --------------------------------------------------------------------------
|
|
116
|
+
// Accuracy
|
|
117
|
+
// --------------------------------------------------------------------------
|
|
118
|
+
async executeAccuracy(json) {
|
|
119
|
+
try {
|
|
120
|
+
const collector = createRoutingFeedbackCollector(10000);
|
|
121
|
+
await collector.initialize();
|
|
122
|
+
const accuracy = collector.analyzeRoutingAccuracy();
|
|
123
|
+
if (json) {
|
|
124
|
+
console.log(JSON.stringify(accuracy, null, 2));
|
|
125
|
+
await this.cleanupAndExit(0);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
console.log(chalk.blue('\n Routing Accuracy Analysis'));
|
|
129
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(40)));
|
|
130
|
+
console.log(` Total outcomes: ${chalk.cyan(String(accuracy.totalOutcomes))}`);
|
|
131
|
+
console.log(` Followed recs: ${chalk.cyan(String(accuracy.followedRecommendations))}`);
|
|
132
|
+
console.log(` Override rate: ${chalk.yellow((accuracy.overrideRate * 100).toFixed(1) + '%')}`);
|
|
133
|
+
console.log(` Rec success rate: ${scoreColor100(accuracy.recommendationSuccessRate * 100)}`);
|
|
134
|
+
console.log(` Override success rate: ${scoreColor100(accuracy.overrideSuccessRate * 100)}`);
|
|
135
|
+
console.log(` Confidence correlation: ${chalk.cyan(accuracy.confidenceCorrelation.toFixed(3))}`);
|
|
136
|
+
// Recommendations
|
|
137
|
+
const recs = collector.getImprovementRecommendations();
|
|
138
|
+
if (recs.length > 0) {
|
|
139
|
+
console.log(chalk.white('\n Recommendations:'));
|
|
140
|
+
for (const rec of recs) {
|
|
141
|
+
console.log(` ${chalk.gray('\u2022')} ${rec}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
console.log('');
|
|
145
|
+
await this.cleanupAndExit(0);
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
console.error(chalk.red('\n Failed to analyze routing accuracy:'), toErrorMessage(error));
|
|
149
|
+
await this.cleanupAndExit(1);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// --------------------------------------------------------------------------
|
|
153
|
+
// Metrics
|
|
154
|
+
// --------------------------------------------------------------------------
|
|
155
|
+
async executeMetrics(json) {
|
|
156
|
+
try {
|
|
157
|
+
const collector = createRoutingFeedbackCollector(10000);
|
|
158
|
+
await collector.initialize();
|
|
159
|
+
const metrics = collector.getAllAgentMetrics();
|
|
160
|
+
if (json) {
|
|
161
|
+
console.log(JSON.stringify(metrics, null, 2));
|
|
162
|
+
await this.cleanupAndExit(0);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (metrics.length === 0) {
|
|
166
|
+
console.log(chalk.yellow('\n No routing metrics available yet. Run some QE tasks first.\n'));
|
|
167
|
+
await this.cleanupAndExit(0);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
console.log(chalk.blue('\n Agent Routing Metrics'));
|
|
171
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(60)));
|
|
172
|
+
console.log(chalk.gray(' Agent Tasks Success Quality Trend'));
|
|
173
|
+
console.log(chalk.gray(' ' + '\u2500'.repeat(60)));
|
|
174
|
+
for (const m of metrics.slice(0, 20)) {
|
|
175
|
+
const trend = m.trend === 'improving' ? chalk.green('\u2191')
|
|
176
|
+
: m.trend === 'declining' ? chalk.red('\u2193')
|
|
177
|
+
: chalk.gray('\u2192');
|
|
178
|
+
console.log(` ${padRight(m.agentId, 24)}` +
|
|
179
|
+
`${padLeft(String(m.totalTasks), 5)} ` +
|
|
180
|
+
`${scoreColor100(m.successRate * 100)} ` +
|
|
181
|
+
`${chalk.cyan(m.avgQualityScore.toFixed(2))} ` +
|
|
182
|
+
`${trend} ${m.trend}`);
|
|
183
|
+
}
|
|
184
|
+
const stats = collector.getStats();
|
|
185
|
+
console.log(chalk.gray(`\n ${stats.totalOutcomes} total outcomes, ${stats.uniqueAgentsUsed} agents`));
|
|
186
|
+
console.log('');
|
|
187
|
+
await this.cleanupAndExit(0);
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
console.error(chalk.red('\n Failed to get agent metrics:'), toErrorMessage(error));
|
|
191
|
+
await this.cleanupAndExit(1);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// ============================================================================
|
|
196
|
+
// Helpers
|
|
197
|
+
// ============================================================================
|
|
198
|
+
function padRight(str, len) {
|
|
199
|
+
return str.length >= len ? str : str + ' '.repeat(len - str.length);
|
|
200
|
+
}
|
|
201
|
+
function padLeft(str, len) {
|
|
202
|
+
return str.length >= len ? str : ' '.repeat(len - str.length) + str;
|
|
203
|
+
}
|
|
204
|
+
function scoreColor(score) {
|
|
205
|
+
const pct = score * 100;
|
|
206
|
+
const str = score.toFixed(3);
|
|
207
|
+
if (pct >= 70)
|
|
208
|
+
return chalk.green(str);
|
|
209
|
+
if (pct >= 40)
|
|
210
|
+
return chalk.yellow(str);
|
|
211
|
+
return chalk.red(str);
|
|
212
|
+
}
|
|
213
|
+
function scoreColor100(pct) {
|
|
214
|
+
const str = pct.toFixed(1) + '%';
|
|
215
|
+
if (pct >= 70)
|
|
216
|
+
return chalk.green(str);
|
|
217
|
+
if (pct >= 40)
|
|
218
|
+
return chalk.yellow(str);
|
|
219
|
+
return chalk.red(str);
|
|
220
|
+
}
|
|
221
|
+
// ============================================================================
|
|
222
|
+
// Factory
|
|
223
|
+
// ============================================================================
|
|
224
|
+
export function createRoutingHandler(cleanupAndExit) {
|
|
225
|
+
return new RoutingHandler(cleanupAndExit);
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=routing-handler.js.map
|
package/dist/cli/index.js
CHANGED
|
@@ -822,6 +822,7 @@ import { createPlatformCommand } from './commands/platform.js';
|
|
|
822
822
|
import { createProveCommand } from './commands/prove.js';
|
|
823
823
|
import { createRuVectorCommand } from './commands/ruvector-commands.js';
|
|
824
824
|
import { createAuditCommand } from './commands/audit.js';
|
|
825
|
+
import { createPipelineCommand } from './commands/pipeline.js';
|
|
825
826
|
program.addCommand(createTokenUsageCommand());
|
|
826
827
|
program.addCommand(createLLMRouterCommand());
|
|
827
828
|
program.addCommand(createSyncCommands());
|
|
@@ -833,6 +834,7 @@ program.addCommand(createPlatformCommand());
|
|
|
833
834
|
program.addCommand(createProveCommand(context, cleanupAndExit, ensureInitialized));
|
|
834
835
|
program.addCommand(createRuVectorCommand());
|
|
835
836
|
program.addCommand(createAuditCommand(context, cleanupAndExit, ensureInitialized));
|
|
837
|
+
program.addCommand(createPipelineCommand(context, cleanupAndExit, ensureInitialized));
|
|
836
838
|
// ============================================================================
|
|
837
839
|
// Shutdown Handlers
|
|
838
840
|
// ============================================================================
|