@hivelore/core 0.39.2 → 0.43.2
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.d.ts +213 -61
- package/dist/index.js +311 -43
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -28,9 +28,18 @@ var AnchorSchema = z.object({
|
|
|
28
28
|
symbols: z.array(z.string()).default([])
|
|
29
29
|
});
|
|
30
30
|
var SensorSchema = z.object({
|
|
31
|
-
kind: z.enum(["regex", "shell", "test"]).default("regex"),
|
|
32
|
-
/**
|
|
31
|
+
kind: z.enum(["regex", "ast", "shell", "test"]).default("regex"),
|
|
32
|
+
/**
|
|
33
|
+
* kind=regex: regex source, matched against added diff lines / file content.
|
|
34
|
+
* kind=ast: an ast-grep structural pattern (e.g. `stripe.paymentIntents.create($$$)`) — matched
|
|
35
|
+
* on the AST of changed files, so comments and string literals can never false-positive. Requires
|
|
36
|
+
* the optional `@ast-grep/napi` engine; without it the sensor is unrunnable (warn, never block).
|
|
37
|
+
*/
|
|
33
38
|
pattern: z.string().optional(),
|
|
39
|
+
/** kind=ast: full ast-grep Rule object (`kind`, `inside`, `has`, `not`, `all`, `any`, …). */
|
|
40
|
+
rule: z.record(z.unknown()).optional(),
|
|
41
|
+
/** kind=ast: explicit language name for extensionless/non-standard files (e.g. python, go). */
|
|
42
|
+
language: z.string().optional(),
|
|
34
43
|
/**
|
|
35
44
|
* Optional "correct-usage" regex (kind=regex). When `pattern` (the risky call) matches but this
|
|
36
45
|
* regex ALSO appears within a small window around the match, the catch is SUPPRESSED — the diff
|
|
@@ -56,6 +65,13 @@ var SensorSchema = z.object({
|
|
|
56
65
|
* the incident the test exists to prevent". Surfaced in the block message and the prevention receipt.
|
|
57
66
|
*/
|
|
58
67
|
incident: z.string().optional(),
|
|
68
|
+
/**
|
|
69
|
+
* kind=shell|test only: the oracle was PROVEN to fail (RED) on the incident state at arming time
|
|
70
|
+
* (`sensors propose --red-ref`): the command passed on the presumed-correct tree AND failed on
|
|
71
|
+
* the replayed incident tree. Distinguishes "a test is routed" from "the test demonstrably
|
|
72
|
+
* catches the incident". Surfaced in the prevention receipt.
|
|
73
|
+
*/
|
|
74
|
+
red_proven: z.boolean().optional(),
|
|
59
75
|
/** `warn` surfaces in review; `block` can hard-block the commit (only when the gate opts in). */
|
|
60
76
|
severity: z.enum(["warn", "block"]).default("warn"),
|
|
61
77
|
/** True when Hivelore generated this sensor automatically (vs. hand-authored). */
|
|
@@ -951,10 +967,11 @@ function buildPreventionReceipt(events, memories, usage, options) {
|
|
|
951
967
|
stage: event.stage ?? null,
|
|
952
968
|
exit_code: event.exit_code ?? null,
|
|
953
969
|
message: sensor?.message ?? null,
|
|
954
|
-
incident: sensor?.incident ?? null
|
|
970
|
+
incident: sensor?.incident ?? null,
|
|
971
|
+
red_proven: sensor?.red_proven === true
|
|
955
972
|
};
|
|
956
973
|
}).sort((a, b) => b.at.localeCompare(a.at));
|
|
957
|
-
const preventedCountTotal = Object.values(usage.by_id).reduce((sum, item) => sum + item.prevented_count, 0);
|
|
974
|
+
const preventedCountTotal = Object.values(usage.by_id).reduce((sum, item) => sum + (item.prevented_count ?? 0), 0);
|
|
958
975
|
return {
|
|
959
976
|
generated_at: now.toISOString(),
|
|
960
977
|
since: options.since.toISOString(),
|
|
@@ -976,7 +993,8 @@ function renderPreventionReceipt(receipt) {
|
|
|
976
993
|
const exit = row.exit_code === null ? "" : `, exit ${row.exit_code}`;
|
|
977
994
|
const stage = row.stage ? ` \u2014 caught at ${row.stage}` : "";
|
|
978
995
|
const incident = row.incident ? ` \u21A9 incident: ${row.incident}` : "";
|
|
979
|
-
|
|
996
|
+
const red = row.red_proven ? " \u2713 RED-proven" : "";
|
|
997
|
+
lines.push(` \u2717\u2192\u2713 ${row.at.slice(0, 10)} ${row.id.padEnd(32)} (${kind}${exit}${stage})${incident}${red}`);
|
|
980
998
|
}
|
|
981
999
|
lines.push(
|
|
982
1000
|
` Trend: ${receipt.total} this window vs ${receipt.previous_total} previous window (${receipt.total <= receipt.previous_total ? "recurrences declining" : "recurrences rising"}).`
|
|
@@ -1175,6 +1193,43 @@ async function recordProjectContextEmission(paths, hash, now = Date.now()) {
|
|
|
1175
1193
|
});
|
|
1176
1194
|
}
|
|
1177
1195
|
|
|
1196
|
+
// src/priority.ts
|
|
1197
|
+
var DEFAULT_PRIORITY_SIGNALS = {
|
|
1198
|
+
type: "",
|
|
1199
|
+
tags: [],
|
|
1200
|
+
requiresHumanApproval: false,
|
|
1201
|
+
directAnchor: false,
|
|
1202
|
+
directSymbol: false,
|
|
1203
|
+
exactTaskMatch: false,
|
|
1204
|
+
strongSemantic: false,
|
|
1205
|
+
usefulSemantic: false,
|
|
1206
|
+
moduleOrDomainMatch: false,
|
|
1207
|
+
tagTaskMatch: false
|
|
1208
|
+
};
|
|
1209
|
+
function prioritySignals(partial) {
|
|
1210
|
+
return { ...DEFAULT_PRIORITY_SIGNALS, ...partial };
|
|
1211
|
+
}
|
|
1212
|
+
function classifyMemoryPriority(signals) {
|
|
1213
|
+
const isNegative = signals.type === "attempt";
|
|
1214
|
+
const isSkill2 = signals.type === "skill";
|
|
1215
|
+
if (signals.requiresHumanApproval || signals.directAnchor || signals.directSymbol || isNegative && (signals.exactTaskMatch || signals.strongSemantic) || isSkill2 && (signals.exactTaskMatch || signals.strongSemantic)) {
|
|
1216
|
+
return "must_read";
|
|
1217
|
+
}
|
|
1218
|
+
if (isStackPackSeed({ tags: signals.tags }) || isEnvWorkaroundMemory({ tags: signals.tags })) {
|
|
1219
|
+
if (isStackPackSeed({ tags: signals.tags }) && (signals.exactTaskMatch || signals.strongSemantic)) {
|
|
1220
|
+
return "useful";
|
|
1221
|
+
}
|
|
1222
|
+
return "background";
|
|
1223
|
+
}
|
|
1224
|
+
if (isSkill2 || signals.moduleOrDomainMatch || signals.exactTaskMatch || signals.usefulSemantic || signals.tagTaskMatch) {
|
|
1225
|
+
return "useful";
|
|
1226
|
+
}
|
|
1227
|
+
return "background";
|
|
1228
|
+
}
|
|
1229
|
+
function priorityRank(priority) {
|
|
1230
|
+
return priority === "must_read" ? 3 : priority === "useful" ? 2 : 1;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1178
1233
|
// src/eval.ts
|
|
1179
1234
|
function round32(n) {
|
|
1180
1235
|
return Math.round(n * 1e3) / 1e3;
|
|
@@ -1302,6 +1357,69 @@ function synthesizeSelfEvalCases(memories, options = {}) {
|
|
|
1302
1357
|
}
|
|
1303
1358
|
return cases;
|
|
1304
1359
|
}
|
|
1360
|
+
function appendProposedRetrievalCases(specRaw, cases) {
|
|
1361
|
+
let spec = {};
|
|
1362
|
+
if (specRaw?.trim()) {
|
|
1363
|
+
try {
|
|
1364
|
+
spec = JSON.parse(specRaw);
|
|
1365
|
+
} catch {
|
|
1366
|
+
spec = {};
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
const existingNames = /* @__PURE__ */ new Set([
|
|
1370
|
+
...(spec.retrieval ?? []).map((c) => c.name),
|
|
1371
|
+
...(spec.proposed_retrieval ?? []).map((c) => c.name)
|
|
1372
|
+
]);
|
|
1373
|
+
const fresh = cases.filter((c) => !existingNames.has(c.name));
|
|
1374
|
+
if (fresh.length > 0) spec.proposed_retrieval = [...spec.proposed_retrieval ?? [], ...fresh];
|
|
1375
|
+
return JSON.stringify(spec, null, 2) + "\n";
|
|
1376
|
+
}
|
|
1377
|
+
function approveProposedCases(specRaw) {
|
|
1378
|
+
let spec;
|
|
1379
|
+
try {
|
|
1380
|
+
spec = JSON.parse(specRaw);
|
|
1381
|
+
} catch {
|
|
1382
|
+
return { raw: specRaw, approved: 0 };
|
|
1383
|
+
}
|
|
1384
|
+
const proposed = spec.proposed_retrieval ?? [];
|
|
1385
|
+
if (proposed.length === 0) return { raw: specRaw, approved: 0 };
|
|
1386
|
+
spec.retrieval = [...spec.retrieval ?? [], ...proposed];
|
|
1387
|
+
delete spec.proposed_retrieval;
|
|
1388
|
+
return { raw: JSON.stringify(spec, null, 2) + "\n", approved: proposed.length };
|
|
1389
|
+
}
|
|
1390
|
+
function runTierContract() {
|
|
1391
|
+
const cases = [
|
|
1392
|
+
{
|
|
1393
|
+
name: "stack-pack seed + strong task evidence \u2192 useful (rescue path stays alive)",
|
|
1394
|
+
expected: "useful",
|
|
1395
|
+
signals: prioritySignals({ type: "convention", tags: ["stack-pack"], strongSemantic: true, usefulSemantic: true })
|
|
1396
|
+
},
|
|
1397
|
+
{
|
|
1398
|
+
name: "stack-pack seed + weak evidence \u2192 background (crowding guard stays)",
|
|
1399
|
+
expected: "background",
|
|
1400
|
+
signals: prioritySignals({ type: "convention", tags: ["stack-pack"], usefulSemantic: true, tagTaskMatch: true })
|
|
1401
|
+
},
|
|
1402
|
+
{
|
|
1403
|
+
name: "env workaround + strong evidence \u2192 background (hard cap stays)",
|
|
1404
|
+
expected: "background",
|
|
1405
|
+
signals: prioritySignals({ type: "gotcha", tags: ["dev-env"], strongSemantic: true, exactTaskMatch: true })
|
|
1406
|
+
},
|
|
1407
|
+
{
|
|
1408
|
+
name: "direct anchor \u2192 must_read (anchors always win)",
|
|
1409
|
+
expected: "must_read",
|
|
1410
|
+
signals: prioritySignals({ type: "convention", directAnchor: true })
|
|
1411
|
+
},
|
|
1412
|
+
{
|
|
1413
|
+
name: "attempt + exact task match \u2192 must_read (negative knowledge first)",
|
|
1414
|
+
expected: "must_read",
|
|
1415
|
+
signals: prioritySignals({ type: "attempt", exactTaskMatch: true })
|
|
1416
|
+
}
|
|
1417
|
+
];
|
|
1418
|
+
return cases.map(({ name, expected, signals }) => {
|
|
1419
|
+
const actual = classifyMemoryPriority(signals);
|
|
1420
|
+
return { name, expected, actual, pass: actual === expected };
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1305
1423
|
|
|
1306
1424
|
// src/confidence.ts
|
|
1307
1425
|
var DEFAULT_CONFIDENCE_THRESHOLDS = {
|
|
@@ -2445,6 +2563,8 @@ var DEFAULT_CONFIG = {
|
|
|
2445
2563
|
antiPatternGate: "anchored",
|
|
2446
2564
|
bootstrapGate: "block",
|
|
2447
2565
|
humanCommits: "relaxed",
|
|
2566
|
+
commandSensorUnrunnable: "warn",
|
|
2567
|
+
sensorWeakeningGate: "warn",
|
|
2448
2568
|
scoreThreshold: 80,
|
|
2449
2569
|
cleanupGeneratedArtifacts: true,
|
|
2450
2570
|
toolProfile: "enforcement",
|
|
@@ -2479,6 +2599,8 @@ var AUTOPILOT_DEFAULTS = {
|
|
|
2479
2599
|
antiPatternGate: "anchored",
|
|
2480
2600
|
bootstrapGate: "block",
|
|
2481
2601
|
humanCommits: "relaxed",
|
|
2602
|
+
commandSensorUnrunnable: "warn",
|
|
2603
|
+
sensorWeakeningGate: "warn",
|
|
2482
2604
|
scoreThreshold: 85,
|
|
2483
2605
|
cleanupGeneratedArtifacts: true,
|
|
2484
2606
|
toolProfile: "enforcement",
|
|
@@ -3958,6 +4080,34 @@ function runSensors(memories, targets) {
|
|
|
3958
4080
|
}
|
|
3959
4081
|
return hits;
|
|
3960
4082
|
}
|
|
4083
|
+
var COMMAND_ENV_EXACT = /* @__PURE__ */ new Set([
|
|
4084
|
+
"PATH",
|
|
4085
|
+
"HOME",
|
|
4086
|
+
"LANG",
|
|
4087
|
+
"LANGUAGE",
|
|
4088
|
+
"TMPDIR",
|
|
4089
|
+
"TMP",
|
|
4090
|
+
"TEMP",
|
|
4091
|
+
"TERM",
|
|
4092
|
+
"SHELL",
|
|
4093
|
+
"USER",
|
|
4094
|
+
"LOGNAME",
|
|
4095
|
+
"PWD",
|
|
4096
|
+
"CI",
|
|
4097
|
+
"COLORTERM",
|
|
4098
|
+
"TZ"
|
|
4099
|
+
]);
|
|
4100
|
+
var COMMAND_ENV_PREFIXES = ["LC_", "NODE_", "NVM_", "npm_", "HIVELORE_", "HAIVE_"];
|
|
4101
|
+
function scrubbedCommandEnv(env) {
|
|
4102
|
+
const out = {};
|
|
4103
|
+
for (const [key, value] of Object.entries(env)) {
|
|
4104
|
+
if (value === void 0) continue;
|
|
4105
|
+
if (COMMAND_ENV_EXACT.has(key) || COMMAND_ENV_PREFIXES.some((p) => key.startsWith(p))) {
|
|
4106
|
+
out[key] = value;
|
|
4107
|
+
}
|
|
4108
|
+
}
|
|
4109
|
+
return out;
|
|
4110
|
+
}
|
|
3961
4111
|
function incidentSuffix(incident) {
|
|
3962
4112
|
const ref = incident?.trim();
|
|
3963
4113
|
return ref ? ` \u21A9 guards incident: ${ref}` : "";
|
|
@@ -3986,6 +4136,34 @@ function selectCommandSensors(memories, changedPaths) {
|
|
|
3986
4136
|
}
|
|
3987
4137
|
return specs;
|
|
3988
4138
|
}
|
|
4139
|
+
function addedLineNumbersFromDiff(diff) {
|
|
4140
|
+
const result = /* @__PURE__ */ new Map();
|
|
4141
|
+
let currentPath = null;
|
|
4142
|
+
let newLine = 0;
|
|
4143
|
+
for (const line of diff.split("\n")) {
|
|
4144
|
+
if (line.startsWith("+++ ")) {
|
|
4145
|
+
const raw = line.slice(4).trim();
|
|
4146
|
+
currentPath = raw === "/dev/null" ? null : normalizeProjectPath(raw);
|
|
4147
|
+
continue;
|
|
4148
|
+
}
|
|
4149
|
+
const hunk = line.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
|
|
4150
|
+
if (hunk) {
|
|
4151
|
+
newLine = Number(hunk[1]);
|
|
4152
|
+
continue;
|
|
4153
|
+
}
|
|
4154
|
+
if (!currentPath) continue;
|
|
4155
|
+
if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
4156
|
+
let set = result.get(currentPath);
|
|
4157
|
+
if (!set) result.set(currentPath, set = /* @__PURE__ */ new Set());
|
|
4158
|
+
set.add(newLine);
|
|
4159
|
+
newLine++;
|
|
4160
|
+
} else if (line.startsWith("-") && !line.startsWith("---")) {
|
|
4161
|
+
} else if (!line.startsWith("\\")) {
|
|
4162
|
+
newLine++;
|
|
4163
|
+
}
|
|
4164
|
+
}
|
|
4165
|
+
return result;
|
|
4166
|
+
}
|
|
3989
4167
|
function sensorTargetsFromDiff(diff) {
|
|
3990
4168
|
const targets = [];
|
|
3991
4169
|
let currentPath = null;
|
|
@@ -4050,6 +4228,7 @@ function diffFileChanges(diff) {
|
|
|
4050
4228
|
const newPath = raw === "/dev/null" ? null : normalizeProjectPath(raw);
|
|
4051
4229
|
current = {
|
|
4052
4230
|
path: newPath ?? oldPath ?? "",
|
|
4231
|
+
created: oldPath === null && newPath !== null,
|
|
4053
4232
|
deleted: newPath === null,
|
|
4054
4233
|
removed: [],
|
|
4055
4234
|
added: []
|
|
@@ -4077,6 +4256,7 @@ function detectSensorWeakening(diff) {
|
|
|
4077
4256
|
weakenings.push({ file: file.path, memory_id: memoryId, change, detail });
|
|
4078
4257
|
};
|
|
4079
4258
|
const removedBlockSeverity = file.removed.some((l) => SEVERITY_BLOCK_RE.test(l));
|
|
4259
|
+
if (file.created) continue;
|
|
4080
4260
|
if (file.deleted) {
|
|
4081
4261
|
if (file.removed.some((l) => SENSOR_BLOCK_START_RE.test(l)) && removedBlockSeverity) {
|
|
4082
4262
|
flag("memory-deleted", "memory file with a block sensor deleted");
|
|
@@ -4099,7 +4279,10 @@ function detectSensorWeakening(diff) {
|
|
|
4099
4279
|
}
|
|
4100
4280
|
const removedAbsent = file.removed.find((l) => SENSOR_ABSENT_KEY_RE.test(l));
|
|
4101
4281
|
const addedAbsent = file.added.find((l) => SENSOR_ABSENT_KEY_RE.test(l));
|
|
4102
|
-
|
|
4282
|
+
const addingNewSensorBlock = file.added.some((l) => SENSOR_BLOCK_START_RE.test(l)) && !file.removed.some(
|
|
4283
|
+
(l) => SENSOR_BLOCK_START_RE.test(l) || SENSOR_ORACLE_KEY_RE.test(l) || SEVERITY_BLOCK_RE.test(l) || SEVERITY_WARN_RE.test(l)
|
|
4284
|
+
);
|
|
4285
|
+
if (!addingNewSensorBlock && addedAbsent !== void 0 && addedAbsent.trim() !== removedAbsent?.trim()) {
|
|
4103
4286
|
flag("suppression-broadened", removedAbsent === void 0 ? "absent marker added" : "absent marker changed");
|
|
4104
4287
|
}
|
|
4105
4288
|
if (file.removed.some((l) => SENSOR_BLOCK_START_RE.test(l)) && removedBlockSeverity && !file.added.some((l) => SENSOR_BLOCK_START_RE.test(l))) {
|
|
@@ -5407,6 +5590,33 @@ function findUncapturedFailures(failures, captureTimes, options = {}) {
|
|
|
5407
5590
|
out.sort((a, b) => a.ts.localeCompare(b.ts));
|
|
5408
5591
|
return out;
|
|
5409
5592
|
}
|
|
5593
|
+
var EXPLORATORY_RE = /^(ls|find|grep|rg|cat|head|tail|which|stat)\b/i;
|
|
5594
|
+
function distillFailureObservations(failures, options = {}) {
|
|
5595
|
+
const max = options.max ?? 3;
|
|
5596
|
+
const clusters = /* @__PURE__ */ new Map();
|
|
5597
|
+
for (const f of failures) {
|
|
5598
|
+
const summary = f.summary.trim();
|
|
5599
|
+
if (!summary || EXPLORATORY_RE.test(summary.replace(/^Bash:\s*/i, ""))) continue;
|
|
5600
|
+
const key = normalizeSummary(summary);
|
|
5601
|
+
const existing = clusters.get(key);
|
|
5602
|
+
if (existing) {
|
|
5603
|
+
existing.count++;
|
|
5604
|
+
for (const file of f.files ?? []) existing.files.add(file);
|
|
5605
|
+
} else {
|
|
5606
|
+
clusters.set(key, { first: f, count: 1, files: new Set(f.files ?? []) });
|
|
5607
|
+
}
|
|
5608
|
+
}
|
|
5609
|
+
return [...clusters.values()].sort((a, b) => b.count - a.count || b.first.ts.localeCompare(a.first.ts)).slice(0, max).map(({ first, count, files }) => {
|
|
5610
|
+
const summary = first.summary.trim();
|
|
5611
|
+
const firstLine = summary.split("\n")[0].slice(0, 120);
|
|
5612
|
+
return {
|
|
5613
|
+
what: `${first.tool} failed: ${firstLine}`,
|
|
5614
|
+
why_failed: summary.slice(0, 400),
|
|
5615
|
+
paths: [...files].slice(0, 6),
|
|
5616
|
+
occurrences: count
|
|
5617
|
+
};
|
|
5618
|
+
});
|
|
5619
|
+
}
|
|
5410
5620
|
|
|
5411
5621
|
// src/coverage.ts
|
|
5412
5622
|
var DEFAULT_COVERING_TYPES = ["decision", "convention", "gotcha", "architecture"];
|
|
@@ -5906,43 +6116,6 @@ async function handoffAgeMs(root, now = /* @__PURE__ */ new Date()) {
|
|
|
5906
6116
|
}
|
|
5907
6117
|
}
|
|
5908
6118
|
|
|
5909
|
-
// src/priority.ts
|
|
5910
|
-
var DEFAULT_PRIORITY_SIGNALS = {
|
|
5911
|
-
type: "",
|
|
5912
|
-
tags: [],
|
|
5913
|
-
requiresHumanApproval: false,
|
|
5914
|
-
directAnchor: false,
|
|
5915
|
-
directSymbol: false,
|
|
5916
|
-
exactTaskMatch: false,
|
|
5917
|
-
strongSemantic: false,
|
|
5918
|
-
usefulSemantic: false,
|
|
5919
|
-
moduleOrDomainMatch: false,
|
|
5920
|
-
tagTaskMatch: false
|
|
5921
|
-
};
|
|
5922
|
-
function prioritySignals(partial) {
|
|
5923
|
-
return { ...DEFAULT_PRIORITY_SIGNALS, ...partial };
|
|
5924
|
-
}
|
|
5925
|
-
function classifyMemoryPriority(signals) {
|
|
5926
|
-
const isNegative = signals.type === "attempt";
|
|
5927
|
-
const isSkill2 = signals.type === "skill";
|
|
5928
|
-
if (signals.requiresHumanApproval || signals.directAnchor || signals.directSymbol || isNegative && (signals.exactTaskMatch || signals.strongSemantic) || isSkill2 && (signals.exactTaskMatch || signals.strongSemantic)) {
|
|
5929
|
-
return "must_read";
|
|
5930
|
-
}
|
|
5931
|
-
if (isStackPackSeed({ tags: signals.tags }) || isEnvWorkaroundMemory({ tags: signals.tags })) {
|
|
5932
|
-
if (isStackPackSeed({ tags: signals.tags }) && (signals.exactTaskMatch || signals.strongSemantic)) {
|
|
5933
|
-
return "useful";
|
|
5934
|
-
}
|
|
5935
|
-
return "background";
|
|
5936
|
-
}
|
|
5937
|
-
if (isSkill2 || signals.moduleOrDomainMatch || signals.exactTaskMatch || signals.usefulSemantic || signals.tagTaskMatch) {
|
|
5938
|
-
return "useful";
|
|
5939
|
-
}
|
|
5940
|
-
return "background";
|
|
5941
|
-
}
|
|
5942
|
-
function priorityRank(priority) {
|
|
5943
|
-
return priority === "must_read" ? 3 : priority === "useful" ? 2 : 1;
|
|
5944
|
-
}
|
|
5945
|
-
|
|
5946
6119
|
// src/agent-context.ts
|
|
5947
6120
|
var AGENT_ENV_SIGNALS = [
|
|
5948
6121
|
{ name: "HAIVE_SESSION_ID", label: "hivelore-run-wrapper" },
|
|
@@ -5960,6 +6133,92 @@ function detectAgentContext(env = typeof process !== "undefined" ? process.env :
|
|
|
5960
6133
|
const signals = AGENT_ENV_SIGNALS.filter(({ name }) => (env[name] ?? "").trim().length > 0).map(({ name, label }) => `${label} (${name})`);
|
|
5961
6134
|
return { agent: signals.length > 0, signals: [...new Set(signals)] };
|
|
5962
6135
|
}
|
|
6136
|
+
|
|
6137
|
+
// src/pr-review-ingest.ts
|
|
6138
|
+
var REVIEW_LEARNING_MARKER = /(^|\s)\/?hivelore[:,]?\s+remember\b|(^|\s)hivelore:/i;
|
|
6139
|
+
var INSTRUCTION_RE = /\b(never|always|don'?t|do not|must(?: not)?|should(?: not|n'?t)?|avoid|prefer|instead of|use\s+\S+\s+instead)\b/i;
|
|
6140
|
+
var MAX_INSTRUCTION_CHARS = 500;
|
|
6141
|
+
var MIN_INSTRUCTION_CHARS = 12;
|
|
6142
|
+
function prNumberFrom(comment) {
|
|
6143
|
+
const m = comment.pull_request_url?.match(/\/pulls\/(\d+)$/);
|
|
6144
|
+
return m ? Number(m[1]) : void 0;
|
|
6145
|
+
}
|
|
6146
|
+
function extractReviewLearnings(payload) {
|
|
6147
|
+
if (!Array.isArray(payload)) return [];
|
|
6148
|
+
const comments = payload;
|
|
6149
|
+
const learnings = [];
|
|
6150
|
+
for (const comment of comments) {
|
|
6151
|
+
if (typeof comment?.id !== "number") continue;
|
|
6152
|
+
if ((comment.user?.type ?? "").toLowerCase() === "bot") continue;
|
|
6153
|
+
const body = (comment.body ?? "").trim();
|
|
6154
|
+
if (body.length < MIN_INSTRUCTION_CHARS) continue;
|
|
6155
|
+
const marked = REVIEW_LEARNING_MARKER.test(body);
|
|
6156
|
+
if (!marked && !INSTRUCTION_RE.test(body)) continue;
|
|
6157
|
+
const instruction = body.replace(REVIEW_LEARNING_MARKER, " ").replace(/\s+/g, " ").trim().slice(0, MAX_INSTRUCTION_CHARS);
|
|
6158
|
+
if (instruction.length < MIN_INSTRUCTION_CHARS) continue;
|
|
6159
|
+
learnings.push({
|
|
6160
|
+
thread_id: comment.in_reply_to_id ?? comment.id,
|
|
6161
|
+
comment_id: comment.id,
|
|
6162
|
+
...comment.path ? { path: comment.path } : {},
|
|
6163
|
+
...typeof (comment.line ?? comment.original_line) === "number" ? { line: comment.line ?? comment.original_line } : {},
|
|
6164
|
+
author: comment.user?.login ?? "reviewer",
|
|
6165
|
+
instruction,
|
|
6166
|
+
...comment.html_url ? { url: comment.html_url } : {},
|
|
6167
|
+
...prNumberFrom(comment) !== void 0 ? { pr_number: prNumberFrom(comment) } : {}
|
|
6168
|
+
});
|
|
6169
|
+
}
|
|
6170
|
+
return learnings;
|
|
6171
|
+
}
|
|
6172
|
+
function reviewLearningsToDrafts(learnings, options = {}) {
|
|
6173
|
+
const limit = options.limit ?? 20;
|
|
6174
|
+
const drafts = [];
|
|
6175
|
+
const seenThreads = /* @__PURE__ */ new Set();
|
|
6176
|
+
for (const learning of learnings) {
|
|
6177
|
+
if (drafts.length >= limit) break;
|
|
6178
|
+
if (seenThreads.has(learning.thread_id)) {
|
|
6179
|
+
const idx = drafts.findIndex((d) => d.key === `github-pr:${learning.thread_id}`);
|
|
6180
|
+
if (idx >= 0) drafts.splice(idx, 1);
|
|
6181
|
+
}
|
|
6182
|
+
seenThreads.add(learning.thread_id);
|
|
6183
|
+
const key = `github-pr:${learning.thread_id}`;
|
|
6184
|
+
const slugSource = learning.instruction.toLowerCase().replace(/[^a-z0-9\s]/g, " ");
|
|
6185
|
+
const slug = slugSource.trim().split(/\s+/).slice(0, 6).join("-") || "review-learning";
|
|
6186
|
+
const finding = {
|
|
6187
|
+
tool: "github-pr",
|
|
6188
|
+
ruleId: "review-learning",
|
|
6189
|
+
severity: "major",
|
|
6190
|
+
message: learning.instruction,
|
|
6191
|
+
path: learning.path ?? "",
|
|
6192
|
+
...learning.line !== void 0 ? { line: learning.line } : {},
|
|
6193
|
+
key
|
|
6194
|
+
};
|
|
6195
|
+
const baseFm = buildFrontmatter({
|
|
6196
|
+
type: "convention",
|
|
6197
|
+
slug,
|
|
6198
|
+
scope: options.scope ?? "team",
|
|
6199
|
+
module: options.module,
|
|
6200
|
+
tags: ["review-learning"],
|
|
6201
|
+
paths: learning.path ? [learning.path] : [],
|
|
6202
|
+
author: options.author ?? learning.author
|
|
6203
|
+
});
|
|
6204
|
+
const frontmatter = { ...baseFm, status: "proposed", topic: `ingest:${key}` };
|
|
6205
|
+
const provenance = [
|
|
6206
|
+
learning.pr_number !== void 0 ? `PR #${learning.pr_number}` : null,
|
|
6207
|
+
`@${learning.author}`,
|
|
6208
|
+
learning.url ?? null
|
|
6209
|
+
].filter(Boolean).join(" \xB7 ");
|
|
6210
|
+
const body = `# Review learning: ${learning.instruction.slice(0, 80)}
|
|
6211
|
+
|
|
6212
|
+
${learning.instruction}
|
|
6213
|
+
|
|
6214
|
+
` + (learning.path ? `Applies to: \`${learning.path}\`${learning.line !== void 0 ? ` (line ${learning.line} at review time)` : ""}
|
|
6215
|
+
|
|
6216
|
+
` : "") + `_From a review thread (${provenance}). Review: approve, refine, or reject \u2014 then consider \`hivelore sensors propose\` to make it a deterministic gate._
|
|
6217
|
+
`;
|
|
6218
|
+
drafts.push({ key, topic: `ingest:${key}`, frontmatter, body, finding, has_sensor: false });
|
|
6219
|
+
}
|
|
6220
|
+
return drafts;
|
|
6221
|
+
}
|
|
5963
6222
|
export {
|
|
5964
6223
|
AUTOPILOT_DEFAULTS,
|
|
5965
6224
|
ActivationSchema,
|
|
@@ -5998,6 +6257,7 @@ export {
|
|
|
5998
6257
|
PREVENTION_DEBOUNCE_MS,
|
|
5999
6258
|
PROJECT_CONTEXT_FILE,
|
|
6000
6259
|
PROJECT_CONTEXT_THROTTLE_MS,
|
|
6260
|
+
REVIEW_LEARNING_MARKER,
|
|
6001
6261
|
RUNTIME_JOURNAL_FILENAME,
|
|
6002
6262
|
SCAFFOLD_MARKER_RE,
|
|
6003
6263
|
SEED_QUALITY_FLOOR,
|
|
@@ -6010,6 +6270,7 @@ export {
|
|
|
6010
6270
|
USAGE_FILE,
|
|
6011
6271
|
USAGE_LOG_DIR,
|
|
6012
6272
|
USAGE_LOG_FILE,
|
|
6273
|
+
addedLineNumbersFromDiff,
|
|
6013
6274
|
addedLinesFromDiff,
|
|
6014
6275
|
aggregateRetrieval,
|
|
6015
6276
|
aggregateSensors,
|
|
@@ -6018,11 +6279,13 @@ export {
|
|
|
6018
6279
|
antiPatternGateParams,
|
|
6019
6280
|
appendEvalHistory,
|
|
6020
6281
|
appendPreventionEvent,
|
|
6282
|
+
appendProposedRetrievalCases,
|
|
6021
6283
|
appendRuntimeJournalEntry,
|
|
6022
6284
|
appendSensorEvaluations,
|
|
6023
6285
|
appendUsageEvent,
|
|
6024
6286
|
applyConflictResolution,
|
|
6025
6287
|
applyFeedbackAdjustment,
|
|
6288
|
+
approveProposedCases,
|
|
6026
6289
|
assessBootstrapState,
|
|
6027
6290
|
assessScaffoldLoop,
|
|
6028
6291
|
assessSensorHealth,
|
|
@@ -6064,6 +6327,7 @@ export {
|
|
|
6064
6327
|
detectStacksFromManifests,
|
|
6065
6328
|
diffContract,
|
|
6066
6329
|
diffHasDistinctiveOverlap,
|
|
6330
|
+
distillFailureObservations,
|
|
6067
6331
|
distinctiveCap,
|
|
6068
6332
|
draftsFromFindings,
|
|
6069
6333
|
emptyUsage,
|
|
@@ -6075,6 +6339,7 @@ export {
|
|
|
6075
6339
|
existingGateMissShas,
|
|
6076
6340
|
extractActionsBriefBody,
|
|
6077
6341
|
extractReferencedPaths,
|
|
6342
|
+
extractReviewLearnings,
|
|
6078
6343
|
extractSensorExamples,
|
|
6079
6344
|
extractSnippet,
|
|
6080
6345
|
extractTestFilePathsFromCommand,
|
|
@@ -6189,8 +6454,10 @@ export {
|
|
|
6189
6454
|
resolveProjectInfo,
|
|
6190
6455
|
retirementSignal,
|
|
6191
6456
|
revertedShaFromCommit,
|
|
6457
|
+
reviewLearningsToDrafts,
|
|
6192
6458
|
runRegexSensor,
|
|
6193
6459
|
runSensors,
|
|
6460
|
+
runTierContract,
|
|
6194
6461
|
runtimeJournalPath,
|
|
6195
6462
|
saveCodeMap,
|
|
6196
6463
|
saveConfig,
|
|
@@ -6199,6 +6466,7 @@ export {
|
|
|
6199
6466
|
scannableSensorTargets,
|
|
6200
6467
|
scoreRetrievalCase,
|
|
6201
6468
|
scoreSensorCase,
|
|
6469
|
+
scrubbedCommandEnv,
|
|
6202
6470
|
selectCommandSensors,
|
|
6203
6471
|
sensorAppliesToPath,
|
|
6204
6472
|
sensorLedgerPath,
|