@merittdev/horus 0.12.2 → 0.12.3
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 +535 -232
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -65880,8 +65880,8 @@ init_cjs_shims();
|
|
|
65880
65880
|
|
|
65881
65881
|
// ../../packages/core/src/version.ts
|
|
65882
65882
|
init_cjs_shims();
|
|
65883
|
-
var HORUS_VERSION = true ? "0.12.
|
|
65884
|
-
var PINNED_SOURCE_VERSION = "2.0.
|
|
65883
|
+
var HORUS_VERSION = true ? "0.12.3" : "dev";
|
|
65884
|
+
var PINNED_SOURCE_VERSION = "2.0.2";
|
|
65885
65885
|
|
|
65886
65886
|
// ../../packages/core/src/evidence.ts
|
|
65887
65887
|
init_cjs_shims();
|
|
@@ -71127,6 +71127,42 @@ async function assertSourceVersionPinned() {
|
|
|
71127
71127
|
function isAnalyzed(root) {
|
|
71128
71128
|
return (0, import_node_fs3.existsSync)((0, import_node_path3.join)(root, ".horus", "source"));
|
|
71129
71129
|
}
|
|
71130
|
+
var CURRENT_STORE_BACKEND = (process.env["HORUS_SOURCE_STORAGE_BACKEND"] ?? "sqlite").trim().toLowerCase();
|
|
71131
|
+
function readSourceMeta(root) {
|
|
71132
|
+
try {
|
|
71133
|
+
const raw = (0, import_node_fs3.readFileSync)((0, import_node_path3.join)(root, ".horus", "source", "meta.json"), "utf8");
|
|
71134
|
+
const j3 = JSON.parse(raw);
|
|
71135
|
+
const stats = j3["stats"] ?? void 0;
|
|
71136
|
+
return {
|
|
71137
|
+
storeBackend: typeof j3["store_backend"] === "string" ? j3["store_backend"].toLowerCase() : void 0,
|
|
71138
|
+
embeddingsComplete: typeof j3["embeddings_complete"] === "boolean" ? j3["embeddings_complete"] : void 0,
|
|
71139
|
+
stats: stats ? { symbols: stats["symbols"], embeddings: stats["embeddings"] } : void 0
|
|
71140
|
+
};
|
|
71141
|
+
} catch {
|
|
71142
|
+
return null;
|
|
71143
|
+
}
|
|
71144
|
+
}
|
|
71145
|
+
function metaHasEmbeddings(meta) {
|
|
71146
|
+
if (meta.embeddingsComplete === false) return false;
|
|
71147
|
+
const embeddings = meta.stats?.embeddings ?? 0;
|
|
71148
|
+
const symbols = meta.stats?.symbols ?? 0;
|
|
71149
|
+
return !(symbols > 0 && embeddings === 0);
|
|
71150
|
+
}
|
|
71151
|
+
function indexNeedsReanalyze(root) {
|
|
71152
|
+
if (!isAnalyzed(root)) return null;
|
|
71153
|
+
if (CURRENT_STORE_BACKEND !== "kuzu" && (0, import_node_fs3.existsSync)((0, import_node_path3.join)(root, ".horus", "source", "kuzu"))) {
|
|
71154
|
+
return "legacy K\xF9zuDB store present";
|
|
71155
|
+
}
|
|
71156
|
+
const meta = readSourceMeta(root);
|
|
71157
|
+
if (meta === null) return null;
|
|
71158
|
+
if (meta.storeBackend && meta.storeBackend !== CURRENT_STORE_BACKEND) {
|
|
71159
|
+
return `store backend changed (${meta.storeBackend} -> ${CURRENT_STORE_BACKEND})`;
|
|
71160
|
+
}
|
|
71161
|
+
if (!metaHasEmbeddings(meta)) {
|
|
71162
|
+
return "index has no semantic embeddings (symbols present but 0 vectors)";
|
|
71163
|
+
}
|
|
71164
|
+
return null;
|
|
71165
|
+
}
|
|
71130
71166
|
async function analyzeRepo(root) {
|
|
71131
71167
|
const bin = await resolveSourceBin();
|
|
71132
71168
|
if (!bin) throw new Error("horus-source not found on PATH. Install it: curl -fsSL https://horus.sh/install.sh | bash");
|
|
@@ -71457,7 +71493,7 @@ var SourceCodeProvider = class {
|
|
|
71457
71493
|
if (n4.className) symbol.className = n4.className;
|
|
71458
71494
|
if (n4.language) symbol.language = n4.language;
|
|
71459
71495
|
const content = n4.content;
|
|
71460
|
-
const
|
|
71496
|
+
const snippet2 = typeof content === "string" ? content.slice(0, 600) : void 0;
|
|
71461
71497
|
const sourceBody = typeof content === "string" ? content.slice(0, 2e4) : void 0;
|
|
71462
71498
|
const callees = (detail.callees ?? []).map((c4) => this.serializedToSymbol(c4.node));
|
|
71463
71499
|
const callers = (detail.callers ?? []).map((c4) => this.serializedToSymbol(c4.node));
|
|
@@ -71471,7 +71507,7 @@ var SourceCodeProvider = class {
|
|
|
71471
71507
|
}));
|
|
71472
71508
|
return {
|
|
71473
71509
|
symbol,
|
|
71474
|
-
snippet,
|
|
71510
|
+
snippet: snippet2,
|
|
71475
71511
|
...sourceBody !== void 0 ? { sourceBody } : {},
|
|
71476
71512
|
callers,
|
|
71477
71513
|
callees,
|
|
@@ -93004,7 +93040,13 @@ var MEMORY_RELS = /* @__PURE__ */ new Set([
|
|
|
93004
93040
|
function isSymmetricRel(rel) {
|
|
93005
93041
|
return rel === "recurs-with";
|
|
93006
93042
|
}
|
|
93007
|
-
var INCIDENT_DERIVED_KINDS = /* @__PURE__ */ new Set([
|
|
93043
|
+
var INCIDENT_DERIVED_KINDS = /* @__PURE__ */ new Set([
|
|
93044
|
+
"confirmed-outcome",
|
|
93045
|
+
"incident-pattern",
|
|
93046
|
+
// HOR-432: auto-captured investigation memories carry the same incident-family signature/tags so
|
|
93047
|
+
// they participate in recurrence detection. CONTEXT-ONLY — never read by the scoring path.
|
|
93048
|
+
"investigation"
|
|
93049
|
+
]);
|
|
93008
93050
|
function normalizeStoredSignature(sig) {
|
|
93009
93051
|
const s5 = (sig ?? "").trim();
|
|
93010
93052
|
if (s5 === "" || /^\|*$/.test(s5)) return null;
|
|
@@ -93109,6 +93151,35 @@ function createLocalMemoryStore(db) {
|
|
|
93109
93151
|
});
|
|
93110
93152
|
return row;
|
|
93111
93153
|
},
|
|
93154
|
+
async update(id, patch, opts) {
|
|
93155
|
+
const current = await getById(id);
|
|
93156
|
+
if (current === null) throw new Error(`memory item not found: ${id}`);
|
|
93157
|
+
const set = {};
|
|
93158
|
+
if (patch.claim !== void 0) {
|
|
93159
|
+
assertClaimClean(patch.claim);
|
|
93160
|
+
set.claim = patch.claim;
|
|
93161
|
+
}
|
|
93162
|
+
if (patch.confidence !== void 0) set.confidence = patch.confidence;
|
|
93163
|
+
if (patch.payload !== void 0) set.payload = patch.payload;
|
|
93164
|
+
if (Object.keys(set).length > 0) {
|
|
93165
|
+
await db.update(memoryItem).set(set).where(eq(memoryItem.id, id));
|
|
93166
|
+
}
|
|
93167
|
+
await appendAudit({
|
|
93168
|
+
memoryId: id,
|
|
93169
|
+
action: opts.action ?? "update",
|
|
93170
|
+
actor: opts.audit.actor,
|
|
93171
|
+
fromStatus: null,
|
|
93172
|
+
toStatus: null,
|
|
93173
|
+
note: opts.audit.note ?? null,
|
|
93174
|
+
detail: {
|
|
93175
|
+
...opts.detection ? { detection: opts.detection } : {},
|
|
93176
|
+
...opts.detail ?? {}
|
|
93177
|
+
}
|
|
93178
|
+
});
|
|
93179
|
+
const updated = await getById(id);
|
|
93180
|
+
if (updated === null) throw new Error(`memory item vanished during update: ${id}`);
|
|
93181
|
+
return updated;
|
|
93182
|
+
},
|
|
93112
93183
|
get: getById,
|
|
93113
93184
|
async query(q2) {
|
|
93114
93185
|
const repo = q2.repo.trim();
|
|
@@ -93296,6 +93367,395 @@ function createLocalMemoryStore(db) {
|
|
|
93296
93367
|
};
|
|
93297
93368
|
}
|
|
93298
93369
|
|
|
93370
|
+
// ../../packages/engine/src/auto-investigation-memory.ts
|
|
93371
|
+
init_cjs_shims();
|
|
93372
|
+
|
|
93373
|
+
// ../../packages/engine/src/memory-detect.ts
|
|
93374
|
+
init_cjs_shims();
|
|
93375
|
+
var import_node_crypto3 = require("crypto");
|
|
93376
|
+
|
|
93377
|
+
// ../../packages/engine/src/memory-store.ts
|
|
93378
|
+
init_cjs_shims();
|
|
93379
|
+
var HIDDEN_STATUSES = [
|
|
93380
|
+
"forgotten",
|
|
93381
|
+
"deprecated",
|
|
93382
|
+
"contradicted"
|
|
93383
|
+
];
|
|
93384
|
+
function tokenize2(text2) {
|
|
93385
|
+
const seen = /* @__PURE__ */ new Set();
|
|
93386
|
+
const out2 = [];
|
|
93387
|
+
for (const raw of text2.toLowerCase().split(/[^a-z0-9]+/)) {
|
|
93388
|
+
if (raw !== "" && !seen.has(raw)) {
|
|
93389
|
+
seen.add(raw);
|
|
93390
|
+
out2.push(raw);
|
|
93391
|
+
}
|
|
93392
|
+
}
|
|
93393
|
+
return out2;
|
|
93394
|
+
}
|
|
93395
|
+
function scopeSpecificity(scope) {
|
|
93396
|
+
if (scope.startsWith("symbol:")) return 3;
|
|
93397
|
+
if (scope.startsWith("module:")) return 2;
|
|
93398
|
+
if (scope === "repo") return 1;
|
|
93399
|
+
return 0;
|
|
93400
|
+
}
|
|
93401
|
+
var NoopVectorIndex = class {
|
|
93402
|
+
entries = /* @__PURE__ */ new Map();
|
|
93403
|
+
async upsert(i3) {
|
|
93404
|
+
this.entries.set(i3.memoryId, {
|
|
93405
|
+
memoryId: i3.memoryId,
|
|
93406
|
+
repo: i3.repo,
|
|
93407
|
+
scope: i3.scope,
|
|
93408
|
+
tokens: tokenize2(i3.claim)
|
|
93409
|
+
});
|
|
93410
|
+
}
|
|
93411
|
+
async search(i3) {
|
|
93412
|
+
const repo = i3.repo.trim();
|
|
93413
|
+
if (repo === "") return [];
|
|
93414
|
+
const queryTokens = tokenize2(i3.query);
|
|
93415
|
+
if (queryTokens.length === 0) return [];
|
|
93416
|
+
const scored = [...this.entries.values()].filter((e) => e.repo === repo).map((e) => ({
|
|
93417
|
+
memoryId: e.memoryId,
|
|
93418
|
+
score: tagOverlap(queryTokens, e.tokens),
|
|
93419
|
+
specificity: scopeSpecificity(e.scope)
|
|
93420
|
+
})).filter((h3) => h3.score > 0).sort(
|
|
93421
|
+
(a2, b5) => b5.score - a2.score || b5.specificity - a2.specificity || (a2.memoryId < b5.memoryId ? -1 : a2.memoryId > b5.memoryId ? 1 : 0)
|
|
93422
|
+
);
|
|
93423
|
+
const limit = Number.isFinite(i3.limit) && i3.limit > 0 ? Math.floor(i3.limit) : scored.length;
|
|
93424
|
+
return scored.slice(0, limit).map((h3) => ({ memoryId: h3.memoryId, score: h3.score }));
|
|
93425
|
+
}
|
|
93426
|
+
async remove(memoryId) {
|
|
93427
|
+
this.entries.delete(memoryId);
|
|
93428
|
+
}
|
|
93429
|
+
};
|
|
93430
|
+
|
|
93431
|
+
// ../../packages/engine/src/memory-detect.ts
|
|
93432
|
+
var TAG_OVERLAP_MIN = 0.6;
|
|
93433
|
+
var CLAIM_JACCARD_MIN = 0.6;
|
|
93434
|
+
var MIN_CLAIM_TOKENS = 4;
|
|
93435
|
+
var INCIDENT_DERIVED_KINDS2 = /* @__PURE__ */ new Set([
|
|
93436
|
+
"confirmed-outcome",
|
|
93437
|
+
"incident-pattern"
|
|
93438
|
+
]);
|
|
93439
|
+
function usableSignature(sig) {
|
|
93440
|
+
const s5 = (sig ?? "").trim();
|
|
93441
|
+
if (s5 === "" || /^\|*$/.test(s5)) return null;
|
|
93442
|
+
const components = s5.split(/[|,]/).map((c4) => c4.trim()).filter((c4) => c4 !== "");
|
|
93443
|
+
if (components.length === 0) return null;
|
|
93444
|
+
if (components.every((c4) => isGenericTag(c4))) return null;
|
|
93445
|
+
return s5;
|
|
93446
|
+
}
|
|
93447
|
+
function sharedNonGenericTags(a2, b5) {
|
|
93448
|
+
const setB = new Set(b5);
|
|
93449
|
+
const seen = /* @__PURE__ */ new Set();
|
|
93450
|
+
const out2 = [];
|
|
93451
|
+
for (const t2 of a2) {
|
|
93452
|
+
if (setB.has(t2) && !isGenericTag(t2) && !seen.has(t2)) {
|
|
93453
|
+
seen.add(t2);
|
|
93454
|
+
out2.push(t2);
|
|
93455
|
+
}
|
|
93456
|
+
}
|
|
93457
|
+
return out2;
|
|
93458
|
+
}
|
|
93459
|
+
function recurrenceEdgeId(idA, idB) {
|
|
93460
|
+
const [lo, hi] = idA <= idB ? [idA, idB] : [idB, idA];
|
|
93461
|
+
const h3 = (0, import_node_crypto3.createHash)("sha256").update(`recurs-with\0${lo}\0${hi}`).digest("hex");
|
|
93462
|
+
return `lnk_recurs_${h3.slice(0, 32)}`;
|
|
93463
|
+
}
|
|
93464
|
+
function normalizeCause(s5) {
|
|
93465
|
+
return (s5 ?? "").trim().toLowerCase().replace(/\s+/g, " ");
|
|
93466
|
+
}
|
|
93467
|
+
function outcomeConflict(a2, b5) {
|
|
93468
|
+
const opposite = a2.resolved === "yes" && b5.resolved === "no" || a2.resolved === "no" && b5.resolved === "yes";
|
|
93469
|
+
if (opposite) {
|
|
93470
|
+
return `outcome verdicts disagree (${a2.resolved} vs ${b5.resolved})`;
|
|
93471
|
+
}
|
|
93472
|
+
const ca = normalizeCause(a2.confirmedCause);
|
|
93473
|
+
const cb = normalizeCause(b5.confirmedCause);
|
|
93474
|
+
if (ca !== "" && cb !== "" && ca !== cb) {
|
|
93475
|
+
return `confirmed causes differ ("${a2.confirmedCause}" vs "${b5.confirmedCause}")`;
|
|
93476
|
+
}
|
|
93477
|
+
return null;
|
|
93478
|
+
}
|
|
93479
|
+
function recurrenceReason(a2, b5) {
|
|
93480
|
+
const sigA = usableSignature(a2.signature);
|
|
93481
|
+
const sigB = usableSignature(b5.signature);
|
|
93482
|
+
if (sigA !== null && sigB !== null && sigA === sigB) {
|
|
93483
|
+
return `identical incident signature "${sigA}"`;
|
|
93484
|
+
}
|
|
93485
|
+
const tagsA = a2.tags ?? [];
|
|
93486
|
+
const tagsB = b5.tags ?? [];
|
|
93487
|
+
const shared = sharedNonGenericTags(tagsA, tagsB);
|
|
93488
|
+
const overlap = tagOverlap([...tagsA], [...tagsB]);
|
|
93489
|
+
if (overlap >= TAG_OVERLAP_MIN && shared.length >= 1) {
|
|
93490
|
+
return `tag overlap ${overlap.toFixed(2)} on [${shared.join(", ")}]`;
|
|
93491
|
+
}
|
|
93492
|
+
if (sigA === null && sigB === null && shared.length >= 1) {
|
|
93493
|
+
const ta = tokenize2(a2.claim);
|
|
93494
|
+
const tb = tokenize2(b5.claim);
|
|
93495
|
+
if (ta.length >= MIN_CLAIM_TOKENS && tb.length >= MIN_CLAIM_TOKENS) {
|
|
93496
|
+
const j3 = tagOverlap(ta, tb);
|
|
93497
|
+
if (j3 >= CLAIM_JACCARD_MIN) {
|
|
93498
|
+
return `claim similarity ${j3.toFixed(2)} corroborated by [${shared.join(", ")}]`;
|
|
93499
|
+
}
|
|
93500
|
+
}
|
|
93501
|
+
}
|
|
93502
|
+
return null;
|
|
93503
|
+
}
|
|
93504
|
+
function edgeKey(rel, from, to) {
|
|
93505
|
+
if (rel === "recurs-with" || rel === "contradicts") {
|
|
93506
|
+
const [lo, hi] = from <= to ? [from, to] : [to, from];
|
|
93507
|
+
return `${rel}|${lo}|${hi}`;
|
|
93508
|
+
}
|
|
93509
|
+
return `${rel}|${from}|${to}`;
|
|
93510
|
+
}
|
|
93511
|
+
async function detectMemoryEdges(store, opts, deps = {}) {
|
|
93512
|
+
const repo = opts.repo.trim();
|
|
93513
|
+
if (repo === "") return [];
|
|
93514
|
+
const items = await store.query({ repo, limit: opts.limit ?? 1e3 });
|
|
93515
|
+
const candidates = items.filter((i3) => INCIDENT_DERIVED_KINDS2.has(i3.kind));
|
|
93516
|
+
if (candidates.length < 2) return [];
|
|
93517
|
+
const existing = /* @__PURE__ */ new Set();
|
|
93518
|
+
const investigationOf = /* @__PURE__ */ new Map();
|
|
93519
|
+
for (const item of candidates) {
|
|
93520
|
+
const out2 = await store.links(item.id, { direction: "out" });
|
|
93521
|
+
for (const l2 of out2) {
|
|
93522
|
+
if (l2.toKind === "memory") {
|
|
93523
|
+
existing.add(edgeKey(l2.rel, l2.fromMemoryId, l2.toRef));
|
|
93524
|
+
} else if (l2.toKind === "incident" && l2.rel === "about-incident") {
|
|
93525
|
+
if (!investigationOf.has(item.id)) investigationOf.set(item.id, l2.toRef);
|
|
93526
|
+
}
|
|
93527
|
+
}
|
|
93528
|
+
}
|
|
93529
|
+
const proposals = [];
|
|
93530
|
+
const outcomeCache = /* @__PURE__ */ new Map();
|
|
93531
|
+
const resolveOutcome = async (investigationId) => {
|
|
93532
|
+
if (deps.outcomeFor === void 0) return null;
|
|
93533
|
+
if (outcomeCache.has(investigationId)) return outcomeCache.get(investigationId) ?? null;
|
|
93534
|
+
let v4 = null;
|
|
93535
|
+
try {
|
|
93536
|
+
v4 = await deps.outcomeFor(investigationId);
|
|
93537
|
+
} catch {
|
|
93538
|
+
v4 = null;
|
|
93539
|
+
}
|
|
93540
|
+
outcomeCache.set(investigationId, v4);
|
|
93541
|
+
return v4;
|
|
93542
|
+
};
|
|
93543
|
+
for (let i3 = 0; i3 < candidates.length; i3++) {
|
|
93544
|
+
const a2 = candidates[i3];
|
|
93545
|
+
for (let j3 = i3 + 1; j3 < candidates.length; j3++) {
|
|
93546
|
+
const b5 = candidates[j3];
|
|
93547
|
+
if (!existing.has(edgeKey("recurs-with", a2.id, b5.id))) {
|
|
93548
|
+
const reason = recurrenceReason(a2, b5);
|
|
93549
|
+
if (reason !== null) {
|
|
93550
|
+
const [lo, hi] = a2.id <= b5.id ? [a2.id, b5.id] : [b5.id, a2.id];
|
|
93551
|
+
proposals.push({
|
|
93552
|
+
id: recurrenceEdgeId(a2.id, b5.id),
|
|
93553
|
+
fromMemoryId: lo,
|
|
93554
|
+
toMemoryId: hi,
|
|
93555
|
+
rel: "recurs-with",
|
|
93556
|
+
detection: "auto:recurrence",
|
|
93557
|
+
reason
|
|
93558
|
+
});
|
|
93559
|
+
}
|
|
93560
|
+
}
|
|
93561
|
+
if (deps.outcomeFor !== void 0 && a2.kind === "confirmed-outcome" && b5.kind === "confirmed-outcome" && !existing.has(edgeKey("contradicts", a2.id, b5.id))) {
|
|
93562
|
+
const sigA = usableSignature(a2.signature);
|
|
93563
|
+
const sigB = usableSignature(b5.signature);
|
|
93564
|
+
const invA = investigationOf.get(a2.id);
|
|
93565
|
+
const invB = investigationOf.get(b5.id);
|
|
93566
|
+
if (sigA !== null && sigA === sigB && invA !== void 0 && invB !== void 0) {
|
|
93567
|
+
const oa = await resolveOutcome(invA);
|
|
93568
|
+
const ob = await resolveOutcome(invB);
|
|
93569
|
+
if (oa !== null && ob !== null) {
|
|
93570
|
+
const reason = outcomeConflict(oa, ob);
|
|
93571
|
+
if (reason !== null) {
|
|
93572
|
+
const [lo, hi] = a2.id <= b5.id ? [a2.id, b5.id] : [b5.id, a2.id];
|
|
93573
|
+
const invLo = lo === a2.id ? invA : invB;
|
|
93574
|
+
const invHi = lo === a2.id ? invB : invA;
|
|
93575
|
+
proposals.push({
|
|
93576
|
+
id: "",
|
|
93577
|
+
// directional — the store mints the id
|
|
93578
|
+
fromMemoryId: lo,
|
|
93579
|
+
toMemoryId: hi,
|
|
93580
|
+
rel: "contradicts",
|
|
93581
|
+
detection: "auto:contradiction",
|
|
93582
|
+
reason,
|
|
93583
|
+
detail: { investigationIds: [invLo, invHi] }
|
|
93584
|
+
});
|
|
93585
|
+
}
|
|
93586
|
+
}
|
|
93587
|
+
}
|
|
93588
|
+
}
|
|
93589
|
+
}
|
|
93590
|
+
}
|
|
93591
|
+
return proposals;
|
|
93592
|
+
}
|
|
93593
|
+
|
|
93594
|
+
// ../../packages/engine/src/auto-investigation-memory.ts
|
|
93595
|
+
var RECURRENCE_SCAN_LIMIT = 100;
|
|
93596
|
+
var MAX_INVESTIGATION_IDS = 20;
|
|
93597
|
+
function snippet(text2, max = 140) {
|
|
93598
|
+
const s5 = text2.trim().replace(/\s+/g, " ");
|
|
93599
|
+
return s5.length > max ? `${s5.slice(0, max - 1)}\u2026` : s5;
|
|
93600
|
+
}
|
|
93601
|
+
function autoInvestigationMemoryEnabled() {
|
|
93602
|
+
const v4 = (process.env.HORUS_AUTO_INVESTIGATION_MEMORY ?? "").trim().toLowerCase();
|
|
93603
|
+
return v4 !== "0" && v4 !== "false" && v4 !== "off" && v4 !== "no";
|
|
93604
|
+
}
|
|
93605
|
+
function deriveInvestigationFields(report) {
|
|
93606
|
+
const hint = report.input.hint;
|
|
93607
|
+
const topHyp = report.hypotheses[0];
|
|
93608
|
+
const headline = topHyp !== void 0 ? snippet(topHyp.statement || topHyp.category || report.summary) : snippet(report.summary || "(no leading hypothesis)");
|
|
93609
|
+
return {
|
|
93610
|
+
hint,
|
|
93611
|
+
claim: `Investigation: ${snippet(hint)} -> ${headline}`,
|
|
93612
|
+
confidence: report.confidence,
|
|
93613
|
+
signature: deriveSignature(report),
|
|
93614
|
+
tags: deriveTags(report),
|
|
93615
|
+
topHypothesisCategory: topHyp !== void 0 ? topHyp.category ?? null : null
|
|
93616
|
+
};
|
|
93617
|
+
}
|
|
93618
|
+
function recurrenceCountOf(payload) {
|
|
93619
|
+
if (payload !== null && typeof payload === "object") {
|
|
93620
|
+
const c4 = payload.recurrenceCount;
|
|
93621
|
+
if (typeof c4 === "number" && Number.isFinite(c4) && c4 >= 1) return Math.floor(c4);
|
|
93622
|
+
}
|
|
93623
|
+
return 1;
|
|
93624
|
+
}
|
|
93625
|
+
async function createInvestigationMemory(store, investigationId, report, audit) {
|
|
93626
|
+
const repo = report.input.repo?.trim() || null;
|
|
93627
|
+
if (repo === null) return null;
|
|
93628
|
+
const fields = deriveInvestigationFields(report);
|
|
93629
|
+
const item = {
|
|
93630
|
+
id: "",
|
|
93631
|
+
kind: "investigation",
|
|
93632
|
+
source: "investigation",
|
|
93633
|
+
scope: "repo",
|
|
93634
|
+
visibility: "private",
|
|
93635
|
+
// HONEST confidence — the report's own value, never inflated.
|
|
93636
|
+
confidence: fields.confidence,
|
|
93637
|
+
claim: fields.claim,
|
|
93638
|
+
repo,
|
|
93639
|
+
evidence: [],
|
|
93640
|
+
// Incident-family recall keys (same derivation as incident_memory) so this memory recurs.
|
|
93641
|
+
signature: fields.signature,
|
|
93642
|
+
tags: fields.tags,
|
|
93643
|
+
payload: {
|
|
93644
|
+
investigationId,
|
|
93645
|
+
hint: fields.hint,
|
|
93646
|
+
topHypothesis: fields.topHypothesisCategory,
|
|
93647
|
+
// First sighting: a single investigation is "recurred 1×". Bumped on each later recurrence.
|
|
93648
|
+
recurrenceCount: 1
|
|
93649
|
+
}
|
|
93650
|
+
};
|
|
93651
|
+
const created = await store.add(item, audit);
|
|
93652
|
+
if (investigationId !== null && investigationId.trim() !== "") {
|
|
93653
|
+
await store.addLink(
|
|
93654
|
+
{
|
|
93655
|
+
id: "",
|
|
93656
|
+
fromMemoryId: created.id,
|
|
93657
|
+
rel: "about-incident",
|
|
93658
|
+
toKind: "incident",
|
|
93659
|
+
toRef: investigationId
|
|
93660
|
+
},
|
|
93661
|
+
{ detection: "structural", audit }
|
|
93662
|
+
);
|
|
93663
|
+
}
|
|
93664
|
+
return created;
|
|
93665
|
+
}
|
|
93666
|
+
async function detectRecurrence(store, probe2, repo) {
|
|
93667
|
+
const r = repo.trim();
|
|
93668
|
+
if (r === "") return null;
|
|
93669
|
+
const existing = await store.query({
|
|
93670
|
+
repo: r,
|
|
93671
|
+
kind: ["investigation"],
|
|
93672
|
+
limit: RECURRENCE_SCAN_LIMIT
|
|
93673
|
+
});
|
|
93674
|
+
for (const candidate of existing) {
|
|
93675
|
+
if (recurrenceReason(candidate, probe2) !== null) return candidate;
|
|
93676
|
+
}
|
|
93677
|
+
return null;
|
|
93678
|
+
}
|
|
93679
|
+
async function consolidateRecurrence(store, existing, investigationId, fields, reportTime, audit) {
|
|
93680
|
+
const prev = existing.payload ?? {};
|
|
93681
|
+
const recurrenceCount = recurrenceCountOf(prev) + 1;
|
|
93682
|
+
const prevIds = Array.isArray(prev.investigationIds) ? prev.investigationIds.filter((x4) => typeof x4 === "string") : [];
|
|
93683
|
+
const seedIds = prevIds.length > 0 ? prevIds : typeof prev.investigationId === "string" ? [prev.investigationId] : [];
|
|
93684
|
+
const thisId = investigationId !== null && investigationId.trim() !== "" ? investigationId : null;
|
|
93685
|
+
const investigationIds = (thisId !== null ? [...seedIds, thisId] : seedIds).slice(
|
|
93686
|
+
-MAX_INVESTIGATION_IDS
|
|
93687
|
+
);
|
|
93688
|
+
const payload = {
|
|
93689
|
+
...prev,
|
|
93690
|
+
recurrenceCount,
|
|
93691
|
+
lastSeenAt: reportTime.toISOString(),
|
|
93692
|
+
investigationIds,
|
|
93693
|
+
// Refresh the "latest finding" provenance; keep the original first-seen id for reference.
|
|
93694
|
+
hint: fields.hint,
|
|
93695
|
+
topHypothesis: fields.topHypothesisCategory,
|
|
93696
|
+
investigationId: typeof prev.investigationId === "string" ? prev.investigationId : seedIds[0] ?? thisId
|
|
93697
|
+
};
|
|
93698
|
+
const updated = await store.update(
|
|
93699
|
+
existing.id,
|
|
93700
|
+
{
|
|
93701
|
+
claim: fields.claim,
|
|
93702
|
+
// HONEST: the LATEST report's confidence, verbatim — never inflated, never an average.
|
|
93703
|
+
confidence: fields.confidence,
|
|
93704
|
+
payload
|
|
93705
|
+
},
|
|
93706
|
+
{
|
|
93707
|
+
audit,
|
|
93708
|
+
action: "recurrence",
|
|
93709
|
+
detection: "auto:recurrence-consolidate",
|
|
93710
|
+
detail: { recurrenceCount, ...thisId !== null ? { investigationId: thisId } : {} }
|
|
93711
|
+
}
|
|
93712
|
+
);
|
|
93713
|
+
if (thisId !== null) {
|
|
93714
|
+
await store.addLink(
|
|
93715
|
+
{
|
|
93716
|
+
id: "",
|
|
93717
|
+
fromMemoryId: existing.id,
|
|
93718
|
+
rel: "about-incident",
|
|
93719
|
+
toKind: "incident",
|
|
93720
|
+
toRef: thisId
|
|
93721
|
+
},
|
|
93722
|
+
{ detection: "structural", audit }
|
|
93723
|
+
);
|
|
93724
|
+
}
|
|
93725
|
+
return updated;
|
|
93726
|
+
}
|
|
93727
|
+
async function captureInvestigationMemory(store, investigationId, report, reportTime, audit) {
|
|
93728
|
+
const repo = report.input.repo?.trim() || null;
|
|
93729
|
+
if (repo === null) return { action: "skipped", memoryId: null, recurrenceCount: 0 };
|
|
93730
|
+
const fields = deriveInvestigationFields(report);
|
|
93731
|
+
const existing = await detectRecurrence(
|
|
93732
|
+
store,
|
|
93733
|
+
{ signature: fields.signature, tags: fields.tags, claim: fields.claim },
|
|
93734
|
+
repo
|
|
93735
|
+
);
|
|
93736
|
+
if (existing !== null) {
|
|
93737
|
+
const updated = await consolidateRecurrence(
|
|
93738
|
+
store,
|
|
93739
|
+
existing,
|
|
93740
|
+
investigationId,
|
|
93741
|
+
fields,
|
|
93742
|
+
reportTime,
|
|
93743
|
+
audit
|
|
93744
|
+
);
|
|
93745
|
+
return {
|
|
93746
|
+
action: "consolidated",
|
|
93747
|
+
memoryId: updated.id,
|
|
93748
|
+
recurrenceCount: recurrenceCountOf(updated.payload)
|
|
93749
|
+
};
|
|
93750
|
+
}
|
|
93751
|
+
const created = await createInvestigationMemory(store, investigationId, report, audit);
|
|
93752
|
+
return {
|
|
93753
|
+
action: created !== null ? "created" : "skipped",
|
|
93754
|
+
memoryId: created?.id ?? null,
|
|
93755
|
+
recurrenceCount: 1
|
|
93756
|
+
};
|
|
93757
|
+
}
|
|
93758
|
+
|
|
93299
93759
|
// ../../packages/engine/src/gaps.ts
|
|
93300
93760
|
init_cjs_shims();
|
|
93301
93761
|
function detectMissingEvidence(r, connectors = {}, mode) {
|
|
@@ -94858,8 +95318,8 @@ async function investigate(input, deps) {
|
|
|
94858
95318
|
// HOR-386 — empty-return: route to `search` for the right name (deterministic).
|
|
94859
95319
|
nextSteps: route({ command: "investigate", empty: true, query: hint })
|
|
94860
95320
|
};
|
|
94861
|
-
const
|
|
94862
|
-
if (
|
|
95321
|
+
const persistedEmpty = await persist(db, input, report2);
|
|
95322
|
+
if (persistedEmpty) report2.id = persistedEmpty.id;
|
|
94863
95323
|
return report2;
|
|
94864
95324
|
}
|
|
94865
95325
|
let label = input.service ?? hint;
|
|
@@ -96253,13 +96713,26 @@ async function investigate(input, deps) {
|
|
|
96253
96713
|
query: hint
|
|
96254
96714
|
});
|
|
96255
96715
|
report.sourceStatus = buildRuntimeSourceStatus(evidence2, connectorFlags);
|
|
96256
|
-
const
|
|
96257
|
-
|
|
96716
|
+
const persisted = await persist(db, input, report);
|
|
96717
|
+
const persistedId = persisted?.id ?? null;
|
|
96718
|
+
if (persistedId !== null) report.id = persistedId;
|
|
96258
96719
|
report.persisted = persistedId !== null;
|
|
96259
96720
|
if (persistedId !== null) {
|
|
96260
96721
|
const tags = deriveTags(report);
|
|
96261
96722
|
report.similarIncidents = await recallSimilar(db, tags, persistedId, input.repo ?? null);
|
|
96262
96723
|
await storeIncidentMemory(db, persistedId, report);
|
|
96724
|
+
const store = deps.store;
|
|
96725
|
+
if (store !== void 0 && autoInvestigationMemoryEnabled()) {
|
|
96726
|
+
try {
|
|
96727
|
+
const audit = {
|
|
96728
|
+
actor: { kind: "system" },
|
|
96729
|
+
note: "auto: investigation memory (HOR-432)"
|
|
96730
|
+
};
|
|
96731
|
+
const reportTime = persisted?.createdAt ?? /* @__PURE__ */ new Date();
|
|
96732
|
+
await captureInvestigationMemory(store, persistedId, report, reportTime, audit);
|
|
96733
|
+
} catch {
|
|
96734
|
+
}
|
|
96735
|
+
}
|
|
96263
96736
|
}
|
|
96264
96737
|
if (persistedId !== null) {
|
|
96265
96738
|
try {
|
|
@@ -96303,7 +96776,7 @@ async function persist(db, input, report) {
|
|
|
96303
96776
|
incidentInput: input,
|
|
96304
96777
|
status: "open",
|
|
96305
96778
|
summary: report.summary
|
|
96306
|
-
}).returning({ id: investigations.id });
|
|
96779
|
+
}).returning({ id: investigations.id, createdAt: investigations.createdAt });
|
|
96307
96780
|
const row = inserted[0];
|
|
96308
96781
|
if (!row) return null;
|
|
96309
96782
|
const investigationId = row.id;
|
|
@@ -96347,7 +96820,7 @@ async function persist(db, input, report) {
|
|
|
96347
96820
|
}))
|
|
96348
96821
|
);
|
|
96349
96822
|
}
|
|
96350
|
-
return investigationId;
|
|
96823
|
+
return { id: investigationId, createdAt: row.createdAt ?? null };
|
|
96351
96824
|
} catch {
|
|
96352
96825
|
return null;
|
|
96353
96826
|
}
|
|
@@ -97056,225 +97529,6 @@ function blastRadiusToJSON(r) {
|
|
|
97056
97529
|
return JSON.stringify(r, null, 2);
|
|
97057
97530
|
}
|
|
97058
97531
|
|
|
97059
|
-
// ../../packages/engine/src/memory-store.ts
|
|
97060
|
-
init_cjs_shims();
|
|
97061
|
-
var HIDDEN_STATUSES = [
|
|
97062
|
-
"forgotten",
|
|
97063
|
-
"deprecated",
|
|
97064
|
-
"contradicted"
|
|
97065
|
-
];
|
|
97066
|
-
function tokenize2(text2) {
|
|
97067
|
-
const seen = /* @__PURE__ */ new Set();
|
|
97068
|
-
const out2 = [];
|
|
97069
|
-
for (const raw of text2.toLowerCase().split(/[^a-z0-9]+/)) {
|
|
97070
|
-
if (raw !== "" && !seen.has(raw)) {
|
|
97071
|
-
seen.add(raw);
|
|
97072
|
-
out2.push(raw);
|
|
97073
|
-
}
|
|
97074
|
-
}
|
|
97075
|
-
return out2;
|
|
97076
|
-
}
|
|
97077
|
-
function scopeSpecificity(scope) {
|
|
97078
|
-
if (scope.startsWith("symbol:")) return 3;
|
|
97079
|
-
if (scope.startsWith("module:")) return 2;
|
|
97080
|
-
if (scope === "repo") return 1;
|
|
97081
|
-
return 0;
|
|
97082
|
-
}
|
|
97083
|
-
var NoopVectorIndex = class {
|
|
97084
|
-
entries = /* @__PURE__ */ new Map();
|
|
97085
|
-
async upsert(i3) {
|
|
97086
|
-
this.entries.set(i3.memoryId, {
|
|
97087
|
-
memoryId: i3.memoryId,
|
|
97088
|
-
repo: i3.repo,
|
|
97089
|
-
scope: i3.scope,
|
|
97090
|
-
tokens: tokenize2(i3.claim)
|
|
97091
|
-
});
|
|
97092
|
-
}
|
|
97093
|
-
async search(i3) {
|
|
97094
|
-
const repo = i3.repo.trim();
|
|
97095
|
-
if (repo === "") return [];
|
|
97096
|
-
const queryTokens = tokenize2(i3.query);
|
|
97097
|
-
if (queryTokens.length === 0) return [];
|
|
97098
|
-
const scored = [...this.entries.values()].filter((e) => e.repo === repo).map((e) => ({
|
|
97099
|
-
memoryId: e.memoryId,
|
|
97100
|
-
score: tagOverlap(queryTokens, e.tokens),
|
|
97101
|
-
specificity: scopeSpecificity(e.scope)
|
|
97102
|
-
})).filter((h3) => h3.score > 0).sort(
|
|
97103
|
-
(a2, b5) => b5.score - a2.score || b5.specificity - a2.specificity || (a2.memoryId < b5.memoryId ? -1 : a2.memoryId > b5.memoryId ? 1 : 0)
|
|
97104
|
-
);
|
|
97105
|
-
const limit = Number.isFinite(i3.limit) && i3.limit > 0 ? Math.floor(i3.limit) : scored.length;
|
|
97106
|
-
return scored.slice(0, limit).map((h3) => ({ memoryId: h3.memoryId, score: h3.score }));
|
|
97107
|
-
}
|
|
97108
|
-
async remove(memoryId) {
|
|
97109
|
-
this.entries.delete(memoryId);
|
|
97110
|
-
}
|
|
97111
|
-
};
|
|
97112
|
-
|
|
97113
|
-
// ../../packages/engine/src/memory-detect.ts
|
|
97114
|
-
init_cjs_shims();
|
|
97115
|
-
var import_node_crypto3 = require("crypto");
|
|
97116
|
-
var TAG_OVERLAP_MIN = 0.6;
|
|
97117
|
-
var CLAIM_JACCARD_MIN = 0.6;
|
|
97118
|
-
var MIN_CLAIM_TOKENS = 4;
|
|
97119
|
-
var INCIDENT_DERIVED_KINDS2 = /* @__PURE__ */ new Set([
|
|
97120
|
-
"confirmed-outcome",
|
|
97121
|
-
"incident-pattern"
|
|
97122
|
-
]);
|
|
97123
|
-
function usableSignature(sig) {
|
|
97124
|
-
const s5 = (sig ?? "").trim();
|
|
97125
|
-
if (s5 === "" || /^\|*$/.test(s5)) return null;
|
|
97126
|
-
const components = s5.split(/[|,]/).map((c4) => c4.trim()).filter((c4) => c4 !== "");
|
|
97127
|
-
if (components.length === 0) return null;
|
|
97128
|
-
if (components.every((c4) => isGenericTag(c4))) return null;
|
|
97129
|
-
return s5;
|
|
97130
|
-
}
|
|
97131
|
-
function sharedNonGenericTags(a2, b5) {
|
|
97132
|
-
const setB = new Set(b5);
|
|
97133
|
-
const seen = /* @__PURE__ */ new Set();
|
|
97134
|
-
const out2 = [];
|
|
97135
|
-
for (const t2 of a2) {
|
|
97136
|
-
if (setB.has(t2) && !isGenericTag(t2) && !seen.has(t2)) {
|
|
97137
|
-
seen.add(t2);
|
|
97138
|
-
out2.push(t2);
|
|
97139
|
-
}
|
|
97140
|
-
}
|
|
97141
|
-
return out2;
|
|
97142
|
-
}
|
|
97143
|
-
function recurrenceEdgeId(idA, idB) {
|
|
97144
|
-
const [lo, hi] = idA <= idB ? [idA, idB] : [idB, idA];
|
|
97145
|
-
const h3 = (0, import_node_crypto3.createHash)("sha256").update(`recurs-with\0${lo}\0${hi}`).digest("hex");
|
|
97146
|
-
return `lnk_recurs_${h3.slice(0, 32)}`;
|
|
97147
|
-
}
|
|
97148
|
-
function normalizeCause(s5) {
|
|
97149
|
-
return (s5 ?? "").trim().toLowerCase().replace(/\s+/g, " ");
|
|
97150
|
-
}
|
|
97151
|
-
function outcomeConflict(a2, b5) {
|
|
97152
|
-
const opposite = a2.resolved === "yes" && b5.resolved === "no" || a2.resolved === "no" && b5.resolved === "yes";
|
|
97153
|
-
if (opposite) {
|
|
97154
|
-
return `outcome verdicts disagree (${a2.resolved} vs ${b5.resolved})`;
|
|
97155
|
-
}
|
|
97156
|
-
const ca = normalizeCause(a2.confirmedCause);
|
|
97157
|
-
const cb = normalizeCause(b5.confirmedCause);
|
|
97158
|
-
if (ca !== "" && cb !== "" && ca !== cb) {
|
|
97159
|
-
return `confirmed causes differ ("${a2.confirmedCause}" vs "${b5.confirmedCause}")`;
|
|
97160
|
-
}
|
|
97161
|
-
return null;
|
|
97162
|
-
}
|
|
97163
|
-
function recurrenceReason(a2, b5) {
|
|
97164
|
-
const sigA = usableSignature(a2.signature);
|
|
97165
|
-
const sigB = usableSignature(b5.signature);
|
|
97166
|
-
if (sigA !== null && sigB !== null && sigA === sigB) {
|
|
97167
|
-
return `identical incident signature "${sigA}"`;
|
|
97168
|
-
}
|
|
97169
|
-
const tagsA = a2.tags ?? [];
|
|
97170
|
-
const tagsB = b5.tags ?? [];
|
|
97171
|
-
const shared = sharedNonGenericTags(tagsA, tagsB);
|
|
97172
|
-
const overlap = tagOverlap([...tagsA], [...tagsB]);
|
|
97173
|
-
if (overlap >= TAG_OVERLAP_MIN && shared.length >= 1) {
|
|
97174
|
-
return `tag overlap ${overlap.toFixed(2)} on [${shared.join(", ")}]`;
|
|
97175
|
-
}
|
|
97176
|
-
if (sigA === null && sigB === null && shared.length >= 1) {
|
|
97177
|
-
const ta = tokenize2(a2.claim);
|
|
97178
|
-
const tb = tokenize2(b5.claim);
|
|
97179
|
-
if (ta.length >= MIN_CLAIM_TOKENS && tb.length >= MIN_CLAIM_TOKENS) {
|
|
97180
|
-
const j3 = tagOverlap(ta, tb);
|
|
97181
|
-
if (j3 >= CLAIM_JACCARD_MIN) {
|
|
97182
|
-
return `claim similarity ${j3.toFixed(2)} corroborated by [${shared.join(", ")}]`;
|
|
97183
|
-
}
|
|
97184
|
-
}
|
|
97185
|
-
}
|
|
97186
|
-
return null;
|
|
97187
|
-
}
|
|
97188
|
-
function edgeKey(rel, from, to) {
|
|
97189
|
-
if (rel === "recurs-with" || rel === "contradicts") {
|
|
97190
|
-
const [lo, hi] = from <= to ? [from, to] : [to, from];
|
|
97191
|
-
return `${rel}|${lo}|${hi}`;
|
|
97192
|
-
}
|
|
97193
|
-
return `${rel}|${from}|${to}`;
|
|
97194
|
-
}
|
|
97195
|
-
async function detectMemoryEdges(store, opts, deps = {}) {
|
|
97196
|
-
const repo = opts.repo.trim();
|
|
97197
|
-
if (repo === "") return [];
|
|
97198
|
-
const items = await store.query({ repo, limit: opts.limit ?? 1e3 });
|
|
97199
|
-
const candidates = items.filter((i3) => INCIDENT_DERIVED_KINDS2.has(i3.kind));
|
|
97200
|
-
if (candidates.length < 2) return [];
|
|
97201
|
-
const existing = /* @__PURE__ */ new Set();
|
|
97202
|
-
const investigationOf = /* @__PURE__ */ new Map();
|
|
97203
|
-
for (const item of candidates) {
|
|
97204
|
-
const out2 = await store.links(item.id, { direction: "out" });
|
|
97205
|
-
for (const l2 of out2) {
|
|
97206
|
-
if (l2.toKind === "memory") {
|
|
97207
|
-
existing.add(edgeKey(l2.rel, l2.fromMemoryId, l2.toRef));
|
|
97208
|
-
} else if (l2.toKind === "incident" && l2.rel === "about-incident") {
|
|
97209
|
-
if (!investigationOf.has(item.id)) investigationOf.set(item.id, l2.toRef);
|
|
97210
|
-
}
|
|
97211
|
-
}
|
|
97212
|
-
}
|
|
97213
|
-
const proposals = [];
|
|
97214
|
-
const outcomeCache = /* @__PURE__ */ new Map();
|
|
97215
|
-
const resolveOutcome = async (investigationId) => {
|
|
97216
|
-
if (deps.outcomeFor === void 0) return null;
|
|
97217
|
-
if (outcomeCache.has(investigationId)) return outcomeCache.get(investigationId) ?? null;
|
|
97218
|
-
let v4 = null;
|
|
97219
|
-
try {
|
|
97220
|
-
v4 = await deps.outcomeFor(investigationId);
|
|
97221
|
-
} catch {
|
|
97222
|
-
v4 = null;
|
|
97223
|
-
}
|
|
97224
|
-
outcomeCache.set(investigationId, v4);
|
|
97225
|
-
return v4;
|
|
97226
|
-
};
|
|
97227
|
-
for (let i3 = 0; i3 < candidates.length; i3++) {
|
|
97228
|
-
const a2 = candidates[i3];
|
|
97229
|
-
for (let j3 = i3 + 1; j3 < candidates.length; j3++) {
|
|
97230
|
-
const b5 = candidates[j3];
|
|
97231
|
-
if (!existing.has(edgeKey("recurs-with", a2.id, b5.id))) {
|
|
97232
|
-
const reason = recurrenceReason(a2, b5);
|
|
97233
|
-
if (reason !== null) {
|
|
97234
|
-
const [lo, hi] = a2.id <= b5.id ? [a2.id, b5.id] : [b5.id, a2.id];
|
|
97235
|
-
proposals.push({
|
|
97236
|
-
id: recurrenceEdgeId(a2.id, b5.id),
|
|
97237
|
-
fromMemoryId: lo,
|
|
97238
|
-
toMemoryId: hi,
|
|
97239
|
-
rel: "recurs-with",
|
|
97240
|
-
detection: "auto:recurrence",
|
|
97241
|
-
reason
|
|
97242
|
-
});
|
|
97243
|
-
}
|
|
97244
|
-
}
|
|
97245
|
-
if (deps.outcomeFor !== void 0 && a2.kind === "confirmed-outcome" && b5.kind === "confirmed-outcome" && !existing.has(edgeKey("contradicts", a2.id, b5.id))) {
|
|
97246
|
-
const sigA = usableSignature(a2.signature);
|
|
97247
|
-
const sigB = usableSignature(b5.signature);
|
|
97248
|
-
const invA = investigationOf.get(a2.id);
|
|
97249
|
-
const invB = investigationOf.get(b5.id);
|
|
97250
|
-
if (sigA !== null && sigA === sigB && invA !== void 0 && invB !== void 0) {
|
|
97251
|
-
const oa = await resolveOutcome(invA);
|
|
97252
|
-
const ob = await resolveOutcome(invB);
|
|
97253
|
-
if (oa !== null && ob !== null) {
|
|
97254
|
-
const reason = outcomeConflict(oa, ob);
|
|
97255
|
-
if (reason !== null) {
|
|
97256
|
-
const [lo, hi] = a2.id <= b5.id ? [a2.id, b5.id] : [b5.id, a2.id];
|
|
97257
|
-
const invLo = lo === a2.id ? invA : invB;
|
|
97258
|
-
const invHi = lo === a2.id ? invB : invA;
|
|
97259
|
-
proposals.push({
|
|
97260
|
-
id: "",
|
|
97261
|
-
// directional — the store mints the id
|
|
97262
|
-
fromMemoryId: lo,
|
|
97263
|
-
toMemoryId: hi,
|
|
97264
|
-
rel: "contradicts",
|
|
97265
|
-
detection: "auto:contradiction",
|
|
97266
|
-
reason,
|
|
97267
|
-
detail: { investigationIds: [invLo, invHi] }
|
|
97268
|
-
});
|
|
97269
|
-
}
|
|
97270
|
-
}
|
|
97271
|
-
}
|
|
97272
|
-
}
|
|
97273
|
-
}
|
|
97274
|
-
}
|
|
97275
|
-
return proposals;
|
|
97276
|
-
}
|
|
97277
|
-
|
|
97278
97532
|
// ../../packages/engine/src/memory-recall.ts
|
|
97279
97533
|
init_cjs_shims();
|
|
97280
97534
|
var import_node_crypto4 = require("crypto");
|
|
@@ -101915,8 +102169,13 @@ async function runIndex(opts) {
|
|
|
101915
102169
|
console.error(import_picocolors6.default.red(` ${err2.message}`));
|
|
101916
102170
|
return 1;
|
|
101917
102171
|
}
|
|
101918
|
-
|
|
101919
|
-
|
|
102172
|
+
const staleReason = isAnalyzed(root) ? indexNeedsReanalyze(root) : null;
|
|
102173
|
+
if (!isAnalyzed(root) || staleReason) {
|
|
102174
|
+
console.log(
|
|
102175
|
+
import_picocolors6.default.dim(
|
|
102176
|
+
staleReason ? ` existing index is stale (${staleReason}) \u2014 re-analyzing with source-intelligence backend\u2026` : " analyzing with source-intelligence backend (first time \u2014 this can take a while)\u2026"
|
|
102177
|
+
)
|
|
102178
|
+
);
|
|
101920
102179
|
try {
|
|
101921
102180
|
await analyzeRepo(root);
|
|
101922
102181
|
} catch (err2) {
|
|
@@ -115375,6 +115634,9 @@ async function runOneInvestigation(input, ctx, opts = {}) {
|
|
|
115375
115634
|
{
|
|
115376
115635
|
code: ctx.code,
|
|
115377
115636
|
db: ctx.dbHandle.db,
|
|
115637
|
+
// HOR-432 — the authored-memory store so every investigation auto-captures a recurrence-aware
|
|
115638
|
+
// memory. Best-effort + CONTEXT-ONLY in the engine (never feeds scoring, never blocks delivery).
|
|
115639
|
+
store: createLocalMemoryStore(ctx.dbHandle.db),
|
|
115378
115640
|
logs: ctx.logs,
|
|
115379
115641
|
mongo: ctx.mongo,
|
|
115380
115642
|
postgres: ctx.postgres,
|
|
@@ -117432,6 +117694,39 @@ function createCloudMemoryStore(client, cfg) {
|
|
|
117432
117694
|
tags: null
|
|
117433
117695
|
};
|
|
117434
117696
|
},
|
|
117697
|
+
// HOR-432 consolidation mirror: refresh the scalar fields the cloud accepts (claim/confidence).
|
|
117698
|
+
// PRIVACY: `payload` NEVER leaves the device (it can stash a vector) — it is intentionally dropped
|
|
117699
|
+
// here, exactly like `add`/`toSyncInput`. The return is the server echo (or a synthesized stub) and
|
|
117700
|
+
// is only consumed by the dual-write wrapper, which discards it; reads always come from local.
|
|
117701
|
+
async update(id, patch, _opts) {
|
|
117702
|
+
const input = { clientId: id };
|
|
117703
|
+
if (patch.claim !== void 0) input.claim = patch.claim;
|
|
117704
|
+
if (patch.confidence !== void 0) input.confidence = patch.confidence;
|
|
117705
|
+
const res = await client.syncMemoryItems(projectId, { items: [input] });
|
|
117706
|
+
const record2 = res.items?.find((r) => r.clientId === id);
|
|
117707
|
+
if (record2) return fromWire(record2, repoFallback);
|
|
117708
|
+
return {
|
|
117709
|
+
id,
|
|
117710
|
+
kind: "investigation",
|
|
117711
|
+
claim: patch.claim ?? "",
|
|
117712
|
+
scope: "repo",
|
|
117713
|
+
source: "investigation",
|
|
117714
|
+
evidence: [],
|
|
117715
|
+
confidence: patch.confidence ?? 0,
|
|
117716
|
+
status: "fresh",
|
|
117717
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
117718
|
+
lastVerifiedAt: null,
|
|
117719
|
+
lastVerifiedHash: null,
|
|
117720
|
+
orgId: null,
|
|
117721
|
+
workspaceId: null,
|
|
117722
|
+
repo: repoFallback,
|
|
117723
|
+
userId: null,
|
|
117724
|
+
visibility: "private",
|
|
117725
|
+
payload: null,
|
|
117726
|
+
signature: null,
|
|
117727
|
+
tags: null
|
|
117728
|
+
};
|
|
117729
|
+
},
|
|
117435
117730
|
async get(id) {
|
|
117436
117731
|
try {
|
|
117437
117732
|
return await findInList(id);
|
|
@@ -117539,6 +117834,14 @@ function dualWriteMemoryStore(local, cloud, onCloudError = () => {
|
|
|
117539
117834
|
});
|
|
117540
117835
|
return row;
|
|
117541
117836
|
},
|
|
117837
|
+
async update(id, patch, opts) {
|
|
117838
|
+
const row = await local.update(id, patch, opts);
|
|
117839
|
+
await mirror(async () => {
|
|
117840
|
+
await cloud.update(id, patch, opts);
|
|
117841
|
+
await mirrorLatestAudit(id);
|
|
117842
|
+
});
|
|
117843
|
+
return row;
|
|
117844
|
+
},
|
|
117542
117845
|
async setStatus(id, status, audit) {
|
|
117543
117846
|
await local.setStatus(id, status, audit);
|
|
117544
117847
|
await mirror(async () => {
|