@hivelore/core 0.53.2 → 0.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +133 -1
- package/dist/index.js +264 -77
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1394,10 +1394,10 @@ function sensorPatternBrittleness(pattern) {
|
|
|
1394
1394
|
function normalizeProjectPath(value) {
|
|
1395
1395
|
return value.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^[ab]\//, "").replace(/\/+$/g, "");
|
|
1396
1396
|
}
|
|
1397
|
-
function sensorAppliesToPath(sensor, anchorPaths,
|
|
1397
|
+
function sensorAppliesToPath(sensor, anchorPaths, path21) {
|
|
1398
1398
|
const scopes = sensor.paths.length > 0 ? sensor.paths : anchorPaths;
|
|
1399
1399
|
if (scopes.length === 0) return true;
|
|
1400
|
-
const target = normalizeProjectPath(
|
|
1400
|
+
const target = normalizeProjectPath(path21);
|
|
1401
1401
|
return scopes.some((rawScope) => {
|
|
1402
1402
|
const scope = normalizeProjectPath(rawScope);
|
|
1403
1403
|
if (!scope) return false;
|
|
@@ -4241,6 +4241,178 @@ async function usageLogSize(paths) {
|
|
|
4241
4241
|
return { exists: true, size_bytes: st.size, lines: raw.split("\n").filter((l) => l).length };
|
|
4242
4242
|
}
|
|
4243
4243
|
|
|
4244
|
+
// src/friction.ts
|
|
4245
|
+
import { appendFile as appendFile3, mkdir as mkdir9, readFile as readFile12, writeFile as writeFile8 } from "fs/promises";
|
|
4246
|
+
import { existsSync as existsSync12 } from "fs";
|
|
4247
|
+
import { createHash as createHash2 } from "crypto";
|
|
4248
|
+
import path14 from "path";
|
|
4249
|
+
var FRICTION_LOG_FILE = "friction.jsonl";
|
|
4250
|
+
var FRICTION_STATE_FILE = "friction-state.json";
|
|
4251
|
+
var FRICTION_FIELD_MAX = 2e3;
|
|
4252
|
+
function frictionLogPath(paths) {
|
|
4253
|
+
return path14.join(paths.runtimeDir, FRICTION_LOG_FILE);
|
|
4254
|
+
}
|
|
4255
|
+
function frictionStatePath(paths) {
|
|
4256
|
+
return path14.join(paths.runtimeDir, FRICTION_STATE_FILE);
|
|
4257
|
+
}
|
|
4258
|
+
function normalizeFrictionSummary(value) {
|
|
4259
|
+
return value.toLowerCase().replace(/[a-z]?:?[\\/](?:[\w.-]+[\\/])+/g, "/").replace(/\d{3,}/g, "N").replace(/\s+/g, " ").trim();
|
|
4260
|
+
}
|
|
4261
|
+
function frictionFingerprint(input) {
|
|
4262
|
+
const basis = [
|
|
4263
|
+
input.kind,
|
|
4264
|
+
input.surface.trim().toLowerCase(),
|
|
4265
|
+
normalizeFrictionSummary(input.summary)
|
|
4266
|
+
].join("|");
|
|
4267
|
+
return createHash2("sha256").update(basis).digest("hex").slice(0, 16);
|
|
4268
|
+
}
|
|
4269
|
+
function truncate(value) {
|
|
4270
|
+
if (value === void 0) return void 0;
|
|
4271
|
+
const trimmed = value.trim();
|
|
4272
|
+
if (!trimmed) return void 0;
|
|
4273
|
+
return trimmed.length > FRICTION_FIELD_MAX ? `${trimmed.slice(0, FRICTION_FIELD_MAX)}
|
|
4274
|
+
\u2026[truncated]` : trimmed;
|
|
4275
|
+
}
|
|
4276
|
+
function normalizeKind(kind, repro) {
|
|
4277
|
+
if (kind === "bug" && !truncate(repro)) {
|
|
4278
|
+
return { kind: "suggestion", downgraded_from: "bug" };
|
|
4279
|
+
}
|
|
4280
|
+
return { kind };
|
|
4281
|
+
}
|
|
4282
|
+
async function appendFrictionReport(paths, input) {
|
|
4283
|
+
const { kind, downgraded_from } = normalizeKind(input.kind, input.repro);
|
|
4284
|
+
const summary = truncate(input.summary) ?? "(no summary)";
|
|
4285
|
+
const surface = truncate(input.surface) ?? "(unknown)";
|
|
4286
|
+
const fingerprint = frictionFingerprint({ kind, surface, summary });
|
|
4287
|
+
const report = {
|
|
4288
|
+
at: (input.now ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
4289
|
+
kind,
|
|
4290
|
+
surface,
|
|
4291
|
+
summary,
|
|
4292
|
+
...truncate(input.expected) ? { expected: truncate(input.expected) } : {},
|
|
4293
|
+
...truncate(input.observed) ? { observed: truncate(input.observed) } : {},
|
|
4294
|
+
...truncate(input.repro) ? { repro: truncate(input.repro) } : {},
|
|
4295
|
+
...input.version ? { version: input.version } : {},
|
|
4296
|
+
...downgraded_from ? { downgraded_from } : {},
|
|
4297
|
+
fingerprint
|
|
4298
|
+
};
|
|
4299
|
+
const prior = (await readFrictionReports(paths)).filter((r) => r.fingerprint === fingerprint);
|
|
4300
|
+
try {
|
|
4301
|
+
if (!existsSync12(paths.runtimeDir)) await mkdir9(paths.runtimeDir, { recursive: true });
|
|
4302
|
+
await appendFile3(frictionLogPath(paths), JSON.stringify(report) + "\n", "utf8");
|
|
4303
|
+
} catch {
|
|
4304
|
+
}
|
|
4305
|
+
const state = await loadFrictionState(paths);
|
|
4306
|
+
return {
|
|
4307
|
+
report,
|
|
4308
|
+
occurrences: prior.length + 1,
|
|
4309
|
+
already_reported: prior.length > 0,
|
|
4310
|
+
...state[fingerprint] ? { resolved_as: state[fingerprint] } : {}
|
|
4311
|
+
};
|
|
4312
|
+
}
|
|
4313
|
+
async function readFrictionReports(paths) {
|
|
4314
|
+
const file = frictionLogPath(paths);
|
|
4315
|
+
if (!existsSync12(file)) return [];
|
|
4316
|
+
let raw;
|
|
4317
|
+
try {
|
|
4318
|
+
raw = await readFile12(file, "utf8");
|
|
4319
|
+
} catch {
|
|
4320
|
+
return [];
|
|
4321
|
+
}
|
|
4322
|
+
const out = [];
|
|
4323
|
+
for (const line of raw.split("\n")) {
|
|
4324
|
+
if (!line.trim()) continue;
|
|
4325
|
+
try {
|
|
4326
|
+
const parsed = JSON.parse(line);
|
|
4327
|
+
if (parsed.fingerprint && parsed.at && parsed.summary) out.push(parsed);
|
|
4328
|
+
} catch {
|
|
4329
|
+
}
|
|
4330
|
+
}
|
|
4331
|
+
return out;
|
|
4332
|
+
}
|
|
4333
|
+
async function loadFrictionState(paths) {
|
|
4334
|
+
const file = frictionStatePath(paths);
|
|
4335
|
+
if (!existsSync12(file)) return {};
|
|
4336
|
+
try {
|
|
4337
|
+
const parsed = JSON.parse(await readFile12(file, "utf8"));
|
|
4338
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
4339
|
+
} catch {
|
|
4340
|
+
return {};
|
|
4341
|
+
}
|
|
4342
|
+
}
|
|
4343
|
+
async function saveFrictionState(paths, state) {
|
|
4344
|
+
if (!existsSync12(paths.runtimeDir)) await mkdir9(paths.runtimeDir, { recursive: true });
|
|
4345
|
+
await writeFile8(frictionStatePath(paths), JSON.stringify(state, null, 2) + "\n", "utf8");
|
|
4346
|
+
}
|
|
4347
|
+
async function setFrictionStatus(paths, fingerprint, status, url) {
|
|
4348
|
+
const state = await loadFrictionState(paths);
|
|
4349
|
+
const entry = {
|
|
4350
|
+
status,
|
|
4351
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4352
|
+
...url ? { url } : {}
|
|
4353
|
+
};
|
|
4354
|
+
state[fingerprint] = entry;
|
|
4355
|
+
await saveFrictionState(paths, state);
|
|
4356
|
+
return entry;
|
|
4357
|
+
}
|
|
4358
|
+
function groupFriction(reports, state = {}) {
|
|
4359
|
+
const byFingerprint = /* @__PURE__ */ new Map();
|
|
4360
|
+
for (const report of reports) {
|
|
4361
|
+
const bucket = byFingerprint.get(report.fingerprint);
|
|
4362
|
+
if (bucket) bucket.push(report);
|
|
4363
|
+
else byFingerprint.set(report.fingerprint, [report]);
|
|
4364
|
+
}
|
|
4365
|
+
const groups = [];
|
|
4366
|
+
for (const [fingerprint, bucket] of byFingerprint) {
|
|
4367
|
+
const sorted = [...bucket].sort((a, b) => a.at.localeCompare(b.at));
|
|
4368
|
+
const latest = sorted[sorted.length - 1];
|
|
4369
|
+
const resolution = state[fingerprint];
|
|
4370
|
+
groups.push({
|
|
4371
|
+
fingerprint,
|
|
4372
|
+
kind: latest.kind,
|
|
4373
|
+
surface: latest.surface,
|
|
4374
|
+
summary: latest.summary,
|
|
4375
|
+
count: sorted.length,
|
|
4376
|
+
first_seen: sorted[0].at,
|
|
4377
|
+
last_seen: latest.at,
|
|
4378
|
+
latest,
|
|
4379
|
+
status: resolution?.status ?? "open",
|
|
4380
|
+
...resolution?.at ? { submitted_at: resolution.at } : {},
|
|
4381
|
+
...resolution?.url ? { url: resolution.url } : {}
|
|
4382
|
+
});
|
|
4383
|
+
}
|
|
4384
|
+
return groups.sort(
|
|
4385
|
+
(a, b) => b.count - a.count || b.last_seen.localeCompare(a.last_seen)
|
|
4386
|
+
);
|
|
4387
|
+
}
|
|
4388
|
+
function formatFrictionIssue(group) {
|
|
4389
|
+
const title = `[${group.kind}] ${group.surface}: ${group.summary}`.slice(0, 120);
|
|
4390
|
+
const r = group.latest;
|
|
4391
|
+
const lines = [
|
|
4392
|
+
`**Surface:** \`${group.surface}\``,
|
|
4393
|
+
`**Reported:** ${group.count}\xD7 (first ${group.first_seen.slice(0, 10)}, last ${group.last_seen.slice(0, 10)})`,
|
|
4394
|
+
...r.version ? [`**Version:** ${r.version}`] : [],
|
|
4395
|
+
"",
|
|
4396
|
+
"### What happened",
|
|
4397
|
+
group.summary
|
|
4398
|
+
];
|
|
4399
|
+
if (r.expected) lines.push("", "### Expected", r.expected);
|
|
4400
|
+
if (r.observed) lines.push("", "### Observed", r.observed);
|
|
4401
|
+
if (r.repro) lines.push("", "### Reproduction", "```sh", r.repro, "```");
|
|
4402
|
+
if (r.downgraded_from === "bug") {
|
|
4403
|
+
lines.push(
|
|
4404
|
+
"",
|
|
4405
|
+
"> Reported as a bug but filed as a suggestion: no reproduction was supplied."
|
|
4406
|
+
);
|
|
4407
|
+
}
|
|
4408
|
+
lines.push(
|
|
4409
|
+
"",
|
|
4410
|
+
"---",
|
|
4411
|
+
"<sub>Captured by an AI agent session via `report_friction` and reviewed by a human before submission.</sub>"
|
|
4412
|
+
);
|
|
4413
|
+
return { title, body: lines.join("\n") };
|
|
4414
|
+
}
|
|
4415
|
+
|
|
4244
4416
|
// src/briefing-preset.ts
|
|
4245
4417
|
var BRIEFING_PRESET_DEFAULTS = {
|
|
4246
4418
|
/** Fast session start — minimal tokens, skip module CONTEXT.md slices */
|
|
@@ -4307,21 +4479,21 @@ function extractActionsBriefBody(markdown, maxChars = MAX_DEFAULT_CHARS) {
|
|
|
4307
4479
|
}
|
|
4308
4480
|
|
|
4309
4481
|
// src/resolve-project.ts
|
|
4310
|
-
import { existsSync as
|
|
4311
|
-
import
|
|
4482
|
+
import { existsSync as existsSync13 } from "fs";
|
|
4483
|
+
import path15 from "path";
|
|
4312
4484
|
var ROOT_MARKERS2 = [".ai", ".git", "package.json"];
|
|
4313
4485
|
function markersAtRoot(root) {
|
|
4314
4486
|
const found = [];
|
|
4315
4487
|
for (const m of ROOT_MARKERS2) {
|
|
4316
|
-
if (
|
|
4488
|
+
if (existsSync13(path15.join(root, m))) found.push(m);
|
|
4317
4489
|
}
|
|
4318
4490
|
return found;
|
|
4319
4491
|
}
|
|
4320
4492
|
function resolveProjectInfo(opts = {}) {
|
|
4321
4493
|
const env = opts.env ?? process.env;
|
|
4322
|
-
const cwd =
|
|
4494
|
+
const cwd = path15.resolve(opts.cwd ?? process.cwd());
|
|
4323
4495
|
const raw = env.HAIVE_PROJECT_ROOT;
|
|
4324
|
-
const explicit = raw !== void 0 && raw !== "" ?
|
|
4496
|
+
const explicit = raw !== void 0 && raw !== "" ? path15.resolve(raw) : null;
|
|
4325
4497
|
const resolvedRoot = explicit ?? findProjectRoot(cwd);
|
|
4326
4498
|
const paths = resolveHaivePaths(resolvedRoot);
|
|
4327
4499
|
return {
|
|
@@ -4329,8 +4501,8 @@ function resolveProjectInfo(opts = {}) {
|
|
|
4329
4501
|
resolved_root: resolvedRoot,
|
|
4330
4502
|
haive_project_root_env: explicit,
|
|
4331
4503
|
explicit_root: explicit != null,
|
|
4332
|
-
haive_dir_exists:
|
|
4333
|
-
memories_dir_exists:
|
|
4504
|
+
haive_dir_exists: existsSync13(paths.haiveDir),
|
|
4505
|
+
memories_dir_exists: existsSync13(paths.memoriesDir),
|
|
4334
4506
|
runtime_dir: paths.runtimeDir,
|
|
4335
4507
|
markers_found: markersAtRoot(resolvedRoot)
|
|
4336
4508
|
};
|
|
@@ -4585,16 +4757,16 @@ function findLexicalConflictPairs(memories, opts) {
|
|
|
4585
4757
|
}
|
|
4586
4758
|
|
|
4587
4759
|
// src/runtime-journal.ts
|
|
4588
|
-
import { mkdir as
|
|
4589
|
-
import { existsSync as
|
|
4590
|
-
import
|
|
4760
|
+
import { mkdir as mkdir10, readFile as readFile13, appendFile as appendFile4 } from "fs/promises";
|
|
4761
|
+
import { existsSync as existsSync14 } from "fs";
|
|
4762
|
+
import path16 from "path";
|
|
4591
4763
|
var RUNTIME_JOURNAL_FILENAME = "session-journal.ndjson";
|
|
4592
4764
|
function runtimeJournalPath(paths) {
|
|
4593
|
-
return
|
|
4765
|
+
return path16.join(paths.runtimeDir, RUNTIME_JOURNAL_FILENAME);
|
|
4594
4766
|
}
|
|
4595
4767
|
async function appendRuntimeJournalEntry(paths, entry) {
|
|
4596
4768
|
try {
|
|
4597
|
-
await
|
|
4769
|
+
await mkdir10(paths.runtimeDir, { recursive: true });
|
|
4598
4770
|
const line = {
|
|
4599
4771
|
ts: entry.ts ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
4600
4772
|
kind: entry.kind,
|
|
@@ -4602,7 +4774,7 @@ async function appendRuntimeJournalEntry(paths, entry) {
|
|
|
4602
4774
|
...entry.tool !== void 0 ? { tool: entry.tool } : {},
|
|
4603
4775
|
...entry.meta !== void 0 ? { meta: entry.meta } : {}
|
|
4604
4776
|
};
|
|
4605
|
-
await
|
|
4777
|
+
await appendFile4(
|
|
4606
4778
|
runtimeJournalPath(paths),
|
|
4607
4779
|
JSON.stringify(line) + "\n",
|
|
4608
4780
|
"utf8"
|
|
@@ -4612,9 +4784,9 @@ async function appendRuntimeJournalEntry(paths, entry) {
|
|
|
4612
4784
|
}
|
|
4613
4785
|
async function readRuntimeJournalTail(paths, limit) {
|
|
4614
4786
|
const file = runtimeJournalPath(paths);
|
|
4615
|
-
if (!
|
|
4787
|
+
if (!existsSync14(file) || limit <= 0) return [];
|
|
4616
4788
|
try {
|
|
4617
|
-
const raw = await
|
|
4789
|
+
const raw = await readFile13(file, "utf8");
|
|
4618
4790
|
const lines = raw.trim().split("\n").filter(Boolean);
|
|
4619
4791
|
const parsed = [];
|
|
4620
4792
|
for (const line of lines.slice(-limit)) {
|
|
@@ -4630,22 +4802,22 @@ async function readRuntimeJournalTail(paths, limit) {
|
|
|
4630
4802
|
}
|
|
4631
4803
|
|
|
4632
4804
|
// src/enforcement.ts
|
|
4633
|
-
import { mkdir as
|
|
4634
|
-
import { existsSync as
|
|
4635
|
-
import
|
|
4805
|
+
import { mkdir as mkdir11, readdir as readdir4, readFile as readFile14, writeFile as writeFile9 } from "fs/promises";
|
|
4806
|
+
import { existsSync as existsSync15 } from "fs";
|
|
4807
|
+
import path17 from "path";
|
|
4636
4808
|
var BRIEFING_MARKER_TTL_MS = 12 * 60 * 60 * 1e3;
|
|
4637
4809
|
var SESSION_RECAP_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
4638
4810
|
function enforcementDir(paths) {
|
|
4639
|
-
return
|
|
4811
|
+
return path17.join(paths.runtimeDir, "enforcement");
|
|
4640
4812
|
}
|
|
4641
4813
|
function briefingMarkersDir(paths) {
|
|
4642
|
-
return
|
|
4814
|
+
return path17.join(enforcementDir(paths), "briefings");
|
|
4643
4815
|
}
|
|
4644
4816
|
function normalizeSessionId(sessionId) {
|
|
4645
4817
|
return (sessionId?.trim() || "default").replace(/[^a-zA-Z0-9_.-]+/g, "-").slice(0, 120);
|
|
4646
4818
|
}
|
|
4647
4819
|
function briefingMarkerPath(paths, sessionId) {
|
|
4648
|
-
return
|
|
4820
|
+
return path17.join(briefingMarkersDir(paths), `${normalizeSessionId(sessionId)}.json`);
|
|
4649
4821
|
}
|
|
4650
4822
|
async function writeBriefingMarker(paths, input) {
|
|
4651
4823
|
const sessionId = normalizeSessionId(input.sessionId);
|
|
@@ -4670,8 +4842,8 @@ async function writeBriefingMarker(paths, input) {
|
|
|
4670
4842
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4671
4843
|
root: paths.root
|
|
4672
4844
|
};
|
|
4673
|
-
await
|
|
4674
|
-
await
|
|
4845
|
+
await mkdir11(briefingMarkersDir(paths), { recursive: true });
|
|
4846
|
+
await writeFile9(
|
|
4675
4847
|
briefingMarkerPath(paths, marker.session_id),
|
|
4676
4848
|
JSON.stringify(marker, null, 2) + "\n",
|
|
4677
4849
|
"utf8"
|
|
@@ -4680,9 +4852,9 @@ async function writeBriefingMarker(paths, input) {
|
|
|
4680
4852
|
}
|
|
4681
4853
|
async function readSessionBriefingMarker(paths, sessionId, ttlMs = BRIEFING_MARKER_TTL_MS) {
|
|
4682
4854
|
const file = briefingMarkerPath(paths, sessionId);
|
|
4683
|
-
if (!
|
|
4855
|
+
if (!existsSync15(file)) return null;
|
|
4684
4856
|
try {
|
|
4685
|
-
const marker = JSON.parse(await
|
|
4857
|
+
const marker = JSON.parse(await readFile14(file, "utf8"));
|
|
4686
4858
|
const created = Date.parse(marker.created_at);
|
|
4687
4859
|
if (!Number.isFinite(created) || Date.now() - created > ttlMs) return null;
|
|
4688
4860
|
return marker;
|
|
@@ -4694,18 +4866,18 @@ async function hasRecentBriefingMarker(paths, sessionId, ttlMs = BRIEFING_MARKER
|
|
|
4694
4866
|
const now = Date.now();
|
|
4695
4867
|
const candidates = [];
|
|
4696
4868
|
const exact = briefingMarkerPath(paths, sessionId);
|
|
4697
|
-
if (
|
|
4869
|
+
if (existsSync15(exact)) candidates.push(exact);
|
|
4698
4870
|
try {
|
|
4699
4871
|
const dir = briefingMarkersDir(paths);
|
|
4700
4872
|
const files = await readdir4(dir);
|
|
4701
4873
|
for (const file of files) {
|
|
4702
|
-
if (file.endsWith(".json")) candidates.push(
|
|
4874
|
+
if (file.endsWith(".json")) candidates.push(path17.join(dir, file));
|
|
4703
4875
|
}
|
|
4704
4876
|
} catch {
|
|
4705
4877
|
}
|
|
4706
4878
|
for (const file of new Set(candidates)) {
|
|
4707
4879
|
try {
|
|
4708
|
-
const marker = JSON.parse(await
|
|
4880
|
+
const marker = JSON.parse(await readFile14(file, "utf8"));
|
|
4709
4881
|
const created = Date.parse(marker.created_at);
|
|
4710
4882
|
if (Number.isFinite(created) && now - created <= ttlMs) return true;
|
|
4711
4883
|
} catch {
|
|
@@ -4717,12 +4889,12 @@ async function readRecentBriefingMarker(paths, sessionId, ttlMs = BRIEFING_MARKE
|
|
|
4717
4889
|
const now = Date.now();
|
|
4718
4890
|
const candidates = [];
|
|
4719
4891
|
const exact = briefingMarkerPath(paths, sessionId);
|
|
4720
|
-
if (
|
|
4892
|
+
if (existsSync15(exact)) candidates.push(exact);
|
|
4721
4893
|
try {
|
|
4722
4894
|
const dir = briefingMarkersDir(paths);
|
|
4723
4895
|
const files = await readdir4(dir);
|
|
4724
4896
|
for (const file of files) {
|
|
4725
|
-
if (file.endsWith(".json")) candidates.push(
|
|
4897
|
+
if (file.endsWith(".json")) candidates.push(path17.join(dir, file));
|
|
4726
4898
|
}
|
|
4727
4899
|
} catch {
|
|
4728
4900
|
}
|
|
@@ -4730,7 +4902,7 @@ async function readRecentBriefingMarker(paths, sessionId, ttlMs = BRIEFING_MARKE
|
|
|
4730
4902
|
let freshestTs = 0;
|
|
4731
4903
|
for (const file of new Set(candidates)) {
|
|
4732
4904
|
try {
|
|
4733
|
-
const marker = JSON.parse(await
|
|
4905
|
+
const marker = JSON.parse(await readFile14(file, "utf8"));
|
|
4734
4906
|
const created = Date.parse(marker.created_at);
|
|
4735
4907
|
if (!Number.isFinite(created) || now - created > ttlMs) continue;
|
|
4736
4908
|
if (created > freshestTs) {
|
|
@@ -4780,15 +4952,15 @@ function isRetiredMemory(fm, body = "", now = /* @__PURE__ */ new Date()) {
|
|
|
4780
4952
|
}
|
|
4781
4953
|
|
|
4782
4954
|
// src/sensor-ledger.ts
|
|
4783
|
-
import { createHash as
|
|
4784
|
-
import { existsSync as
|
|
4785
|
-
import { appendFile as
|
|
4786
|
-
import
|
|
4955
|
+
import { createHash as createHash3 } from "crypto";
|
|
4956
|
+
import { existsSync as existsSync16, readFileSync as readFileSync2 } from "fs";
|
|
4957
|
+
import { appendFile as appendFile5, mkdir as mkdir12, readFile as readFile15, rename, writeFile as writeFile10 } from "fs/promises";
|
|
4958
|
+
import path18 from "path";
|
|
4787
4959
|
var MAX_LINES = 1e4;
|
|
4788
4960
|
var RETAINED_LINES = 8e3;
|
|
4789
4961
|
var DAY_MS = 864e5;
|
|
4790
4962
|
function sensorLedgerPath(paths) {
|
|
4791
|
-
return
|
|
4963
|
+
return path18.join(paths.runtimeDir, "enforcement", "sensor-ledger.ndjson");
|
|
4792
4964
|
}
|
|
4793
4965
|
function isEvaluation(value) {
|
|
4794
4966
|
if (!value || typeof value !== "object") return false;
|
|
@@ -4799,13 +4971,13 @@ async function appendSensorEvaluations(paths, evaluations) {
|
|
|
4799
4971
|
if (evaluations.length === 0) return;
|
|
4800
4972
|
try {
|
|
4801
4973
|
const file = sensorLedgerPath(paths);
|
|
4802
|
-
await
|
|
4803
|
-
await
|
|
4804
|
-
const raw = await
|
|
4974
|
+
await mkdir12(path18.dirname(file), { recursive: true });
|
|
4975
|
+
await appendFile5(file, evaluations.map((e) => JSON.stringify(e)).join("\n") + "\n", "utf8");
|
|
4976
|
+
const raw = await readFile15(file, "utf8");
|
|
4805
4977
|
const lines = raw.split("\n").filter(Boolean);
|
|
4806
4978
|
if (lines.length > MAX_LINES) {
|
|
4807
4979
|
const temp = `${file}.${process.pid}.tmp`;
|
|
4808
|
-
await
|
|
4980
|
+
await writeFile10(temp, lines.slice(-RETAINED_LINES).join("\n") + "\n", "utf8");
|
|
4809
4981
|
await rename(temp, file);
|
|
4810
4982
|
}
|
|
4811
4983
|
} catch {
|
|
@@ -4814,9 +4986,9 @@ async function appendSensorEvaluations(paths, evaluations) {
|
|
|
4814
4986
|
async function loadSensorLedger(paths, opts = {}) {
|
|
4815
4987
|
try {
|
|
4816
4988
|
const file = sensorLedgerPath(paths);
|
|
4817
|
-
if (!
|
|
4989
|
+
if (!existsSync16(file)) return [];
|
|
4818
4990
|
const since = opts.since ? Date.parse(opts.since) : Number.NEGATIVE_INFINITY;
|
|
4819
|
-
const raw = await
|
|
4991
|
+
const raw = await readFile15(file, "utf8");
|
|
4820
4992
|
const out = [];
|
|
4821
4993
|
for (const line of raw.split("\n")) {
|
|
4822
4994
|
if (!line.trim()) continue;
|
|
@@ -4838,11 +5010,11 @@ function computeScopeHash(root, scopedFiles) {
|
|
|
4838
5010
|
try {
|
|
4839
5011
|
const files = [...new Set(scopedFiles.map((f) => f.replace(/\\/g, "/")))].sort();
|
|
4840
5012
|
if (files.length === 0) return "";
|
|
4841
|
-
const hash =
|
|
5013
|
+
const hash = createHash3("sha256");
|
|
4842
5014
|
let included = 0;
|
|
4843
5015
|
for (const rel of files) {
|
|
4844
|
-
const abs =
|
|
4845
|
-
if (!
|
|
5016
|
+
const abs = path18.resolve(root, rel);
|
|
5017
|
+
if (!existsSync16(abs)) continue;
|
|
4846
5018
|
try {
|
|
4847
5019
|
hash.update(rel);
|
|
4848
5020
|
hash.update("\0");
|
|
@@ -5576,8 +5748,8 @@ function normalizeFindingSeverity(raw) {
|
|
|
5576
5748
|
return "info";
|
|
5577
5749
|
}
|
|
5578
5750
|
}
|
|
5579
|
-
function findingKey(tool, ruleId,
|
|
5580
|
-
return `${tool}:${ruleId}:${
|
|
5751
|
+
function findingKey(tool, ruleId, path21) {
|
|
5752
|
+
return `${tool}:${ruleId}:${path21}`;
|
|
5581
5753
|
}
|
|
5582
5754
|
function coerceJson(input) {
|
|
5583
5755
|
if (typeof input === "string") {
|
|
@@ -5611,8 +5783,8 @@ function parseSarif(input) {
|
|
|
5611
5783
|
const physical = asRecord(location.physicalLocation);
|
|
5612
5784
|
const artifact = asRecord(physical.artifactLocation);
|
|
5613
5785
|
const region = asRecord(physical.region);
|
|
5614
|
-
const
|
|
5615
|
-
if (!
|
|
5786
|
+
const path21 = typeof artifact.uri === "string" ? normalizeUri(artifact.uri) : "";
|
|
5787
|
+
if (!path21) continue;
|
|
5616
5788
|
const line = typeof region.startLine === "number" ? region.startLine : void 0;
|
|
5617
5789
|
const snippet = typeof asRecord(region.snippet).text === "string" ? asRecord(region.snippet).text.trim() : void 0;
|
|
5618
5790
|
findings.push({
|
|
@@ -5620,10 +5792,10 @@ function parseSarif(input) {
|
|
|
5620
5792
|
ruleId,
|
|
5621
5793
|
message: message.trim(),
|
|
5622
5794
|
severity,
|
|
5623
|
-
path:
|
|
5795
|
+
path: path21,
|
|
5624
5796
|
...line !== void 0 ? { line } : {},
|
|
5625
5797
|
...snippet ? { snippet } : {},
|
|
5626
|
-
key: findingKey(tool, ruleId,
|
|
5798
|
+
key: findingKey(tool, ruleId, path21)
|
|
5627
5799
|
});
|
|
5628
5800
|
}
|
|
5629
5801
|
}
|
|
@@ -5642,17 +5814,17 @@ function parseSonar(input) {
|
|
|
5642
5814
|
(typeof issue.severity === "string" ? issue.severity : void 0) ?? impactSeverity
|
|
5643
5815
|
);
|
|
5644
5816
|
const component = typeof issue.component === "string" ? issue.component : "";
|
|
5645
|
-
const
|
|
5646
|
-
if (!
|
|
5817
|
+
const path21 = componentToPath(component);
|
|
5818
|
+
if (!path21) continue;
|
|
5647
5819
|
const line = typeof issue.line === "number" ? issue.line : void 0;
|
|
5648
5820
|
findings.push({
|
|
5649
5821
|
tool: "sonar",
|
|
5650
5822
|
ruleId,
|
|
5651
5823
|
message,
|
|
5652
5824
|
severity,
|
|
5653
|
-
path:
|
|
5825
|
+
path: path21,
|
|
5654
5826
|
...line !== void 0 ? { line } : {},
|
|
5655
|
-
key: findingKey("sonar", ruleId,
|
|
5827
|
+
key: findingKey("sonar", ruleId, path21)
|
|
5656
5828
|
});
|
|
5657
5829
|
}
|
|
5658
5830
|
return findings;
|
|
@@ -5665,7 +5837,7 @@ function parseEslintJson(input, opts = {}) {
|
|
|
5665
5837
|
const file = asRecord(fileRaw);
|
|
5666
5838
|
const rawPath = typeof file.filePath === "string" ? file.filePath : "";
|
|
5667
5839
|
if (!rawPath) continue;
|
|
5668
|
-
const
|
|
5840
|
+
const path21 = cwd && rawPath.startsWith(cwd) ? rawPath.slice(cwd.length) : rawPath;
|
|
5669
5841
|
for (const msgRaw of asArray(file.messages)) {
|
|
5670
5842
|
const msg = asRecord(msgRaw);
|
|
5671
5843
|
const ruleId = typeof msg.ruleId === "string" && msg.ruleId ? msg.ruleId : "parse-error";
|
|
@@ -5677,9 +5849,9 @@ function parseEslintJson(input, opts = {}) {
|
|
|
5677
5849
|
ruleId,
|
|
5678
5850
|
message,
|
|
5679
5851
|
severity,
|
|
5680
|
-
path:
|
|
5852
|
+
path: path21,
|
|
5681
5853
|
...line !== void 0 ? { line } : {},
|
|
5682
|
-
key: findingKey("eslint", ruleId,
|
|
5854
|
+
key: findingKey("eslint", ruleId, path21)
|
|
5683
5855
|
});
|
|
5684
5856
|
}
|
|
5685
5857
|
}
|
|
@@ -6129,7 +6301,7 @@ function tallyHotFiles(paths, source = "agent") {
|
|
|
6129
6301
|
if (!norm) continue;
|
|
6130
6302
|
counts.set(norm, (counts.get(norm) ?? 0) + 1);
|
|
6131
6303
|
}
|
|
6132
|
-
return [...counts.entries()].map(([
|
|
6304
|
+
return [...counts.entries()].map(([path21, changes]) => ({ path: path21, changes, source })).sort((a, b) => b.changes - a.changes);
|
|
6133
6305
|
}
|
|
6134
6306
|
function mergeHotFiles(a, b) {
|
|
6135
6307
|
const merged = /* @__PURE__ */ new Map();
|
|
@@ -6149,21 +6321,21 @@ function mergeHotFiles(a, b) {
|
|
|
6149
6321
|
}
|
|
6150
6322
|
|
|
6151
6323
|
// src/eval-history.ts
|
|
6152
|
-
import { appendFile as
|
|
6153
|
-
import { existsSync as
|
|
6154
|
-
import
|
|
6324
|
+
import { appendFile as appendFile6, mkdir as mkdir13, readFile as readFile16 } from "fs/promises";
|
|
6325
|
+
import { existsSync as existsSync17 } from "fs";
|
|
6326
|
+
import path19 from "path";
|
|
6155
6327
|
function evalHistoryPath(paths) {
|
|
6156
|
-
return
|
|
6328
|
+
return path19.join(paths.haiveDir, ".cache", "eval-history.jsonl");
|
|
6157
6329
|
}
|
|
6158
6330
|
async function appendEvalHistory(paths, entry) {
|
|
6159
6331
|
const file = evalHistoryPath(paths);
|
|
6160
|
-
await
|
|
6161
|
-
await
|
|
6332
|
+
await mkdir13(path19.dirname(file), { recursive: true });
|
|
6333
|
+
await appendFile6(file, JSON.stringify(entry) + "\n", "utf8");
|
|
6162
6334
|
}
|
|
6163
6335
|
async function loadEvalHistory(paths) {
|
|
6164
6336
|
const file = evalHistoryPath(paths);
|
|
6165
|
-
if (!
|
|
6166
|
-
const raw = await
|
|
6337
|
+
if (!existsSync17(file)) return [];
|
|
6338
|
+
const raw = await readFile16(file, "utf8").catch(() => "");
|
|
6167
6339
|
const out = [];
|
|
6168
6340
|
for (const line of raw.split("\n")) {
|
|
6169
6341
|
const trimmed = line.trim();
|
|
@@ -6506,12 +6678,12 @@ ${trimmed}`;
|
|
|
6506
6678
|
}
|
|
6507
6679
|
|
|
6508
6680
|
// src/handoff.ts
|
|
6509
|
-
import { writeFile as
|
|
6510
|
-
import { existsSync as
|
|
6511
|
-
import
|
|
6681
|
+
import { writeFile as writeFile11, readFile as readFile17, stat as stat3 } from "fs/promises";
|
|
6682
|
+
import { existsSync as existsSync18 } from "fs";
|
|
6683
|
+
import path20 from "path";
|
|
6512
6684
|
var HANDOFF_FILENAME = "NEXT.md";
|
|
6513
6685
|
function handoffFilePath(root) {
|
|
6514
|
-
return
|
|
6686
|
+
return path20.join(root, HANDOFF_FILENAME);
|
|
6515
6687
|
}
|
|
6516
6688
|
function buildHandoffMarkdown(data) {
|
|
6517
6689
|
const at = (data.at ?? /* @__PURE__ */ new Date()).toISOString();
|
|
@@ -6560,18 +6732,18 @@ function buildHandoffMarkdown(data) {
|
|
|
6560
6732
|
}
|
|
6561
6733
|
async function writeSessionHandoff(root, data) {
|
|
6562
6734
|
const file = handoffFilePath(root);
|
|
6563
|
-
await
|
|
6735
|
+
await writeFile11(file, buildHandoffMarkdown(data), "utf8");
|
|
6564
6736
|
return file;
|
|
6565
6737
|
}
|
|
6566
6738
|
async function readSessionHandoff(root) {
|
|
6567
6739
|
const file = handoffFilePath(root);
|
|
6568
|
-
if (!
|
|
6569
|
-
const raw = await
|
|
6740
|
+
if (!existsSync18(file)) return null;
|
|
6741
|
+
const raw = await readFile17(file, "utf8").catch(() => "");
|
|
6570
6742
|
return raw.trim() ? raw : null;
|
|
6571
6743
|
}
|
|
6572
6744
|
async function handoffAgeMs(root, now = /* @__PURE__ */ new Date()) {
|
|
6573
6745
|
const file = handoffFilePath(root);
|
|
6574
|
-
if (!
|
|
6746
|
+
if (!existsSync18(file)) return null;
|
|
6575
6747
|
try {
|
|
6576
6748
|
const s = await stat3(file);
|
|
6577
6749
|
return Math.max(0, now.getTime() - s.mtimeMs);
|
|
@@ -6707,6 +6879,9 @@ export {
|
|
|
6707
6879
|
DEFAULT_DORMANT_DAYS,
|
|
6708
6880
|
DEFAULT_PRIORITY_SIGNALS,
|
|
6709
6881
|
ENV_WORKAROUND_TAGS,
|
|
6882
|
+
FRICTION_FIELD_MAX,
|
|
6883
|
+
FRICTION_LOG_FILE,
|
|
6884
|
+
FRICTION_STATE_FILE,
|
|
6710
6885
|
GUESSABLE_THRESHOLD,
|
|
6711
6886
|
HAIVE_DIR,
|
|
6712
6887
|
HAIVE_OWNED_FILES,
|
|
@@ -6744,6 +6919,7 @@ export {
|
|
|
6744
6919
|
anchorMatchesComponent,
|
|
6745
6920
|
antiPatternGateParams,
|
|
6746
6921
|
appendEvalHistory,
|
|
6922
|
+
appendFrictionReport,
|
|
6747
6923
|
appendPreventionEvent,
|
|
6748
6924
|
appendProposedRetrievalCases,
|
|
6749
6925
|
appendRuntimeJournalEntry,
|
|
@@ -6821,10 +6997,15 @@ export {
|
|
|
6821
6997
|
findingBody,
|
|
6822
6998
|
findingToDraft,
|
|
6823
6999
|
firstMemoryOneLine,
|
|
7000
|
+
formatFrictionIssue,
|
|
7001
|
+
frictionFingerprint,
|
|
7002
|
+
frictionLogPath,
|
|
7003
|
+
frictionStatePath,
|
|
6824
7004
|
gatePassedShas,
|
|
6825
7005
|
generateBridges,
|
|
6826
7006
|
getUsage,
|
|
6827
7007
|
globToRegExp,
|
|
7008
|
+
groupFriction,
|
|
6828
7009
|
handoffAgeMs,
|
|
6829
7010
|
handoffFilePath,
|
|
6830
7011
|
hasPendingTestMarker,
|
|
@@ -6861,6 +7042,7 @@ export {
|
|
|
6861
7042
|
loadConfig,
|
|
6862
7043
|
loadConfigSync,
|
|
6863
7044
|
loadEvalHistory,
|
|
7045
|
+
loadFrictionState,
|
|
6864
7046
|
loadMemoriesFromDir,
|
|
6865
7047
|
loadMemoriesFromDirDetailed,
|
|
6866
7048
|
loadMemory,
|
|
@@ -6879,6 +7061,8 @@ export {
|
|
|
6879
7061
|
newMemoryId,
|
|
6880
7062
|
normalizeFindingSeverity,
|
|
6881
7063
|
normalizeFramework,
|
|
7064
|
+
normalizeFrictionSummary,
|
|
7065
|
+
normalizeKind,
|
|
6882
7066
|
normalizeScaffoldStyle,
|
|
6883
7067
|
normalizeSessionId,
|
|
6884
7068
|
overallScore,
|
|
@@ -6907,6 +7091,7 @@ export {
|
|
|
6907
7091
|
quarantineNote,
|
|
6908
7092
|
queryCodeMap,
|
|
6909
7093
|
rankMemoriesLexical,
|
|
7094
|
+
readFrictionReports,
|
|
6910
7095
|
readRecentBriefingMarker,
|
|
6911
7096
|
readRuntimeJournalTail,
|
|
6912
7097
|
readSessionHandoff,
|
|
@@ -6938,6 +7123,7 @@ export {
|
|
|
6938
7123
|
runtimeJournalPath,
|
|
6939
7124
|
saveCodeMap,
|
|
6940
7125
|
saveConfig,
|
|
7126
|
+
saveFrictionState,
|
|
6941
7127
|
saveUsageIndex,
|
|
6942
7128
|
scaffoldPostIncidentTest,
|
|
6943
7129
|
scannableSensorTargets,
|
|
@@ -6952,6 +7138,7 @@ export {
|
|
|
6952
7138
|
sensorSelfCheck,
|
|
6953
7139
|
sensorTargetsFromDiff,
|
|
6954
7140
|
serializeMemory,
|
|
7141
|
+
setFrictionStatus,
|
|
6955
7142
|
snapshotContract,
|
|
6956
7143
|
specificityScore,
|
|
6957
7144
|
stripPrivate,
|