@merittdev/horus 0.8.5 → 0.8.7
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/dist/index.cjs +87 -23
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -65880,7 +65880,7 @@ init_cjs_shims();
|
|
|
65880
65880
|
|
|
65881
65881
|
// ../../packages/core/src/version.ts
|
|
65882
65882
|
init_cjs_shims();
|
|
65883
|
-
var HORUS_VERSION = true ? "0.8.
|
|
65883
|
+
var HORUS_VERSION = true ? "0.8.7" : "dev";
|
|
65884
65884
|
var PINNED_SOURCE_VERSION = "1.5.0";
|
|
65885
65885
|
|
|
65886
65886
|
// ../../packages/core/src/evidence.ts
|
|
@@ -91925,6 +91925,8 @@ function correlate(evidence2) {
|
|
|
91925
91925
|
init_cjs_shims();
|
|
91926
91926
|
var PREFER = /resolver|controller|\bservice\b|route|router|handler|gateway|processor|worker|consumer|repository|usecase|use-case|endpoint/i;
|
|
91927
91927
|
var DEMOTE = /\butil|helper|\bscript|backfill|legacy|migration|seeder|fixture|\bmock\b|\btest\b|\bspec\b/i;
|
|
91928
|
+
var PRESENTATION_NAME = /^(build|format|render|compose|serialize)[A-Z].*(Message|Markup|Html|Text|Label|Summary|Notification|String)$/;
|
|
91929
|
+
var PREDICATE_NAME = /^(is|has|can|should|was|were|are|does|did|needs|allows?|must)[A-Z]/;
|
|
91928
91930
|
var TYPE_SUFFIX = /(Result|Input|Response|Payload|Args|Dto|Props|Edge|Connection)$/;
|
|
91929
91931
|
var INTERFACE_PREFIX = /^I(?=[A-Z][a-z])/;
|
|
91930
91932
|
var TYPE_LIKE_NAME = new RegExp(`(?:${TYPE_SUFFIX.source})|(?:${INTERFACE_PREFIX.source})`);
|
|
@@ -91952,8 +91954,9 @@ function scoreSeed(s5, index2, hintTokens, changedFiles, hintHasCode) {
|
|
|
91952
91954
|
let score = 0;
|
|
91953
91955
|
if (PREFER.test(hay)) score += hintHasCode ? 0 : 3;
|
|
91954
91956
|
if (DEMOTE.test(hay)) score -= 3;
|
|
91957
|
+
if (PRESENTATION_NAME.test(s5.name)) score -= 3;
|
|
91955
91958
|
if (isTypeLikeName(s5.name)) score -= 4;
|
|
91956
|
-
if (s5.startLine !== void 0 && s5.endLine !== void 0 && s5.endLine - s5.startLine <= 3) {
|
|
91959
|
+
if (s5.startLine !== void 0 && s5.endLine !== void 0 && s5.endLine - s5.startLine <= 3 && !PREDICATE_NAME.test(s5.name)) {
|
|
91957
91960
|
score -= 3;
|
|
91958
91961
|
}
|
|
91959
91962
|
if (changedFiles !== void 0 && changedFiles.has(s5.filePath)) score += 5;
|
|
@@ -92279,6 +92282,15 @@ function clamp015(n4) {
|
|
|
92279
92282
|
var SEV_ERROR = 2;
|
|
92280
92283
|
var SEV_WARN = 1;
|
|
92281
92284
|
var SEV_INFO = 0;
|
|
92285
|
+
var GENERIC_SYMBOL_NAMES = /* @__PURE__ */ new Set([
|
|
92286
|
+
"",
|
|
92287
|
+
"constructor",
|
|
92288
|
+
"unknown",
|
|
92289
|
+
"unknown-producer",
|
|
92290
|
+
"unknown-worker",
|
|
92291
|
+
"anonymous",
|
|
92292
|
+
"default"
|
|
92293
|
+
]);
|
|
92282
92294
|
function seedEmittedSeverityTier(level, code, message) {
|
|
92283
92295
|
const lv = level?.toLowerCase();
|
|
92284
92296
|
if (lv === "error" || lv === "fatal" || lv === "critical") return SEV_ERROR;
|
|
@@ -92471,10 +92483,21 @@ function looksDiffable(since) {
|
|
|
92471
92483
|
function looksExplanatory(hint) {
|
|
92472
92484
|
const h3 = hint.trim();
|
|
92473
92485
|
if (h3.length === 0) return false;
|
|
92486
|
+
if (/^\s*(explain|describe|walk\s+me\s+through|tell\s+me\s+(?:about|how))\b/i.test(h3)) {
|
|
92487
|
+
return true;
|
|
92488
|
+
}
|
|
92489
|
+
if (/\b(recently\s+changed|what\s+changed|changed\s+recently|regress|regression)\b/i.test(h3)) {
|
|
92490
|
+
return false;
|
|
92491
|
+
}
|
|
92474
92492
|
if (/\b(how|what|why|when|where)\b.*\b(work|works|working|do|does|done|happen|happens|flow|flows|behave|called|invoked|triggered|used)\b/i.test(h3)) {
|
|
92475
92493
|
return true;
|
|
92476
92494
|
}
|
|
92477
|
-
if (
|
|
92495
|
+
if (/\b(fail|fails|failed|failing|failure|break|breaks|breaking|broke|broken|crash|crashes|crashing|down|slow|stuck|hang|hangs|hung|timeout|timing\s*out|wrong|exception|error|errors|leak|leaking|stopped|throwing|throws|reject|rejected|denied|not\s+work|doesn'?t\s+work|isn'?t\s+work|can'?t|cannot|caus(?:e|es|ing)|misbehav)/i.test(
|
|
92496
|
+
h3
|
|
92497
|
+
)) {
|
|
92498
|
+
return false;
|
|
92499
|
+
}
|
|
92500
|
+
if (/^\s*(how|what|why|when|where)\b/i.test(h3)) {
|
|
92478
92501
|
return true;
|
|
92479
92502
|
}
|
|
92480
92503
|
return false;
|
|
@@ -92729,16 +92752,33 @@ async function investigate(input, deps) {
|
|
|
92729
92752
|
} catch {
|
|
92730
92753
|
edges = [];
|
|
92731
92754
|
}
|
|
92732
|
-
const symbolNames =
|
|
92733
|
-
top.name,
|
|
92734
|
-
|
|
92735
|
-
|
|
92736
|
-
|
|
92755
|
+
const symbolNames = new Set(
|
|
92756
|
+
[top.name, ...ctx.callers.map((s5) => s5.name), ...ctx.callees.map((s5) => s5.name)].filter(
|
|
92757
|
+
(n4) => n4 && !GENERIC_SYMBOL_NAMES.has(n4)
|
|
92758
|
+
)
|
|
92759
|
+
);
|
|
92737
92760
|
queueHits = edges.filter((e) => {
|
|
92738
92761
|
const bySymbol = e.producerSymbol !== null && symbolNames.has(e.producerSymbol) || e.workerSymbol !== null && symbolNames.has(e.workerSymbol);
|
|
92739
92762
|
const byFile = e.producerFile === top.filePath || e.workerFile === top.filePath;
|
|
92740
92763
|
return bySymbol || byFile;
|
|
92741
92764
|
});
|
|
92765
|
+
if (queueHits.length > 0 && typeof code.searchSymbols === "function") {
|
|
92766
|
+
const resolvesInRepo = async (name2) => {
|
|
92767
|
+
if (!name2 || GENERIC_SYMBOL_NAMES.has(name2)) return false;
|
|
92768
|
+
try {
|
|
92769
|
+
return (await code.searchSymbols(name2, 3)).some((s5) => s5.name === name2);
|
|
92770
|
+
} catch {
|
|
92771
|
+
return true;
|
|
92772
|
+
}
|
|
92773
|
+
};
|
|
92774
|
+
const validated2 = [];
|
|
92775
|
+
for (const e of queueHits) {
|
|
92776
|
+
if (await resolvesInRepo(e.producerSymbol) || await resolvesInRepo(e.workerSymbol)) {
|
|
92777
|
+
validated2.push(e);
|
|
92778
|
+
}
|
|
92779
|
+
}
|
|
92780
|
+
queueHits = validated2;
|
|
92781
|
+
}
|
|
92742
92782
|
if (input.since !== void 0 && looksDiffable(input.since)) {
|
|
92743
92783
|
try {
|
|
92744
92784
|
changes = await code.detectChanges({ base: input.since, compare: "HEAD" });
|
|
@@ -92922,7 +92962,10 @@ async function investigate(input, deps) {
|
|
|
92922
92962
|
message: sig.sampleMessage ?? "",
|
|
92923
92963
|
evId: existingEvId,
|
|
92924
92964
|
isNew: sig.isNew ?? false,
|
|
92925
|
-
level: sig.level ?? null
|
|
92965
|
+
level: sig.level ?? null,
|
|
92966
|
+
raisedBySeed: true,
|
|
92967
|
+
// literal is in the seed's own body
|
|
92968
|
+
raiserName: top?.name ?? ""
|
|
92926
92969
|
});
|
|
92927
92970
|
}
|
|
92928
92971
|
}
|
|
@@ -92970,7 +93013,10 @@ async function investigate(input, deps) {
|
|
|
92970
93013
|
message: sig.sampleMessage ?? "",
|
|
92971
93014
|
evId: ev.id,
|
|
92972
93015
|
isNew: sig.isNew ?? false,
|
|
92973
|
-
level: sig.level ?? null
|
|
93016
|
+
level: sig.level ?? null,
|
|
93017
|
+
raisedBySeed: true,
|
|
93018
|
+
// literal is in the seed's own body
|
|
93019
|
+
raiserName: top?.name ?? ""
|
|
92974
93020
|
});
|
|
92975
93021
|
}
|
|
92976
93022
|
} catch {
|
|
@@ -92980,17 +93026,28 @@ async function investigate(input, deps) {
|
|
|
92980
93026
|
const GAP_D_MAX_RESOLVE = 12;
|
|
92981
93027
|
const seedScope = /* @__PURE__ */ new Set([top.id, ...(ctx?.callees ?? []).map((c4) => c4.id)]);
|
|
92982
93028
|
const alreadyEmitted = new Set(seedEmittedJoins.map((j3) => j3.code));
|
|
92983
|
-
const
|
|
93029
|
+
const eligible = analysis.signatures.filter(
|
|
92984
93030
|
(s5) => s5.key !== "" && s5.key !== "(none)" && s5.count > 0 && !alreadyEmitted.has(s5.key)
|
|
92985
|
-
)
|
|
93031
|
+
);
|
|
93032
|
+
const meaningful = meaningfulHintTokens.filter((t2) => t2.length >= 4);
|
|
93033
|
+
const hintRelevant = (s5) => meaningful.some(
|
|
93034
|
+
(t2) => s5.key.toLowerCase().includes(t2) || (s5.sampleMessage ?? "").toLowerCase().includes(t2)
|
|
93035
|
+
);
|
|
93036
|
+
const candidates = [
|
|
93037
|
+
.../* @__PURE__ */ new Set([
|
|
93038
|
+
...[...eligible].sort((a2, b5) => b5.count - a2.count).slice(0, GAP_D_MAX_RESOLVE),
|
|
93039
|
+
...eligible.filter(hintRelevant).slice(0, GAP_D_MAX_RESOLVE)
|
|
93040
|
+
])
|
|
93041
|
+
].slice(0, GAP_D_MAX_RESOLVE * 2);
|
|
92986
93042
|
for (const sig of candidates) {
|
|
92987
|
-
let
|
|
93043
|
+
let raisers = [];
|
|
92988
93044
|
try {
|
|
92989
|
-
|
|
93045
|
+
raisers = await deps.code.searchSymbols(sig.key, 3);
|
|
92990
93046
|
} catch {
|
|
92991
93047
|
continue;
|
|
92992
93048
|
}
|
|
92993
|
-
|
|
93049
|
+
const raiser = raisers.find((r) => r.id === top.id) ?? raisers.find((r) => seedScope.has(r.id));
|
|
93050
|
+
if (raiser === void 0) continue;
|
|
92994
93051
|
let evId = directEvIdByKey.get(sig.key);
|
|
92995
93052
|
if (evId === void 0) {
|
|
92996
93053
|
const where = raiser.id === top.id ? "the seed" : "a callee of the seed";
|
|
@@ -93033,7 +93090,9 @@ async function investigate(input, deps) {
|
|
|
93033
93090
|
message: sig.sampleMessage ?? "",
|
|
93034
93091
|
evId,
|
|
93035
93092
|
isNew: sig.isNew ?? false,
|
|
93036
|
-
level: sig.level ?? null
|
|
93093
|
+
level: sig.level ?? null,
|
|
93094
|
+
raisedBySeed: raiser.id === top.id,
|
|
93095
|
+
raiserName: raiser.name
|
|
93037
93096
|
});
|
|
93038
93097
|
}
|
|
93039
93098
|
}
|
|
@@ -93622,6 +93681,7 @@ async function investigate(input, deps) {
|
|
|
93622
93681
|
}
|
|
93623
93682
|
if (seedEmittedJoins.length > 0 && top && seedEv) {
|
|
93624
93683
|
const ordered = [...seedEmittedJoins].sort((a2, b5) => {
|
|
93684
|
+
if (a2.raisedBySeed !== b5.raisedBySeed) return a2.raisedBySeed ? -1 : 1;
|
|
93625
93685
|
const ta = seedEmittedSeverityTier(a2.level, a2.code, a2.message);
|
|
93626
93686
|
const tb = seedEmittedSeverityTier(b5.level, b5.code, b5.message);
|
|
93627
93687
|
if (ta !== tb) return tb - ta;
|
|
@@ -93635,17 +93695,21 @@ async function investigate(input, deps) {
|
|
|
93635
93695
|
const tier = seedEmittedSeverityTier(lead.level, lead.code, lead.message);
|
|
93636
93696
|
const volumeBoost = Math.min(0.1, Math.log10(Math.max(1, lead.count)) * 0.025);
|
|
93637
93697
|
const msgClause = lead.message ? ` \u2014 "${lead.message.replace(/\s+/g, " ").trim().slice(0, 90)}"` : "";
|
|
93698
|
+
const bySeed = lead.raisedBySeed;
|
|
93699
|
+
const raiser = bySeed ? top.name : lead.raiserName || top.name;
|
|
93700
|
+
const depClause = bySeed ? "" : `, a dependency of ${top.name}`;
|
|
93701
|
+
const calleePenalty = bySeed ? 0 : 0.18;
|
|
93638
93702
|
let title;
|
|
93639
93703
|
let baseScore;
|
|
93640
93704
|
if (tier === SEV_ERROR) {
|
|
93641
|
-
title = `Runtime error ${lead.code} (${lead.count}x in Elasticsearch) is raised by ${
|
|
93642
|
-
baseScore = clamp015(0.6 + volumeBoost);
|
|
93705
|
+
title = bySeed ? `Runtime error ${lead.code} (${lead.count}x in Elasticsearch) is raised by ${raiser} \u2014 the likely failure${msgClause}` : `Runtime error ${lead.code} (${lead.count}x in Elasticsearch) surfaces on ${raiser}${depClause}${msgClause}`;
|
|
93706
|
+
baseScore = clamp015(0.6 + volumeBoost - calleePenalty);
|
|
93643
93707
|
} else if (tier === SEV_WARN) {
|
|
93644
|
-
title = `Warning ${lead.code} (${lead.count}x in Elasticsearch) is raised by ${
|
|
93645
|
-
baseScore = clamp015(0.42 + volumeBoost);
|
|
93708
|
+
title = `Warning ${lead.code} (${lead.count}x in Elasticsearch) ${bySeed ? "is raised by" : "surfaces on"} ${raiser}${depClause}${msgClause}`;
|
|
93709
|
+
baseScore = clamp015(0.42 + volumeBoost - calleePenalty);
|
|
93646
93710
|
} else {
|
|
93647
|
-
title = `Diagnostic signal ${lead.code} (${lead.count}x in Elasticsearch) is logged by ${
|
|
93648
|
-
baseScore = clamp015(0.3 + volumeBoost);
|
|
93711
|
+
title = `Diagnostic signal ${lead.code} (${lead.count}x in Elasticsearch) is logged by ${raiser}${depClause} \u2014 informational, not an error${msgClause}`;
|
|
93712
|
+
baseScore = clamp015(0.3 + volumeBoost - calleePenalty);
|
|
93649
93713
|
}
|
|
93650
93714
|
causeInputs.push({
|
|
93651
93715
|
id: "cause:seed-emitted-error",
|
|
@@ -93785,7 +93849,7 @@ async function investigate(input, deps) {
|
|
|
93785
93849
|
const seedDisclaimer = seedIsLowConfidence && top ? `\u26A0 No symbol closely matched "${hint}" \u2014 "${top.name}" is a low-confidence closest match (semantic). Refine with an exact symbol or error code to target precisely. ` : "";
|
|
93786
93850
|
let summary = headlineCause ? headlineLinked ? `${seedDisclaimer}${banner2}Investigation of "${hint}" ${scope}. Top suspected cause: ${headlineCause.title}.` : `${seedDisclaimer}${banner2}Investigation of "${hint}" ${scope}. No cause is structurally linked to the seed; the strongest co-occurring signal (a lead to verify, not a diagnosis) is: ${headlineCause.title}.` : `${seedDisclaimer}${banner2}Investigation of "${hint}" ${scope}. No dominant suspected cause emerged from the available ${degradedNoSource ? "runtime" : "structural"} evidence.`;
|
|
93787
93851
|
const hasIncidentSignal = directLogEvIds.length > 0 || latencyMetricEvIds.length > 0 || queueMetricEvIds.length > 0 || dataAnomalyEvIds.length > 0 || queueFailureSignals.length > 0 || findings2.some((f5) => f5.kind === "anomaly") || headlineCause !== void 0;
|
|
93788
|
-
const explanatoryHint = looksExplanatory(hint);
|
|
93852
|
+
const explanatoryHint = looksExplanatory(hint) && input.since === void 0;
|
|
93789
93853
|
let behavioral;
|
|
93790
93854
|
if (explanatoryHint) {
|
|
93791
93855
|
let flowPool = flows;
|