nexus-agents 2.26.1 → 2.28.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/README.md +7 -7
- package/dist/{chunk-X33QNBGA.js → chunk-E7EX2KQJ.js} +3 -5
- package/dist/{chunk-X33QNBGA.js.map → chunk-E7EX2KQJ.js.map} +1 -1
- package/dist/{chunk-BOWNZMPH.js → chunk-L2SHSW4T.js} +3017 -1300
- package/dist/chunk-L2SHSW4T.js.map +1 -0
- package/dist/{chunk-ARNVVQ5W.js → chunk-LKSTILEE.js} +1213 -117
- package/dist/chunk-LKSTILEE.js.map +1 -0
- package/dist/{chunk-L3LQ3RP5.js → chunk-QZEAD6AG.js} +10339 -6289
- package/dist/chunk-QZEAD6AG.js.map +1 -0
- package/dist/{chunk-LCHCASB7.js → chunk-UGNLR4NZ.js} +2 -2
- package/dist/{chunk-UVQ7R4C4.js → chunk-YSDUVCCZ.js} +137 -717
- package/dist/chunk-YSDUVCCZ.js.map +1 -0
- package/dist/cli.d.ts +8 -1
- package/dist/cli.js +644 -216
- package/dist/cli.js.map +1 -1
- package/dist/{dist-Y5F6UM2N.js → dist-H5XNXVAV.js} +1384 -1295
- package/dist/dist-H5XNXVAV.js.map +1 -0
- package/dist/doctor-deep-BDE2PHVX.js +11 -0
- package/dist/index.d.ts +4299 -7411
- package/dist/index.js +588 -132
- package/dist/index.js.map +1 -1
- package/dist/{setup-command-VNF3KTCJ.js → setup-command-SS7LMN7Y.js} +5 -6
- package/dist/setup-config-DSMOOLVW.js +9 -0
- package/dist/workflows/templates/code-review.yaml +1 -1
- package/dist/workflows/templates/refactoring.yaml +1 -1
- package/dist/workflows/templates/research-review.yaml +19 -4
- package/dist/workflows/templates/security-audit.yaml +1 -1
- package/dist/workflows/templates/standards-review.yaml +1 -1
- package/package.json +12 -12
- package/src/workflows/templates/code-review.yaml +1 -1
- package/src/workflows/templates/refactoring.yaml +1 -1
- package/src/workflows/templates/research-review.yaml +19 -4
- package/src/workflows/templates/security-audit.yaml +1 -1
- package/src/workflows/templates/standards-review.yaml +1 -1
- package/dist/chunk-ARNVVQ5W.js.map +0 -1
- package/dist/chunk-BOWNZMPH.js.map +0 -1
- package/dist/chunk-L3LQ3RP5.js.map +0 -1
- package/dist/chunk-LCDOP543.js +0 -365
- package/dist/chunk-LCDOP543.js.map +0 -1
- package/dist/chunk-PGNRXCYY.js +0 -776
- package/dist/chunk-PGNRXCYY.js.map +0 -1
- package/dist/chunk-UVQ7R4C4.js.map +0 -1
- package/dist/dist-Y5F6UM2N.js.map +0 -1
- package/dist/doctor-deep-I2J5CRFG.js +0 -13
- package/dist/setup-config-VQSWWJ5O.js +0 -9
- /package/dist/{chunk-LCHCASB7.js.map → chunk-UGNLR4NZ.js.map} +0 -0
- /package/dist/{doctor-deep-I2J5CRFG.js.map → doctor-deep-BDE2PHVX.js.map} +0 -0
- /package/dist/{setup-command-VNF3KTCJ.js.map → setup-command-SS7LMN7Y.js.map} +0 -0
- /package/dist/{setup-config-VQSWWJ5O.js.map → setup-config-DSMOOLVW.js.map} +0 -0
package/dist/chunk-PGNRXCYY.js
DELETED
|
@@ -1,776 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
HEARTBEAT_TIMEOUTS
|
|
3
|
-
} from "./chunk-LCDOP543.js";
|
|
4
|
-
import {
|
|
5
|
-
CLI_NAMES,
|
|
6
|
-
RateLimitError,
|
|
7
|
-
TASK_CATEGORIES,
|
|
8
|
-
computeAdaptiveThresholds,
|
|
9
|
-
getErrorMessage,
|
|
10
|
-
getOutcomeStore,
|
|
11
|
-
getSpecialization,
|
|
12
|
-
getTimeProvider
|
|
13
|
-
} from "./chunk-BOWNZMPH.js";
|
|
14
|
-
|
|
15
|
-
// src/mcp/tools/weather-report-types.ts
|
|
16
|
-
import { z } from "zod";
|
|
17
|
-
var WeatherReportInputSchema = z.object({
|
|
18
|
-
/** Filter by CLI name. */
|
|
19
|
-
cli: z.enum(CLI_NAMES).optional().describe("Filter by CLI"),
|
|
20
|
-
/** Filter by task category. */
|
|
21
|
-
category: z.enum([
|
|
22
|
-
"architecture",
|
|
23
|
-
"code_generation",
|
|
24
|
-
"code_review",
|
|
25
|
-
"research",
|
|
26
|
-
"security_review",
|
|
27
|
-
"planning",
|
|
28
|
-
"documentation",
|
|
29
|
-
"testing",
|
|
30
|
-
"devops",
|
|
31
|
-
"exploration"
|
|
32
|
-
]).optional().describe("Filter by task category"),
|
|
33
|
-
/** Include adaptive routing bonus data. */
|
|
34
|
-
includeAdaptive: z.boolean().optional().default(true).describe("Include adaptive routing bonuses (default: true)")
|
|
35
|
-
});
|
|
36
|
-
var WeatherReportConfigSchema = z.object({
|
|
37
|
-
/** Minimum observations before adjusting bonuses (lowered for faster activation). */
|
|
38
|
-
coldStartThreshold: z.number().int().min(1).max(1e3).default(3),
|
|
39
|
-
/** Exploration rate: fraction of random routing (0.0-1.0). */
|
|
40
|
-
explorationRate: z.number().min(0).max(1).default(0.1),
|
|
41
|
-
/** Max adaptive bonus adjustment (+/-). */
|
|
42
|
-
maxBonusAdjustment: z.number().min(0).max(10).default(5)
|
|
43
|
-
});
|
|
44
|
-
function createDefaultWeatherConfig() {
|
|
45
|
-
return WeatherReportConfigSchema.parse({});
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// src/mcp/gateway/gateway-keywords.ts
|
|
49
|
-
var SECURITY_KEYWORDS = [
|
|
50
|
-
"security",
|
|
51
|
-
"vulnerabilit",
|
|
52
|
-
"cve-",
|
|
53
|
-
"exploit",
|
|
54
|
-
"injection",
|
|
55
|
-
"xss",
|
|
56
|
-
"csrf",
|
|
57
|
-
"auth",
|
|
58
|
-
"penetration",
|
|
59
|
-
"threat",
|
|
60
|
-
"malware",
|
|
61
|
-
"credentials",
|
|
62
|
-
"secrets",
|
|
63
|
-
"encryption",
|
|
64
|
-
"certificate"
|
|
65
|
-
];
|
|
66
|
-
var ARCHITECTURE_KEYWORDS = [
|
|
67
|
-
"architecture",
|
|
68
|
-
"breaking change",
|
|
69
|
-
"breaking api",
|
|
70
|
-
"api change",
|
|
71
|
-
"migration",
|
|
72
|
-
"refactor.*system",
|
|
73
|
-
"redesign",
|
|
74
|
-
"microservice",
|
|
75
|
-
"monolith",
|
|
76
|
-
"deprecation",
|
|
77
|
-
"schema change",
|
|
78
|
-
"database",
|
|
79
|
-
"infrastructure"
|
|
80
|
-
];
|
|
81
|
-
var PROMOTED_ROLES = /* @__PURE__ */ new Set(["security_expert", "architecture_expert"]);
|
|
82
|
-
|
|
83
|
-
// src/mcp/gateway/tier-classifier.ts
|
|
84
|
-
var TOOL_TIER_MAP = {
|
|
85
|
-
// Tier 1 — Read-only
|
|
86
|
-
list_experts: 1 /* DIRECT */,
|
|
87
|
-
list_workflows: 1 /* DIRECT */,
|
|
88
|
-
memory_query: 1 /* DIRECT */,
|
|
89
|
-
memory_stats: 1 /* DIRECT */,
|
|
90
|
-
weather_report: 1 /* DIRECT */,
|
|
91
|
-
research_query: 1 /* DIRECT */,
|
|
92
|
-
research_analyze: 1 /* DIRECT */,
|
|
93
|
-
research_catalog_review: 1 /* DIRECT */,
|
|
94
|
-
// Tier 2 — Model selection
|
|
95
|
-
delegate_to_model: 2 /* ANALYZED */,
|
|
96
|
-
create_expert: 2 /* ANALYZED */,
|
|
97
|
-
execute_expert: 2 /* ANALYZED */,
|
|
98
|
-
research_add: 2 /* ANALYZED */,
|
|
99
|
-
research_discover: 2 /* ANALYZED */,
|
|
100
|
-
registry_import: 2 /* ANALYZED */,
|
|
101
|
-
// Tier 3 — Full orchestration
|
|
102
|
-
orchestrate: 3 /* ORCHESTRATED */,
|
|
103
|
-
consensus_vote: 3 /* ORCHESTRATED */,
|
|
104
|
-
execute_spec: 3 /* ORCHESTRATED */,
|
|
105
|
-
run_workflow: 3 /* ORCHESTRATED */,
|
|
106
|
-
run_graph_workflow: 3 /* ORCHESTRATED */,
|
|
107
|
-
issue_triage: 3 /* ORCHESTRATED */
|
|
108
|
-
};
|
|
109
|
-
function classifyRequestTier(toolName, params, overrides) {
|
|
110
|
-
const defaultTier = TOOL_TIER_MAP[toolName] ?? 2 /* ANALYZED */;
|
|
111
|
-
const effectiveTier = overrides?.[toolName] ?? defaultTier;
|
|
112
|
-
if (defaultTier === 1 /* DIRECT */ && effectiveTier === 1 /* DIRECT */) {
|
|
113
|
-
return 1 /* DIRECT */;
|
|
114
|
-
}
|
|
115
|
-
if (effectiveTier < 3 /* ORCHESTRATED */ && shouldPromote(params)) {
|
|
116
|
-
return 3 /* ORCHESTRATED */;
|
|
117
|
-
}
|
|
118
|
-
return effectiveTier;
|
|
119
|
-
}
|
|
120
|
-
function shouldPromote(params) {
|
|
121
|
-
const role = params["role"];
|
|
122
|
-
if (typeof role === "string" && PROMOTED_ROLES.has(role)) {
|
|
123
|
-
return true;
|
|
124
|
-
}
|
|
125
|
-
const textFields = ["task", "proposal", "prompt"];
|
|
126
|
-
for (const field of textFields) {
|
|
127
|
-
const value = params[field];
|
|
128
|
-
if (typeof value === "string" && containsPromotionKeyword(value)) {
|
|
129
|
-
return true;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
var TIER_NAME_TO_ENUM = {
|
|
135
|
-
DIRECT: 1 /* DIRECT */,
|
|
136
|
-
ANALYZED: 2 /* ANALYZED */,
|
|
137
|
-
ORCHESTRATED: 3 /* ORCHESTRATED */
|
|
138
|
-
};
|
|
139
|
-
function parseTierOverrides(overrides) {
|
|
140
|
-
if (overrides === void 0) return void 0;
|
|
141
|
-
const result = {};
|
|
142
|
-
for (const [tool, tierName] of Object.entries(overrides)) {
|
|
143
|
-
const tier = TIER_NAME_TO_ENUM[tierName];
|
|
144
|
-
if (tier !== void 0) {
|
|
145
|
-
result[tool] = tier;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
return Object.keys(result).length > 0 ? result : void 0;
|
|
149
|
-
}
|
|
150
|
-
function containsPromotionKeyword(text) {
|
|
151
|
-
const lower = text.toLowerCase();
|
|
152
|
-
for (const kw of SECURITY_KEYWORDS) {
|
|
153
|
-
if (lower.includes(kw)) return true;
|
|
154
|
-
}
|
|
155
|
-
for (const kw of ARCHITECTURE_KEYWORDS) {
|
|
156
|
-
if (new RegExp(kw, "i").test(text)) return true;
|
|
157
|
-
}
|
|
158
|
-
return false;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// src/mcp/gateway/tier-recommender.ts
|
|
162
|
-
var DEFAULT_CONFIG = {
|
|
163
|
-
minSamples: 20,
|
|
164
|
-
promoteFailureRate: 0.3,
|
|
165
|
-
demoteSuccessRate: 0.95,
|
|
166
|
-
demoteMinSamples: 50
|
|
167
|
-
};
|
|
168
|
-
function getCategoryTier(category) {
|
|
169
|
-
const categoryToolMap = {
|
|
170
|
-
research: "research_query",
|
|
171
|
-
exploration: "research_discover",
|
|
172
|
-
code_generation: "delegate_to_model",
|
|
173
|
-
code_review: "run_workflow",
|
|
174
|
-
architecture: "orchestrate",
|
|
175
|
-
security_review: "orchestrate",
|
|
176
|
-
planning: "orchestrate",
|
|
177
|
-
documentation: "run_workflow",
|
|
178
|
-
testing: "run_workflow",
|
|
179
|
-
devops: "delegate_to_model"
|
|
180
|
-
};
|
|
181
|
-
const tool = categoryToolMap[category];
|
|
182
|
-
if (tool !== void 0) {
|
|
183
|
-
return TOOL_TIER_MAP[tool] ?? 2 /* ANALYZED */;
|
|
184
|
-
}
|
|
185
|
-
return 2 /* ANALYZED */;
|
|
186
|
-
}
|
|
187
|
-
function generateTierRecommendations(summary, config) {
|
|
188
|
-
const cfg = { ...DEFAULT_CONFIG, ...config };
|
|
189
|
-
const recommendations = [];
|
|
190
|
-
for (const [category, stats] of summary.byCategory) {
|
|
191
|
-
const rec = evaluateCategory(category, stats, cfg);
|
|
192
|
-
if (rec !== null) recommendations.push(rec);
|
|
193
|
-
}
|
|
194
|
-
return recommendations;
|
|
195
|
-
}
|
|
196
|
-
function evaluateCategory(category, stats, cfg) {
|
|
197
|
-
if (stats.count < cfg.minSamples) return null;
|
|
198
|
-
const currentTier = getCategoryTier(category);
|
|
199
|
-
const failureRate = 1 - stats.successRate;
|
|
200
|
-
if (failureRate > cfg.promoteFailureRate && currentTier < 3 /* ORCHESTRATED */) {
|
|
201
|
-
const recommendedTier = currentTier + 1;
|
|
202
|
-
return {
|
|
203
|
-
category,
|
|
204
|
-
currentTier,
|
|
205
|
-
recommendedTier,
|
|
206
|
-
direction: "promote",
|
|
207
|
-
successRate: stats.successRate,
|
|
208
|
-
sampleCount: stats.count,
|
|
209
|
-
reason: `${category}: ${(failureRate * 100).toFixed(0)}% failure rate over ${String(stats.count)} tasks \u2014 recommend promoting to Tier ${String(recommendedTier)}`
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
if (stats.successRate > cfg.demoteSuccessRate && stats.count >= cfg.demoteMinSamples && currentTier > 1 /* DIRECT */) {
|
|
213
|
-
const recommendedTier = currentTier - 1;
|
|
214
|
-
return {
|
|
215
|
-
category,
|
|
216
|
-
currentTier,
|
|
217
|
-
recommendedTier,
|
|
218
|
-
direction: "demote",
|
|
219
|
-
successRate: stats.successRate,
|
|
220
|
-
sampleCount: stats.count,
|
|
221
|
-
reason: `${category}: ${(stats.successRate * 100).toFixed(0)}% success rate over ${String(stats.count)} tasks \u2014 eligible for demotion to Tier ${String(recommendedTier)}`
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
return null;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// src/adapters/rate-limit-detector.ts
|
|
228
|
-
var RATE_LIMIT_PATTERNS = [
|
|
229
|
-
"rate limit",
|
|
230
|
-
"rate_limit",
|
|
231
|
-
"too many requests",
|
|
232
|
-
"429",
|
|
233
|
-
"quota exceeded",
|
|
234
|
-
"throttl",
|
|
235
|
-
"requests per minute",
|
|
236
|
-
"tokens per minute"
|
|
237
|
-
];
|
|
238
|
-
function isRateLimitLikeError(error) {
|
|
239
|
-
const message = getErrorMessage(error).toLowerCase();
|
|
240
|
-
return RATE_LIMIT_PATTERNS.some((p) => message.includes(p));
|
|
241
|
-
}
|
|
242
|
-
function parseRetryAfterMs(errorMessage) {
|
|
243
|
-
const lower = errorMessage.toLowerCase();
|
|
244
|
-
const secondsMatch = /retry[- ]?after[:\s]+(\d+(?:\.\d+)?)\s*(?:s|sec)/i.exec(lower);
|
|
245
|
-
if (secondsMatch !== null) {
|
|
246
|
-
const parsed = parseFloat(secondsMatch[1] ?? "0");
|
|
247
|
-
return Math.ceil(parsed * 1e3);
|
|
248
|
-
}
|
|
249
|
-
const msMatch = /(?:retry[- ]?after|wait)[:\s]+(\d+)\s*(?:ms|millisecond)/i.exec(lower);
|
|
250
|
-
if (msMatch !== null) {
|
|
251
|
-
return parseInt(msMatch[1] ?? "0", 10);
|
|
252
|
-
}
|
|
253
|
-
const againMatch = /try again in (\d+(?:\.\d+)?)\s*(?:s|sec)/i.exec(lower);
|
|
254
|
-
if (againMatch !== null) {
|
|
255
|
-
const parsed = parseFloat(againMatch[1] ?? "0");
|
|
256
|
-
return Math.ceil(parsed * 1e3);
|
|
257
|
-
}
|
|
258
|
-
return void 0;
|
|
259
|
-
}
|
|
260
|
-
function toRateLimitError(error, provider) {
|
|
261
|
-
const message = getErrorMessage(error);
|
|
262
|
-
const retryAfterMs = parseRetryAfterMs(message);
|
|
263
|
-
return new RateLimitError(message, {
|
|
264
|
-
...retryAfterMs !== void 0 ? { retryAfterMs } : {},
|
|
265
|
-
...provider !== void 0 ? { provider } : {},
|
|
266
|
-
...error instanceof Error ? { cause: error } : {}
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
var MAX_EVENTS = 200;
|
|
270
|
-
var events = [];
|
|
271
|
-
function recordRateLimitEvent(event) {
|
|
272
|
-
events.push(event);
|
|
273
|
-
if (events.length > MAX_EVENTS) {
|
|
274
|
-
events.splice(0, events.length - MAX_EVENTS);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
function getRateLimitStats() {
|
|
278
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
279
|
-
for (const event of events) {
|
|
280
|
-
const existing = grouped.get(event.provider);
|
|
281
|
-
if (existing !== void 0) {
|
|
282
|
-
existing.push(event);
|
|
283
|
-
} else {
|
|
284
|
-
grouped.set(event.provider, [event]);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
const stats = [];
|
|
288
|
-
for (const [provider, providerEvents] of grouped) {
|
|
289
|
-
const retryValues = providerEvents.map((e) => e.retryAfterMs).filter((v) => v !== void 0);
|
|
290
|
-
const avgRetry = retryValues.length > 0 ? retryValues.reduce((a, b) => a + b, 0) / retryValues.length : void 0;
|
|
291
|
-
const lastEvent = providerEvents[providerEvents.length - 1];
|
|
292
|
-
stats.push({
|
|
293
|
-
provider,
|
|
294
|
-
totalHits: providerEvents.length,
|
|
295
|
-
lastHitAt: lastEvent?.timestamp ?? 0,
|
|
296
|
-
...avgRetry !== void 0 ? { avgRetryAfterMs: Math.round(avgRetry) } : { avgRetryAfterMs: void 0 }
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
return stats;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// src/mcp/middleware/tool-metrics.ts
|
|
303
|
-
var MAX_ENTRIES = 5e3;
|
|
304
|
-
var metrics = [];
|
|
305
|
-
function recordToolMetric(metric) {
|
|
306
|
-
metrics.push(metric);
|
|
307
|
-
if (metrics.length > MAX_ENTRIES) {
|
|
308
|
-
metrics.splice(0, metrics.length - MAX_ENTRIES);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
function getToolStats() {
|
|
312
|
-
const groups = /* @__PURE__ */ new Map();
|
|
313
|
-
for (const m of metrics) {
|
|
314
|
-
const list = groups.get(m.toolName);
|
|
315
|
-
if (list !== void 0) {
|
|
316
|
-
list.push(m);
|
|
317
|
-
} else {
|
|
318
|
-
groups.set(m.toolName, [m]);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
const stats = [];
|
|
322
|
-
for (const [toolName, list] of groups) {
|
|
323
|
-
const sc = list.filter((m) => m.success).length;
|
|
324
|
-
const td = list.reduce((s, m) => s + m.durationMs, 0);
|
|
325
|
-
stats.push({
|
|
326
|
-
toolName,
|
|
327
|
-
totalCalls: list.length,
|
|
328
|
-
successRate: sc / list.length,
|
|
329
|
-
avgDurationMs: td / list.length,
|
|
330
|
-
errorCount: list.length - sc
|
|
331
|
-
});
|
|
332
|
-
}
|
|
333
|
-
return stats.sort((a, b) => b.totalCalls - a.totalCalls);
|
|
334
|
-
}
|
|
335
|
-
function createMetricsMiddleware() {
|
|
336
|
-
return async (args, ctx, next) => {
|
|
337
|
-
const startTime = getTimeProvider().now();
|
|
338
|
-
try {
|
|
339
|
-
const result = await next(args, ctx);
|
|
340
|
-
const durationMs = getTimeProvider().now() - startTime;
|
|
341
|
-
recordToolMetric({
|
|
342
|
-
toolName: ctx.requestContext.toolName,
|
|
343
|
-
durationMs,
|
|
344
|
-
success: result.isError !== true,
|
|
345
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
346
|
-
});
|
|
347
|
-
return result;
|
|
348
|
-
} catch (error) {
|
|
349
|
-
const durationMs = getTimeProvider().now() - startTime;
|
|
350
|
-
recordToolMetric({
|
|
351
|
-
toolName: ctx.requestContext.toolName,
|
|
352
|
-
durationMs,
|
|
353
|
-
success: false,
|
|
354
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
355
|
-
});
|
|
356
|
-
throw error;
|
|
357
|
-
}
|
|
358
|
-
};
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// src/agents/heartbeat-monitor.ts
|
|
362
|
-
var DEFAULT_SLOW_THRESHOLD_MS = HEARTBEAT_TIMEOUTS.slowThresholdMs;
|
|
363
|
-
var DEFAULT_STALLED_THRESHOLD_MS = HEARTBEAT_TIMEOUTS.stalledThresholdMs;
|
|
364
|
-
var DEFAULT_ABSOLUTE_MAX_MS = HEARTBEAT_TIMEOUTS.absoluteMaxMs;
|
|
365
|
-
var HeartbeatMonitor = class {
|
|
366
|
-
config;
|
|
367
|
-
sessions = /* @__PURE__ */ new Map();
|
|
368
|
-
constructor(config) {
|
|
369
|
-
this.config = {
|
|
370
|
-
slowThresholdMs: config?.slowThresholdMs ?? DEFAULT_SLOW_THRESHOLD_MS,
|
|
371
|
-
stalledThresholdMs: config?.stalledThresholdMs ?? DEFAULT_STALLED_THRESHOLD_MS,
|
|
372
|
-
absoluteMaxMs: config?.absoluteMaxMs ?? DEFAULT_ABSOLUTE_MAX_MS
|
|
373
|
-
};
|
|
374
|
-
}
|
|
375
|
-
/** Start tracking a new expert session. Returns session ID. */
|
|
376
|
-
startSession(expertId) {
|
|
377
|
-
const sessionId = `hb-${expertId}-${String(Date.now())}`;
|
|
378
|
-
const now = Date.now();
|
|
379
|
-
this.sessions.set(sessionId, {
|
|
380
|
-
expertId,
|
|
381
|
-
startedAt: now,
|
|
382
|
-
lastHeartbeat: now,
|
|
383
|
-
heartbeatCount: 0,
|
|
384
|
-
previousHealth: "alive"
|
|
385
|
-
});
|
|
386
|
-
return sessionId;
|
|
387
|
-
}
|
|
388
|
-
/** Record a heartbeat for an active session. */
|
|
389
|
-
heartbeat(sessionId) {
|
|
390
|
-
const entry = this.sessions.get(sessionId);
|
|
391
|
-
if (entry === void 0) return;
|
|
392
|
-
entry.lastHeartbeat = Date.now();
|
|
393
|
-
entry.heartbeatCount++;
|
|
394
|
-
}
|
|
395
|
-
/** Stop tracking a session. */
|
|
396
|
-
endSession(sessionId) {
|
|
397
|
-
this.sessions.delete(sessionId);
|
|
398
|
-
}
|
|
399
|
-
/** Check if a session has exceeded the stalled threshold. */
|
|
400
|
-
isStalled(sessionId) {
|
|
401
|
-
const entry = this.sessions.get(sessionId);
|
|
402
|
-
if (entry === void 0) return false;
|
|
403
|
-
const elapsed = Date.now() - entry.lastHeartbeat;
|
|
404
|
-
return elapsed >= this.config.stalledThresholdMs;
|
|
405
|
-
}
|
|
406
|
-
/** Check if a session has exceeded the absolute max lifetime. */
|
|
407
|
-
isExpired(sessionId) {
|
|
408
|
-
const entry = this.sessions.get(sessionId);
|
|
409
|
-
if (entry === void 0) return false;
|
|
410
|
-
return Date.now() - entry.startedAt >= this.config.absoluteMaxMs;
|
|
411
|
-
}
|
|
412
|
-
/** Get aggregate health report for all active sessions. */
|
|
413
|
-
getHealth() {
|
|
414
|
-
const now = Date.now();
|
|
415
|
-
const sessions = [];
|
|
416
|
-
let stalledCount = 0;
|
|
417
|
-
for (const [sessionId, entry] of this.sessions) {
|
|
418
|
-
const timeSince = now - entry.lastHeartbeat;
|
|
419
|
-
const health = this.classifyHealth(timeSince);
|
|
420
|
-
if (health === "stalled") stalledCount++;
|
|
421
|
-
sessions.push({
|
|
422
|
-
sessionId,
|
|
423
|
-
expertId: entry.expertId,
|
|
424
|
-
startedAt: entry.startedAt,
|
|
425
|
-
lastHeartbeat: entry.lastHeartbeat,
|
|
426
|
-
heartbeatCount: entry.heartbeatCount,
|
|
427
|
-
health,
|
|
428
|
-
elapsedMs: now - entry.startedAt,
|
|
429
|
-
timeSinceHeartbeatMs: timeSince
|
|
430
|
-
});
|
|
431
|
-
}
|
|
432
|
-
return {
|
|
433
|
-
activeSessions: this.sessions.size,
|
|
434
|
-
stalledSessions: stalledCount,
|
|
435
|
-
sessions
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
/**
|
|
439
|
-
* Get health state with transition detection for a session.
|
|
440
|
-
* Updates previousHealth on each call to track transitions.
|
|
441
|
-
* (Issue #1088 Phase 4 — observability)
|
|
442
|
-
*/
|
|
443
|
-
getSessionHealth(sessionId) {
|
|
444
|
-
const entry = this.sessions.get(sessionId);
|
|
445
|
-
if (entry === void 0) return void 0;
|
|
446
|
-
const now = Date.now();
|
|
447
|
-
const timeSince = now - entry.lastHeartbeat;
|
|
448
|
-
const health = this.classifyHealth(timeSince);
|
|
449
|
-
const changed = health !== entry.previousHealth;
|
|
450
|
-
const result = {
|
|
451
|
-
sessionId,
|
|
452
|
-
agentId: entry.expertId,
|
|
453
|
-
health,
|
|
454
|
-
previousHealth: entry.previousHealth,
|
|
455
|
-
changed,
|
|
456
|
-
elapsedMs: now - entry.startedAt,
|
|
457
|
-
heartbeatCount: entry.heartbeatCount
|
|
458
|
-
};
|
|
459
|
-
entry.previousHealth = health;
|
|
460
|
-
return result;
|
|
461
|
-
}
|
|
462
|
-
/** Number of currently tracked sessions. */
|
|
463
|
-
get activeCount() {
|
|
464
|
-
return this.sessions.size;
|
|
465
|
-
}
|
|
466
|
-
classifyHealth(timeSinceMs) {
|
|
467
|
-
if (timeSinceMs >= this.config.stalledThresholdMs) return "stalled";
|
|
468
|
-
if (timeSinceMs >= this.config.slowThresholdMs) return "slow";
|
|
469
|
-
return "alive";
|
|
470
|
-
}
|
|
471
|
-
};
|
|
472
|
-
var globalMonitor;
|
|
473
|
-
function getHeartbeatMonitor() {
|
|
474
|
-
globalMonitor ??= new HeartbeatMonitor();
|
|
475
|
-
return globalMonitor;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
// src/mcp/tools/weather-report.ts
|
|
479
|
-
var CLI_NAMES2 = ["claude", "gemini", "codex", "opencode"];
|
|
480
|
-
function generateWeatherReport(input, config) {
|
|
481
|
-
const cfg = { ...createDefaultWeatherConfig(), ...config };
|
|
482
|
-
const store = getOutcomeStore();
|
|
483
|
-
const includeAdaptive = input.includeAdaptive ?? true;
|
|
484
|
-
const summary = store.summarize(buildQuery(input.cli, input.category));
|
|
485
|
-
const cliWeather = buildCliWeather(summary, input);
|
|
486
|
-
const adaptiveBonuses = includeAdaptive ? computeAdaptiveBonuses(cfg) : [];
|
|
487
|
-
const tierRecommendations = buildTierRecommendations(summary);
|
|
488
|
-
const rateLimits = buildRateLimitReport();
|
|
489
|
-
const toolPerformance = buildToolPerformance();
|
|
490
|
-
const failureBreakdown = buildFailureBreakdown(input);
|
|
491
|
-
const agentHealth = buildAgentHealth();
|
|
492
|
-
const expertPerformance = buildExpertPerformance();
|
|
493
|
-
const base = {
|
|
494
|
-
overall: {
|
|
495
|
-
totalTasks: summary.totalTasks,
|
|
496
|
-
successRate: summary.successRate,
|
|
497
|
-
avgDurationMs: summary.avgDurationMs
|
|
498
|
-
},
|
|
499
|
-
cliWeather,
|
|
500
|
-
adaptiveBonuses,
|
|
501
|
-
tierRecommendations,
|
|
502
|
-
...rateLimits.length > 0 ? { rateLimits } : {},
|
|
503
|
-
...toolPerformance.length > 0 ? { toolPerformance } : {},
|
|
504
|
-
...failureBreakdown.length > 0 ? { failureBreakdown } : {},
|
|
505
|
-
...agentHealth !== void 0 ? { agentHealth } : {},
|
|
506
|
-
...expertPerformance.length > 0 ? { expertPerformance } : {},
|
|
507
|
-
explorationRate: cfg.explorationRate,
|
|
508
|
-
coldStartThreshold: cfg.coldStartThreshold,
|
|
509
|
-
collectedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
510
|
-
};
|
|
511
|
-
if (includeAdaptive) {
|
|
512
|
-
return {
|
|
513
|
-
...base,
|
|
514
|
-
learningInsights: buildLearningInsights(),
|
|
515
|
-
recommendedMappings: buildRecommendedMappings()
|
|
516
|
-
};
|
|
517
|
-
}
|
|
518
|
-
return base;
|
|
519
|
-
}
|
|
520
|
-
function buildAgentHealth() {
|
|
521
|
-
const monitor = getHeartbeatMonitor();
|
|
522
|
-
if (monitor.activeCount === 0) return void 0;
|
|
523
|
-
const health = monitor.getHealth();
|
|
524
|
-
return {
|
|
525
|
-
activeSessions: health.activeSessions,
|
|
526
|
-
stalledSessions: health.stalledSessions,
|
|
527
|
-
sessions: health.sessions.map((s) => ({
|
|
528
|
-
sessionId: s.sessionId,
|
|
529
|
-
expertId: s.expertId,
|
|
530
|
-
health: s.health,
|
|
531
|
-
elapsedMs: s.elapsedMs,
|
|
532
|
-
timeSinceHeartbeatMs: s.timeSinceHeartbeatMs,
|
|
533
|
-
heartbeatCount: s.heartbeatCount
|
|
534
|
-
}))
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
|
-
function buildRateLimitReport() {
|
|
538
|
-
return getRateLimitStats().map((s) => ({
|
|
539
|
-
provider: s.provider,
|
|
540
|
-
totalHits: s.totalHits,
|
|
541
|
-
lastHitAt: s.lastHitAt,
|
|
542
|
-
avgRetryAfterMs: s.avgRetryAfterMs
|
|
543
|
-
}));
|
|
544
|
-
}
|
|
545
|
-
function getAdaptiveBonus(cli, category, config) {
|
|
546
|
-
const cfg = { ...createDefaultWeatherConfig(), ...config };
|
|
547
|
-
const store = getOutcomeStore();
|
|
548
|
-
const cliName = cli;
|
|
549
|
-
const outcomes = store.query({ cli: cliName, category });
|
|
550
|
-
if (outcomes.length < cfg.coldStartThreshold) return 0;
|
|
551
|
-
const thresholds = computeAdaptiveThresholds(store, cliName, category);
|
|
552
|
-
const successRate = outcomes.filter((o) => o.success).length / outcomes.length;
|
|
553
|
-
const delta = successRate - thresholds.baseline;
|
|
554
|
-
const maxBonus = thresholds.maxBonus > 0 ? thresholds.maxBonus : cfg.maxBonusAdjustment;
|
|
555
|
-
const scaled = delta / 0.3 * maxBonus;
|
|
556
|
-
return clamp(scaled, -maxBonus, maxBonus);
|
|
557
|
-
}
|
|
558
|
-
function buildQuery(cli, category) {
|
|
559
|
-
const query = {};
|
|
560
|
-
if (cli !== void 0) query.cli = cli;
|
|
561
|
-
if (category !== void 0) query.category = category;
|
|
562
|
-
return query;
|
|
563
|
-
}
|
|
564
|
-
function buildCliWeather(summary, input) {
|
|
565
|
-
const clis = input.cli !== void 0 ? [input.cli] : [...CLI_NAMES2];
|
|
566
|
-
return clis.map((cli) => {
|
|
567
|
-
const stats = summary.byCli.get(cli);
|
|
568
|
-
const store = getOutcomeStore();
|
|
569
|
-
const cliOutcomes = store.query({ cli });
|
|
570
|
-
const byCategory = /* @__PURE__ */ new Map();
|
|
571
|
-
for (const cat of TASK_CATEGORIES) {
|
|
572
|
-
const catOutcomes = cliOutcomes.filter((o) => o.category === cat);
|
|
573
|
-
if (catOutcomes.length > 0) {
|
|
574
|
-
const sc = catOutcomes.filter((o) => o.success).length;
|
|
575
|
-
const td = catOutcomes.reduce((s, o) => s + o.durationMs, 0);
|
|
576
|
-
byCategory.set(cat, {
|
|
577
|
-
count: catOutcomes.length,
|
|
578
|
-
successRate: sc / catOutcomes.length,
|
|
579
|
-
avgDurationMs: td / catOutcomes.length
|
|
580
|
-
});
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
return {
|
|
584
|
-
cli,
|
|
585
|
-
totalTasks: stats?.count ?? 0,
|
|
586
|
-
successRate: stats?.successRate ?? 0,
|
|
587
|
-
avgDurationMs: stats?.avgDurationMs ?? 0,
|
|
588
|
-
byCategory
|
|
589
|
-
};
|
|
590
|
-
});
|
|
591
|
-
}
|
|
592
|
-
function computeAdaptiveBonuses(cfg) {
|
|
593
|
-
const bonuses = [];
|
|
594
|
-
for (const cli of CLI_NAMES2) {
|
|
595
|
-
for (const category of TASK_CATEGORIES) {
|
|
596
|
-
const spec = getSpecialization(category);
|
|
597
|
-
const staticBonus = getStaticBonusForCli(cli, spec);
|
|
598
|
-
const store = getOutcomeStore();
|
|
599
|
-
const outcomes = store.query({ cli, category });
|
|
600
|
-
const sampleCount = outcomes.length;
|
|
601
|
-
const sufficient = sampleCount >= cfg.coldStartThreshold;
|
|
602
|
-
const adaptiveAdj = sufficient ? getAdaptiveBonus(cli, category, cfg) : 0;
|
|
603
|
-
if (sampleCount > 0 || staticBonus > 0 || adaptiveAdj !== 0) {
|
|
604
|
-
bonuses.push({
|
|
605
|
-
cli,
|
|
606
|
-
category,
|
|
607
|
-
staticBonus,
|
|
608
|
-
adaptiveBonus: Math.round(adaptiveAdj * 10) / 10,
|
|
609
|
-
sampleCount,
|
|
610
|
-
sufficient
|
|
611
|
-
});
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
return bonuses;
|
|
616
|
-
}
|
|
617
|
-
function getStaticBonusForCli(cli, spec) {
|
|
618
|
-
if (cli === spec.primaryCli) return spec.bonus;
|
|
619
|
-
if (cli === spec.secondaryCli) return Math.floor(spec.bonus / 2);
|
|
620
|
-
return 0;
|
|
621
|
-
}
|
|
622
|
-
function clamp(value, min, max) {
|
|
623
|
-
return Math.min(max, Math.max(min, value));
|
|
624
|
-
}
|
|
625
|
-
function buildLearningInsights() {
|
|
626
|
-
const store = getOutcomeStore();
|
|
627
|
-
const insights = [];
|
|
628
|
-
for (const cli of CLI_NAMES2) {
|
|
629
|
-
for (const category of TASK_CATEGORIES) {
|
|
630
|
-
const thresholds = computeAdaptiveThresholds(store, cli, category);
|
|
631
|
-
if (thresholds.sampleCount > 0) {
|
|
632
|
-
insights.push({
|
|
633
|
-
cli,
|
|
634
|
-
category,
|
|
635
|
-
trend: thresholds.trend,
|
|
636
|
-
confidence: thresholds.confidence,
|
|
637
|
-
adjustedBaseline: thresholds.baseline,
|
|
638
|
-
sampleCount: thresholds.sampleCount
|
|
639
|
-
});
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
return insights;
|
|
644
|
-
}
|
|
645
|
-
function buildRecommendedMappings() {
|
|
646
|
-
const store = getOutcomeStore();
|
|
647
|
-
const mappings = [];
|
|
648
|
-
for (const category of TASK_CATEGORIES) {
|
|
649
|
-
let bestCli = "";
|
|
650
|
-
let bestRate = -1;
|
|
651
|
-
let bestCount = 0;
|
|
652
|
-
for (const cli of CLI_NAMES2) {
|
|
653
|
-
const outcomes = store.query({ cli, category });
|
|
654
|
-
if (outcomes.length === 0) continue;
|
|
655
|
-
const rate = outcomes.filter((o) => o.success).length / outcomes.length;
|
|
656
|
-
if (rate > bestRate || rate === bestRate && outcomes.length > bestCount) {
|
|
657
|
-
bestCli = cli;
|
|
658
|
-
bestRate = rate;
|
|
659
|
-
bestCount = outcomes.length;
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
if (bestCli !== "") {
|
|
663
|
-
const confidence = bestCount >= 20 ? "high" : bestCount >= 10 ? "medium" : "low";
|
|
664
|
-
mappings.push({
|
|
665
|
-
category,
|
|
666
|
-
recommendedCli: bestCli,
|
|
667
|
-
successRate: bestRate,
|
|
668
|
-
sampleCount: bestCount,
|
|
669
|
-
confidence
|
|
670
|
-
});
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
return mappings;
|
|
674
|
-
}
|
|
675
|
-
var WORKER_MODEL_PREFIX = "worker-";
|
|
676
|
-
function findDominantError(failed) {
|
|
677
|
-
if (failed.length === 0) return void 0;
|
|
678
|
-
const counts = /* @__PURE__ */ new Map();
|
|
679
|
-
for (const f of failed) {
|
|
680
|
-
const cat = f.failureCategory ?? "unknown";
|
|
681
|
-
counts.set(cat, (counts.get(cat) ?? 0) + 1);
|
|
682
|
-
}
|
|
683
|
-
let maxCount = 0;
|
|
684
|
-
let dominant;
|
|
685
|
-
for (const [cat, count] of counts) {
|
|
686
|
-
if (count > maxCount) {
|
|
687
|
-
maxCount = count;
|
|
688
|
-
dominant = cat;
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
return dominant;
|
|
692
|
-
}
|
|
693
|
-
function buildExpertPerformance() {
|
|
694
|
-
const store = getOutcomeStore();
|
|
695
|
-
const allOutcomes = store.query();
|
|
696
|
-
const workerOutcomes = allOutcomes.filter((o) => o.model.startsWith(WORKER_MODEL_PREFIX));
|
|
697
|
-
if (workerOutcomes.length === 0) return [];
|
|
698
|
-
const byRole = /* @__PURE__ */ new Map();
|
|
699
|
-
for (const o of workerOutcomes) {
|
|
700
|
-
const role = o.model.slice(WORKER_MODEL_PREFIX.length);
|
|
701
|
-
const existing = byRole.get(role) ?? [];
|
|
702
|
-
existing.push(o);
|
|
703
|
-
byRole.set(role, existing);
|
|
704
|
-
}
|
|
705
|
-
const entries = [];
|
|
706
|
-
for (const [role, outcomes] of byRole) {
|
|
707
|
-
const successes = outcomes.filter((o) => o.success).length;
|
|
708
|
-
const totalDuration = outcomes.reduce((s, o) => s + o.durationMs, 0);
|
|
709
|
-
const dominantErrorPattern = findDominantError(outcomes.filter((o) => !o.success));
|
|
710
|
-
entries.push({
|
|
711
|
-
role,
|
|
712
|
-
totalTasks: outcomes.length,
|
|
713
|
-
successRate: successes / outcomes.length,
|
|
714
|
-
avgDurationMs: Math.round(totalDuration / outcomes.length),
|
|
715
|
-
...dominantErrorPattern !== void 0 ? { dominantErrorPattern } : {}
|
|
716
|
-
});
|
|
717
|
-
}
|
|
718
|
-
return entries.sort((a, b) => b.totalTasks - a.totalTasks);
|
|
719
|
-
}
|
|
720
|
-
function buildFailureBreakdown(input) {
|
|
721
|
-
const store = getOutcomeStore();
|
|
722
|
-
const outcomes = store.query(buildQuery(input.cli, input.category));
|
|
723
|
-
const failed = outcomes.filter((o) => !o.success);
|
|
724
|
-
if (failed.length === 0) return [];
|
|
725
|
-
const counts = /* @__PURE__ */ new Map();
|
|
726
|
-
for (const o of failed) {
|
|
727
|
-
const cat = o.failureCategory ?? "unknown";
|
|
728
|
-
counts.set(cat, (counts.get(cat) ?? 0) + 1);
|
|
729
|
-
}
|
|
730
|
-
const entries = [];
|
|
731
|
-
for (const [category, count] of counts) {
|
|
732
|
-
entries.push({
|
|
733
|
-
category,
|
|
734
|
-
count,
|
|
735
|
-
percentage: Math.round(count / failed.length * 1e3) / 10
|
|
736
|
-
});
|
|
737
|
-
}
|
|
738
|
-
return entries.sort((a, b) => b.count - a.count);
|
|
739
|
-
}
|
|
740
|
-
function buildToolPerformance() {
|
|
741
|
-
return getToolStats().map((s) => ({
|
|
742
|
-
toolName: s.toolName,
|
|
743
|
-
totalCalls: s.totalCalls,
|
|
744
|
-
successRate: Math.round(s.successRate * 1e3) / 1e3,
|
|
745
|
-
avgDurationMs: Math.round(s.avgDurationMs),
|
|
746
|
-
errorCount: s.errorCount
|
|
747
|
-
}));
|
|
748
|
-
}
|
|
749
|
-
function buildTierRecommendations(summary) {
|
|
750
|
-
return generateTierRecommendations(summary).map((r) => ({
|
|
751
|
-
category: r.category,
|
|
752
|
-
direction: r.direction,
|
|
753
|
-
currentTier: r.currentTier,
|
|
754
|
-
recommendedTier: r.recommendedTier,
|
|
755
|
-
successRate: r.successRate,
|
|
756
|
-
sampleCount: r.sampleCount,
|
|
757
|
-
reason: r.reason
|
|
758
|
-
}));
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
export {
|
|
762
|
-
isRateLimitLikeError,
|
|
763
|
-
toRateLimitError,
|
|
764
|
-
recordRateLimitEvent,
|
|
765
|
-
getHeartbeatMonitor,
|
|
766
|
-
createMetricsMiddleware,
|
|
767
|
-
WeatherReportInputSchema,
|
|
768
|
-
SECURITY_KEYWORDS,
|
|
769
|
-
ARCHITECTURE_KEYWORDS,
|
|
770
|
-
PROMOTED_ROLES,
|
|
771
|
-
classifyRequestTier,
|
|
772
|
-
parseTierOverrides,
|
|
773
|
-
generateWeatherReport,
|
|
774
|
-
getAdaptiveBonus
|
|
775
|
-
};
|
|
776
|
-
//# sourceMappingURL=chunk-PGNRXCYY.js.map
|