@merittdev/horus 0.8.3 → 0.8.5
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 +187 -18
- 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.5" : "dev";
|
|
65884
65884
|
var PINNED_SOURCE_VERSION = "1.5.0";
|
|
65885
65885
|
|
|
65886
65886
|
// ../../packages/core/src/evidence.ts
|
|
@@ -71867,7 +71867,14 @@ function buildErrorAnalysisBody(q2, field = "event_code", mapping = MERITT_FIELD
|
|
|
71867
71867
|
sample: {
|
|
71868
71868
|
top_hits: {
|
|
71869
71869
|
size: 1,
|
|
71870
|
-
_source: [
|
|
71870
|
+
_source: [
|
|
71871
|
+
mapping.messageField,
|
|
71872
|
+
mapping.levelField,
|
|
71873
|
+
"component",
|
|
71874
|
+
"log_logger",
|
|
71875
|
+
"context",
|
|
71876
|
+
"detail"
|
|
71877
|
+
],
|
|
71871
71878
|
sort: [{ [mapping.timestampField]: { order: "desc" } }]
|
|
71872
71879
|
}
|
|
71873
71880
|
}
|
|
@@ -71881,7 +71888,21 @@ function bucketsOf(node) {
|
|
|
71881
71888
|
const n4 = node;
|
|
71882
71889
|
return n4?.["buckets"] ?? [];
|
|
71883
71890
|
}
|
|
71884
|
-
function
|
|
71891
|
+
function readLevel(src, levelField) {
|
|
71892
|
+
const raw = getField(src, levelField) ?? src[levelField];
|
|
71893
|
+
if (typeof raw === "number" && Number.isFinite(raw)) return valueToLevel(raw);
|
|
71894
|
+
if (typeof raw === "string") {
|
|
71895
|
+
const v4 = raw.trim().toLowerCase();
|
|
71896
|
+
if (v4 === "fatal" || v4 === "error" || v4 === "warn" || v4 === "info" || v4 === "debug" || v4 === "trace") {
|
|
71897
|
+
return v4;
|
|
71898
|
+
}
|
|
71899
|
+
if (v4 === "warning") return "warn";
|
|
71900
|
+
const n4 = Number(v4);
|
|
71901
|
+
if (Number.isFinite(n4)) return valueToLevel(n4);
|
|
71902
|
+
}
|
|
71903
|
+
return void 0;
|
|
71904
|
+
}
|
|
71905
|
+
function parseErrorAnalysis(resp, window2, messageField = "message", levelField) {
|
|
71885
71906
|
const res = resp;
|
|
71886
71907
|
const aggs = res["aggregations"] ?? {};
|
|
71887
71908
|
const hits = res["hits"];
|
|
@@ -71898,12 +71919,14 @@ function parseErrorAnalysis(resp, window2, messageField = "message") {
|
|
|
71898
71919
|
const sampleMessage = typeof rawMsg === "string" ? rawMsg : typeof sampleSrc["message"] === "string" ? sampleSrc["message"] : void 0;
|
|
71899
71920
|
const ctx = sampleSrc["context"];
|
|
71900
71921
|
const sampleContext = ctx !== null && typeof ctx === "object" && !Array.isArray(ctx) ? ctx : void 0;
|
|
71922
|
+
const level = levelField !== void 0 ? readLevel(sampleSrc, levelField) : void 0;
|
|
71901
71923
|
return {
|
|
71902
71924
|
key: String(b5["key"] ?? "") || "(none)",
|
|
71903
71925
|
count: typeof b5["doc_count"] === "number" ? b5["doc_count"] : 0,
|
|
71904
71926
|
firstSeen: typeof first === "string" ? first : "",
|
|
71905
71927
|
lastSeen: typeof last === "string" ? last : "",
|
|
71906
71928
|
services,
|
|
71929
|
+
...level !== void 0 ? { level } : {},
|
|
71907
71930
|
sampleMessage,
|
|
71908
71931
|
sampleComponent: typeof sampleSrc["component"] === "string" ? sampleSrc["component"] : typeof sampleSrc["log_logger"] === "string" ? sampleSrc["log_logger"] : void 0,
|
|
71909
71932
|
...sampleContext !== void 0 ? { sampleContext } : {}
|
|
@@ -72182,7 +72205,12 @@ var ElasticsearchLogsProvider = class {
|
|
|
72182
72205
|
this.resolveIndex(q2),
|
|
72183
72206
|
buildErrorAnalysisBody(current, sigField, this.mapping)
|
|
72184
72207
|
);
|
|
72185
|
-
const analysis = parseErrorAnalysis(
|
|
72208
|
+
const analysis = parseErrorAnalysis(
|
|
72209
|
+
curRes,
|
|
72210
|
+
{ from, to },
|
|
72211
|
+
this.mapping.messageField,
|
|
72212
|
+
this.mapping.levelField
|
|
72213
|
+
);
|
|
72186
72214
|
const fromMs = Date.parse(from);
|
|
72187
72215
|
const toMs = Date.parse(to);
|
|
72188
72216
|
if (!Number.isNaN(fromMs) && !Number.isNaN(toMs) && toMs > fromMs) {
|
|
@@ -92248,6 +92276,24 @@ function clamp015(n4) {
|
|
|
92248
92276
|
if (n4 > 1) return 1;
|
|
92249
92277
|
return n4;
|
|
92250
92278
|
}
|
|
92279
|
+
var SEV_ERROR = 2;
|
|
92280
|
+
var SEV_WARN = 1;
|
|
92281
|
+
var SEV_INFO = 0;
|
|
92282
|
+
function seedEmittedSeverityTier(level, code, message) {
|
|
92283
|
+
const lv = level?.toLowerCase();
|
|
92284
|
+
if (lv === "error" || lv === "fatal" || lv === "critical") return SEV_ERROR;
|
|
92285
|
+
if (lv === "warn" || lv === "warning") return SEV_WARN;
|
|
92286
|
+
if (lv === "info" || lv === "debug" || lv === "trace") return SEV_INFO;
|
|
92287
|
+
const m5 = /^([A-Z])_/.exec(code);
|
|
92288
|
+
const prefix = m5?.[1];
|
|
92289
|
+
if (prefix === "W") return SEV_WARN;
|
|
92290
|
+
if (prefix === "D" || prefix === "I" || prefix === "T") return SEV_INFO;
|
|
92291
|
+
if (prefix === "E" || prefix === "F" || prefix === "C") return SEV_ERROR;
|
|
92292
|
+
const text2 = `${code} ${message ?? ""}`.toLowerCase();
|
|
92293
|
+
if (/\b(skip|skipping|skipped|deprecat|info|debug|trace|notice)\b/.test(text2)) return SEV_INFO;
|
|
92294
|
+
if (/\bwarn(ing)?\b/.test(text2)) return SEV_WARN;
|
|
92295
|
+
return SEV_ERROR;
|
|
92296
|
+
}
|
|
92251
92297
|
function confidenceCeilingForCause(score) {
|
|
92252
92298
|
const s5 = clamp015(score);
|
|
92253
92299
|
if (s5 < 0.2) return 0.6;
|
|
@@ -92438,7 +92484,7 @@ function looksPerformance(hint) {
|
|
|
92438
92484
|
hint
|
|
92439
92485
|
);
|
|
92440
92486
|
}
|
|
92441
|
-
function buildBehavioralWalkthrough(hint, top, ctx, flows) {
|
|
92487
|
+
function buildBehavioralWalkthrough(hint, top, ctx, flows, classMethods = []) {
|
|
92442
92488
|
const shortFile = (f5) => f5.replace(/^.*?((?:src|app|lib|packages)\/.*)$/, "$1");
|
|
92443
92489
|
const isNoise = (s5) => /logger|logging|\.log\b|telemetry/i.test(s5.filePath) || /^(forcontext|getlogger|child|debug|info|warn|error|log|trace|tracer)$/i.test(s5.name);
|
|
92444
92490
|
const flow = flows.slice().sort((a2, b5) => b5.steps.length - a2.steps.length)[0];
|
|
@@ -92456,7 +92502,7 @@ function buildBehavioralWalkthrough(hint, top, ctx, flows) {
|
|
|
92456
92502
|
};
|
|
92457
92503
|
const PERSIST_RE = /(^|[._])(save|insert|upsert|persist|bulkwrite|find|findone|findmany|aggregate|delete|remove)\b|repositor|\brepo\b|\.model|entity|prisma|mongoose|typeorm|knex|\bdao\b|datasource/i;
|
|
92458
92504
|
const EXTERNAL_RE = /(^|[._])(request|publish|enqueue|dispatch|emit|notify)\b|http|axios|\bapi\b|webhook|\bqueue\b|processor|producer|gateway|\.client\b/i;
|
|
92459
|
-
const pool = dedup([...steps, ...ctx?.callees ?? []]).filter((s5) => !isNoise(s5));
|
|
92505
|
+
const pool = dedup([...steps, ...ctx?.callees ?? [], ...classMethods]).filter((s5) => !isNoise(s5));
|
|
92460
92506
|
const persistence = dedup(pool.filter((s5) => PERSIST_RE.test(s5.name) || PERSIST_RE.test(s5.filePath))).slice(0, 6).map((s5) => `${s5.name} (${shortFile(s5.filePath)})`);
|
|
92461
92507
|
const persistNames = new Set(persistence.map((p5) => p5.split(" (")[0]));
|
|
92462
92508
|
const externalCalls = dedup(
|
|
@@ -92467,9 +92513,15 @@ function buildBehavioralWalkthrough(hint, top, ctx, flows) {
|
|
|
92467
92513
|
const loc = entry2.startLine ? `${shortFile(entry2.filePath)}:${entry2.startLine}` : shortFile(entry2.filePath);
|
|
92468
92514
|
lines.push(`Entry point: \`${entry2.name}\` (${loc}).`);
|
|
92469
92515
|
}
|
|
92516
|
+
const methodList = dedup(classMethods.filter((s5) => !isNoise(s5)));
|
|
92470
92517
|
if (steps.length > 1) {
|
|
92471
92518
|
const chain = steps.slice(0, 12).map((s5) => s5.name).join(" \u2192 ");
|
|
92472
92519
|
lines.push(`Flow: ${chain}${steps.length > 12 ? " \u2192 \u2026" : ""}${flow ? ` (${flow.steps.length} steps)` : ""}.`);
|
|
92520
|
+
} else if (entry2 && methodList.length > 0) {
|
|
92521
|
+
const names = methodList.slice(0, 12).map((s5) => s5.name).join(", ");
|
|
92522
|
+
lines.push(
|
|
92523
|
+
`\`${entry2.name}\` is a class \u2014 its methods are the entry points: ${names}${methodList.length > 12 ? ", \u2026" : ""}. Trace one with \`horus explain <method>\`.`
|
|
92524
|
+
);
|
|
92473
92525
|
} else if (entry2) {
|
|
92474
92526
|
lines.push(`No multi-step execution flow was indexed for \`${entry2.name}\` \u2014 showing its direct calls.`);
|
|
92475
92527
|
}
|
|
@@ -92478,6 +92530,28 @@ function buildBehavioralWalkthrough(hint, top, ctx, flows) {
|
|
|
92478
92530
|
if (lines.length === 0) lines.push("No execution flow could be reconstructed for this query from the source graph.");
|
|
92479
92531
|
return { question: hint, entry: entry2, steps, externalCalls, persistence, narrative: lines.join("\n") };
|
|
92480
92532
|
}
|
|
92533
|
+
function isClassSymbol(sym) {
|
|
92534
|
+
return sym !== void 0 && sym.id.split(":", 1)[0]?.toLowerCase() === "class";
|
|
92535
|
+
}
|
|
92536
|
+
async function fetchClassMethods(code, cls) {
|
|
92537
|
+
const safe = (s5) => s5.replace(/["\\]/g, "");
|
|
92538
|
+
const name2 = safe(cls.name);
|
|
92539
|
+
if (!name2) return [];
|
|
92540
|
+
const file = safe(cls.filePath);
|
|
92541
|
+
try {
|
|
92542
|
+
const res = await code.cypher(
|
|
92543
|
+
`MATCH (m:Method) WHERE m.class_name = "${name2}"` + (file ? ` AND m.file_path = "${file}"` : "") + " RETURN m.id, m.name, m.file_path, m.start_line ORDER BY m.start_line LIMIT 40"
|
|
92544
|
+
);
|
|
92545
|
+
return res.rows.map((r) => ({
|
|
92546
|
+
id: String(r[0] ?? ""),
|
|
92547
|
+
name: String(r[1] ?? ""),
|
|
92548
|
+
filePath: String(r[2] ?? ""),
|
|
92549
|
+
...typeof r[3] === "number" ? { startLine: r[3] } : {}
|
|
92550
|
+
})).filter((s5) => s5.id !== "" && s5.name !== "");
|
|
92551
|
+
} catch {
|
|
92552
|
+
return [];
|
|
92553
|
+
}
|
|
92554
|
+
}
|
|
92481
92555
|
function formatRegressionCitation(recentChanges, changes, seedFile) {
|
|
92482
92556
|
const matchesSeed = (f5) => seedFile === void 0 || f5 === seedFile || seedFile.endsWith(f5) || f5.endsWith(seedFile);
|
|
92483
92557
|
const all = recentChanges?.commits ?? [];
|
|
@@ -92847,7 +92921,8 @@ async function investigate(input, deps) {
|
|
|
92847
92921
|
count: sig.count,
|
|
92848
92922
|
message: sig.sampleMessage ?? "",
|
|
92849
92923
|
evId: existingEvId,
|
|
92850
|
-
isNew: sig.isNew ?? false
|
|
92924
|
+
isNew: sig.isNew ?? false,
|
|
92925
|
+
level: sig.level ?? null
|
|
92851
92926
|
});
|
|
92852
92927
|
}
|
|
92853
92928
|
}
|
|
@@ -92894,12 +92969,74 @@ async function investigate(input, deps) {
|
|
|
92894
92969
|
count: sig.count,
|
|
92895
92970
|
message: sig.sampleMessage ?? "",
|
|
92896
92971
|
evId: ev.id,
|
|
92897
|
-
isNew: sig.isNew ?? false
|
|
92972
|
+
isNew: sig.isNew ?? false,
|
|
92973
|
+
level: sig.level ?? null
|
|
92898
92974
|
});
|
|
92899
92975
|
}
|
|
92900
92976
|
} catch {
|
|
92901
92977
|
}
|
|
92902
92978
|
}
|
|
92979
|
+
if (top !== void 0 && typeof deps.code?.searchSymbols === "function") {
|
|
92980
|
+
const GAP_D_MAX_RESOLVE = 12;
|
|
92981
|
+
const seedScope = /* @__PURE__ */ new Set([top.id, ...(ctx?.callees ?? []).map((c4) => c4.id)]);
|
|
92982
|
+
const alreadyEmitted = new Set(seedEmittedJoins.map((j3) => j3.code));
|
|
92983
|
+
const candidates = analysis.signatures.filter(
|
|
92984
|
+
(s5) => s5.key !== "" && s5.key !== "(none)" && s5.count > 0 && !alreadyEmitted.has(s5.key)
|
|
92985
|
+
).sort((a2, b5) => b5.count - a2.count).slice(0, GAP_D_MAX_RESOLVE);
|
|
92986
|
+
for (const sig of candidates) {
|
|
92987
|
+
let raiser;
|
|
92988
|
+
try {
|
|
92989
|
+
raiser = (await deps.code.searchSymbols(sig.key, 1))[0];
|
|
92990
|
+
} catch {
|
|
92991
|
+
continue;
|
|
92992
|
+
}
|
|
92993
|
+
if (raiser === void 0 || !seedScope.has(raiser.id)) continue;
|
|
92994
|
+
let evId = directEvIdByKey.get(sig.key);
|
|
92995
|
+
if (evId === void 0) {
|
|
92996
|
+
const where = raiser.id === top.id ? "the seed" : "a callee of the seed";
|
|
92997
|
+
const links = raiser.startLine !== void 0 && raiser.startLine > 0 ? { symbolId: raiser.id, file: raiser.filePath, line: raiser.startLine } : { symbolId: raiser.id, file: raiser.filePath };
|
|
92998
|
+
const ev = mkEv(
|
|
92999
|
+
"log",
|
|
93000
|
+
`event_code ${sig.key}: ${sig.count}x in Elasticsearch \u2014 raised by ${raiser.name} (${where})`.slice(
|
|
93001
|
+
0,
|
|
93002
|
+
220
|
|
93003
|
+
),
|
|
93004
|
+
{
|
|
93005
|
+
signature: sig.key,
|
|
93006
|
+
count: sig.count,
|
|
93007
|
+
firstSeen: sig.firstSeen,
|
|
93008
|
+
lastSeen: sig.lastSeen,
|
|
93009
|
+
services: sig.services,
|
|
93010
|
+
isNew: sig.isNew ?? false,
|
|
93011
|
+
ratio: sig.ratio ?? null,
|
|
93012
|
+
sampleMessage: sig.sampleMessage ?? null,
|
|
93013
|
+
relevanceClass: "direct",
|
|
93014
|
+
relevanceReason: `event_code ${sig.key} is raised by ${raiser.name} (${where})`,
|
|
93015
|
+
crossSignalJoin: true,
|
|
93016
|
+
seedEmitted: true
|
|
93017
|
+
},
|
|
93018
|
+
links,
|
|
93019
|
+
sig.lastSeen || void 0,
|
|
93020
|
+
0.9
|
|
93021
|
+
);
|
|
93022
|
+
if (sig.isNew) ev.isNew = sig.isNew;
|
|
93023
|
+
if (typeof sig.ratio === "number" && Number.isFinite(sig.ratio)) ev.ratio = sig.ratio;
|
|
93024
|
+
logEvIds.push(ev.id);
|
|
93025
|
+
directLogEvIds.push(ev.id);
|
|
93026
|
+
directSignatures.push({ key: sig.key, count: sig.count, message: sig.sampleMessage ?? "" });
|
|
93027
|
+
directEvIdByKey.set(sig.key, ev.id);
|
|
93028
|
+
evId = ev.id;
|
|
93029
|
+
}
|
|
93030
|
+
seedEmittedJoins.push({
|
|
93031
|
+
code: sig.key,
|
|
93032
|
+
count: sig.count,
|
|
93033
|
+
message: sig.sampleMessage ?? "",
|
|
93034
|
+
evId,
|
|
93035
|
+
isNew: sig.isNew ?? false,
|
|
93036
|
+
level: sig.level ?? null
|
|
93037
|
+
});
|
|
93038
|
+
}
|
|
93039
|
+
}
|
|
92903
93040
|
if (typeof deps.logs.aggregateErrors === "function") {
|
|
92904
93041
|
const sigForEntities = analysis.signatures.find(
|
|
92905
93042
|
(s5) => detectEntityFields(s5.sampleContext).length > 0
|
|
@@ -93161,7 +93298,8 @@ async function investigate(input, deps) {
|
|
|
93161
93298
|
const matchesHint = serviceFilter.length === 0 && hintIsPerf && hintTokens.some(
|
|
93162
93299
|
(t2) => t2.length >= 4 && (panelLower.includes(t2) || labelVals.some((v4) => v4.includes(t2)))
|
|
93163
93300
|
);
|
|
93164
|
-
|
|
93301
|
+
const genericPerf = serviceFilter.length === 0 && hintIsPerf && f5.anomaly === "latency-spike";
|
|
93302
|
+
if (matchesService || matchesHint || genericPerf) latencyMetricEvIds.push(ev.id);
|
|
93165
93303
|
} else if (f5.anomaly === "queue-growth") {
|
|
93166
93304
|
const panelNorm = f5.panelTitle.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
|
93167
93305
|
const labelNorms = Object.values(f5.labels).map(
|
|
@@ -93483,25 +93621,45 @@ async function investigate(input, deps) {
|
|
|
93483
93621
|
});
|
|
93484
93622
|
}
|
|
93485
93623
|
if (seedEmittedJoins.length > 0 && top && seedEv) {
|
|
93486
|
-
const ordered = [...seedEmittedJoins].sort((a2, b5) =>
|
|
93624
|
+
const ordered = [...seedEmittedJoins].sort((a2, b5) => {
|
|
93625
|
+
const ta = seedEmittedSeverityTier(a2.level, a2.code, a2.message);
|
|
93626
|
+
const tb = seedEmittedSeverityTier(b5.level, b5.code, b5.message);
|
|
93627
|
+
if (ta !== tb) return tb - ta;
|
|
93628
|
+
return b5.count - a2.count;
|
|
93629
|
+
});
|
|
93487
93630
|
const lead = ordered[0];
|
|
93488
93631
|
if (lead !== void 0) {
|
|
93489
93632
|
const sourceEvidenceIds = [
|
|
93490
93633
|
.../* @__PURE__ */ new Set([...ordered.map((j3) => j3.evId), seedEv.id])
|
|
93491
93634
|
];
|
|
93635
|
+
const tier = seedEmittedSeverityTier(lead.level, lead.code, lead.message);
|
|
93492
93636
|
const volumeBoost = Math.min(0.1, Math.log10(Math.max(1, lead.count)) * 0.025);
|
|
93493
|
-
const baseScore = clamp015(0.6 + volumeBoost);
|
|
93494
93637
|
const msgClause = lead.message ? ` \u2014 "${lead.message.replace(/\s+/g, " ").trim().slice(0, 90)}"` : "";
|
|
93638
|
+
let title;
|
|
93639
|
+
let baseScore;
|
|
93640
|
+
if (tier === SEV_ERROR) {
|
|
93641
|
+
title = `Runtime error ${lead.code} (${lead.count}x in Elasticsearch) is raised by ${top.name} \u2014 the likely failure${msgClause}`;
|
|
93642
|
+
baseScore = clamp015(0.6 + volumeBoost);
|
|
93643
|
+
} else if (tier === SEV_WARN) {
|
|
93644
|
+
title = `Warning ${lead.code} (${lead.count}x in Elasticsearch) is raised by ${top.name}${msgClause}`;
|
|
93645
|
+
baseScore = clamp015(0.42 + volumeBoost);
|
|
93646
|
+
} else {
|
|
93647
|
+
title = `Diagnostic signal ${lead.code} (${lead.count}x in Elasticsearch) is logged by ${top.name} \u2014 informational, not an error${msgClause}`;
|
|
93648
|
+
baseScore = clamp015(0.3 + volumeBoost);
|
|
93649
|
+
}
|
|
93495
93650
|
causeInputs.push({
|
|
93496
93651
|
id: "cause:seed-emitted-error",
|
|
93497
|
-
title:
|
|
93498
|
-
0,
|
|
93499
|
-
220
|
|
93500
|
-
),
|
|
93652
|
+
title: title.slice(0, 220),
|
|
93501
93653
|
category: "error-correlation",
|
|
93502
93654
|
sourceEvidenceIds,
|
|
93503
93655
|
baseScore,
|
|
93504
|
-
metadata: {
|
|
93656
|
+
metadata: {
|
|
93657
|
+
blastRadius,
|
|
93658
|
+
seedEmitted: true,
|
|
93659
|
+
code: lead.code,
|
|
93660
|
+
count: lead.count,
|
|
93661
|
+
severity: tier === SEV_ERROR ? "error" : tier === SEV_WARN ? "warn" : "info"
|
|
93662
|
+
}
|
|
93505
93663
|
});
|
|
93506
93664
|
}
|
|
93507
93665
|
}
|
|
@@ -93609,7 +93767,11 @@ async function investigate(input, deps) {
|
|
|
93609
93767
|
const seedLinkedEvIds = /* @__PURE__ */ new Set([
|
|
93610
93768
|
...directLogEvIds,
|
|
93611
93769
|
...seedEv ? [seedEv.id] : [],
|
|
93612
|
-
...impactEv ? [impactEv.id] : []
|
|
93770
|
+
...impactEv ? [impactEv.id] : [],
|
|
93771
|
+
// GAP B: for a performance hint, a latency/error-rate anomaly is the structural answer,
|
|
93772
|
+
// not co-occurring noise — let the metric-latency cause headline rather than yielding to
|
|
93773
|
+
// a higher-volume but lower-severity log code.
|
|
93774
|
+
...looksPerformance(hint) ? latencyMetricEvIds : []
|
|
93613
93775
|
]);
|
|
93614
93776
|
const isLinkedToSeed = (ids2) => ids2.some((id) => seedLinkedEvIds.has(id));
|
|
93615
93777
|
const topLinkedCause = rankedCauses.find(
|
|
@@ -93638,7 +93800,14 @@ async function investigate(input, deps) {
|
|
|
93638
93800
|
}
|
|
93639
93801
|
}
|
|
93640
93802
|
}
|
|
93641
|
-
|
|
93803
|
+
let classMethods = [];
|
|
93804
|
+
if (code && isClassSymbol(top)) {
|
|
93805
|
+
const richest = flowPool.slice().sort((a2, b5) => b5.steps.length - a2.steps.length)[0];
|
|
93806
|
+
if (richest === void 0 || richest.steps.length <= 1) {
|
|
93807
|
+
classMethods = await fetchClassMethods(code, top);
|
|
93808
|
+
}
|
|
93809
|
+
}
|
|
93810
|
+
behavioral = buildBehavioralWalkthrough(hint, top, ctx ?? void 0, flowPool, classMethods);
|
|
93642
93811
|
const noisy = hasIncidentSignal ? ' (note: live error signals also exist in this codebase \u2014 rephrase as a symptom like "X failing" for a fault hunt)' : "";
|
|
93643
93812
|
summary = `${seedDisclaimer}${banner2}"${hint}" reads as a "how does it work" question \u2014 here is the code path${noisy}:
|
|
93644
93813
|
|