pi-lens
Version:
Real-time code feedback for pi — LSP, linters, formatters, type-checking, structural analysis & booboo
563 lines (554 loc) • 19.6 kB
JavaScript
import { createRequire as __pilensCreateRequire } from "node:module"; const require = __pilensCreateRequire(import.meta.url);
import {
freshnessFromMtime,
resolveRunnerPath
} from "./chunk-PWFHBTSF.js";
import {
translateGuidanceToolNames
} from "./chunk-N3YQJI6O.js";
import {
logLatency
} from "./chunk-5THXD6X5.js";
import {
normalizeMapKey,
toProjectRelativePath
} from "./chunk-Q5U6FMDI.js";
// dist/clients/advisory-provenance.js
import { createHash } from "node:crypto";
import * as fs from "node:fs";
import * as path from "node:path";
var MAX_ADVISORY_AFFECTED_FILES = 256;
function advisoryPathKey(filePath, cwd) {
return normalizeMapKey(path.resolve(cwd, filePath));
}
function advisoryFileHash(filePath) {
try {
return createHash("sha256").update(fs.readFileSync(filePath)).digest("hex");
} catch (error) {
const code = error.code ?? "unknown";
return code === "ENOENT" ? "missing" : `unreadable:${code}`;
}
}
function snapshotOne(filePath, cwd, role) {
const resolved = path.resolve(cwd, filePath);
try {
const stat = fs.statSync(resolved);
return {
path: resolved,
role,
mtimeMs: stat.mtimeMs,
size: stat.size,
sha256: advisoryFileHash(resolved)
};
} catch (error) {
const code = error.code ?? "unknown";
return {
path: resolved,
role,
mtimeMs: -1,
size: -1,
sha256: code === "ENOENT" ? "missing" : `unreadable:${code}`
};
}
}
function snapshotAdvisoryProvenance(args) {
const seen = /* @__PURE__ */ new Set();
const files = [];
for (const file of args.files) {
const key = advisoryPathKey(file.path, args.cwd);
if (seen.has(key))
continue;
seen.add(key);
files.push(snapshotOne(file.path, args.cwd, file.role));
}
return {
revision: {
sessionId: args.runtime.telemetrySessionId,
projectSeq: args.runtime.projectSeq,
turnIndex: args.runtime.turnIndex,
generation: args.generation,
capturedAt: args.capturedAt ?? Date.now()
},
files,
...args.truncated ? { truncated: true } : {}
};
}
function isFiniteNumber(value) {
return typeof value === "number" && Number.isFinite(value);
}
function isCapturedHash(value) {
return typeof value === "string" && (/^[a-f0-9]{64}$/.test(value) || value === "missing" || value.startsWith("unreadable:"));
}
function isWellFormed(value) {
if (!value || typeof value !== "object")
return false;
const record = value;
const revision = record.revision;
return !!revision && typeof revision.sessionId === "string" && isFiniteNumber(revision.projectSeq) && isFiniteNumber(revision.turnIndex) && isFiniteNumber(revision.generation) && isFiniteNumber(revision.capturedAt) && Array.isArray(record.files) && record.files.length > 0 && record.files.every((file) => !!file && typeof file.path === "string" && (file.role === "source" || file.role === "test" || file.role === "affected") && isFiniteNumber(file.mtimeMs) && isFiniteNumber(file.size) && isCapturedHash(file.sha256));
}
function validateAdvisoryProvenance(record, cwd, runtime) {
if (!isWellFormed(record.provenance)) {
return {
status: "unknown",
reasons: ["malformed-or-legacy-provenance"],
allFilesDeleted: false,
changedPathCount: 0
};
}
const provenance = record.provenance;
const reasons = [];
let unknown = provenance.truncated === true;
if (unknown)
reasons.push("truncated-provenance");
if (runtime) {
if (provenance.revision.sessionId !== runtime.telemetrySessionId)
reasons.push("session-mismatch");
}
let deletedFiles = 0;
const changedPaths = /* @__PURE__ */ new Set();
for (const captured of provenance.files) {
const resolved = path.resolve(cwd, captured.path);
const reasonsBefore = reasons.length;
let stat;
try {
stat = fs.statSync(resolved);
} catch (error) {
const code = error.code ?? "unknown";
if (code === "ENOENT") {
if (captured.sha256 !== "missing") {
deletedFiles += 1;
reasons.push(`missing:${advisoryPathKey(resolved, cwd)}`);
changedPaths.add(advisoryPathKey(resolved, cwd));
}
} else {
unknown = true;
reasons.push(`unreadable:${advisoryPathKey(resolved, cwd)}:${code}`);
changedPaths.add(advisoryPathKey(resolved, cwd));
}
continue;
}
if (captured.sha256.startsWith("unreadable:")) {
unknown = true;
reasons.push(`capture-unreadable:${advisoryPathKey(resolved, cwd)}`);
changedPaths.add(advisoryPathKey(resolved, cwd));
continue;
}
if (captured.sha256 === "missing") {
reasons.push(`created:${advisoryPathKey(resolved, cwd)}`);
changedPaths.add(advisoryPathKey(resolved, cwd));
continue;
}
if (stat.mtimeMs !== captured.mtimeMs || stat.size !== captured.size) {
reasons.push(`metadata-changed:${advisoryPathKey(resolved, cwd)}`);
}
const currentHash = advisoryFileHash(resolved);
if (currentHash.startsWith("unreadable:")) {
unknown = true;
reasons.push(`${currentHash}:${advisoryPathKey(resolved, cwd)}`);
} else if (currentHash !== captured.sha256) {
reasons.push(`content-changed:${advisoryPathKey(resolved, cwd)}`);
}
if (reasons.length > reasonsBefore)
changedPaths.add(advisoryPathKey(resolved, cwd));
}
const allFilesDeleted = deletedFiles === provenance.files.length;
if (unknown)
return {
status: "unknown",
reasons,
allFilesDeleted,
changedPathCount: changedPaths.size
};
return reasons.length > 0 ? {
status: "superseded",
reasons,
allFilesDeleted,
changedPathCount: changedPaths.size
} : { status: "current", reasons: [], allFilesDeleted, changedPathCount: 0 };
}
var MAX_FINDING_PATH_STATS = MAX_ADVISORY_AFFECTED_FILES;
var MAX_LOGGED_DEAD_PATHS = 3;
function statFindingPath(resolvedPath) {
try {
return { state: "present", mtimeMs: fs.statSync(resolvedPath).mtimeMs };
} catch (error) {
const code = error.code ?? "unknown";
return code === "ENOENT" || code === "ENOTDIR" ? { state: "missing" } : { state: "unknown" };
}
}
function freshnessFromFacts(facts, scannedAtMs) {
if (facts.state === "missing")
return "missing";
if (facts.state === "unknown")
return "unknown";
if (scannedAtMs === void 0)
return "live";
return freshnessFromMtime({
mtimeMs: facts.mtimeMs,
referenceMs: scannedAtMs
}).verdict === "stale" ? "stale" : "live";
}
function parseScannedAtMs(scannedAt) {
if (scannedAt === void 0 || scannedAt === null || scannedAt === "") {
return void 0;
}
const ms = typeof scannedAt === "number" ? scannedAt : Date.parse(scannedAt);
return Number.isFinite(ms) ? ms : void 0;
}
function createPathFactsMemo(args) {
return {
facts: /* @__PURE__ */ new Map(),
probe: args.probePath ?? statFindingPath,
limit: args.maxUniquePaths ?? MAX_FINDING_PATH_STATS,
starved: /* @__PURE__ */ new Map()
};
}
function partitionOneSource(args, memo) {
const scannedAtMs = parseScannedAtMs(args.scannedAt);
const onMissing = args.onMissing ?? "drop";
const rawVerdicts = /* @__PURE__ */ new Map();
const live = [];
const stale = [];
const dropped = [];
const deadPaths = [];
const stalePaths = [];
const seenPaths = /* @__PURE__ */ new Set();
let probed = 0;
for (const finding of args.findings) {
const cited = args.citedPath(finding);
if (!cited) {
live.push(finding);
continue;
}
let verdict = rawVerdicts.get(cited);
if (verdict === void 0) {
const resolved = resolveRunnerPath(args.cwd, cited);
let facts = memo.facts.get(resolved);
if (facts === void 0) {
if (probed >= memo.limit) {
memo.starved.set(args.store, (memo.starved.get(args.store) ?? 0) + 1);
facts = { state: "unknown" };
} else {
probed += 1;
facts = memo.probe(resolved);
memo.facts.set(resolved, facts);
}
}
verdict = freshnessFromFacts(facts, scannedAtMs);
if (verdict === "missing" && onMissing === "demote")
verdict = "stale";
if (!seenPaths.has(resolved)) {
seenPaths.add(resolved);
if (verdict === "missing")
deadPaths.push(resolved);
else if (verdict === "stale")
stalePaths.push(resolved);
}
rawVerdicts.set(cited, verdict);
}
if (verdict === "missing")
dropped.push(finding);
else if (verdict === "stale")
stale.push(finding);
else
live.push(finding);
}
return {
live,
stale,
dropped,
deadPaths,
stalePaths,
statCount: memo.facts.size,
truncated: (memo.starved.get(args.store) ?? 0) > 0
};
}
function gateFindingsByPathFreshness(args) {
const memo = createPathFactsMemo(args);
const names = Object.keys(args.sources).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
const gates = {};
const dropped = {};
const demoted = {};
const merged = {
live: [],
stale: [],
dropped: [],
deadPaths: [],
stalePaths: [],
statCount: 0,
truncated: false
};
const scannedAtByStore = {};
for (const name of names) {
const source = args.sources[name];
const partition = partitionOneSource({ ...source, store: name, cwd: args.cwd }, memo);
gates[name] = { live: partition.live, stale: partition.stale };
if (partition.dropped.length > 0)
dropped[name] = partition.dropped.length;
if (partition.stale.length > 0)
demoted[name] = partition.stale.length;
if (source.scannedAt !== void 0) {
scannedAtByStore[name] = String(source.scannedAt);
}
merged.live.push(...partition.live);
merged.stale.push(...partition.stale);
merged.dropped.push(...partition.dropped);
for (const deadPath of partition.deadPaths) {
if (!merged.deadPaths.includes(deadPath))
merged.deadPaths.push(deadPath);
}
for (const stalePath of partition.stalePaths) {
if (!merged.stalePaths.includes(stalePath)) {
merged.stalePaths.push(stalePath);
}
}
}
merged.statCount = memo.facts.size;
merged.truncated = memo.starved.size > 0;
if (args.quiet !== true) {
emitDeadPathDropRecord(args.cwd, merged, dropped);
emitStaleLineDemoteRecord(args.cwd, scannedAtByStore, merged, demoted);
emitStatBudgetRecord(args.cwd, memo);
}
return gates;
}
function emitDeadPathDropRecord(cwd, partition, byStore) {
if (partition.dropped.length === 0)
return;
const stores = Object.keys(byStore);
logLatency({
type: "phase",
phase: "finding_dead_path_drop",
filePath: cwd,
durationMs: 0,
metadata: {
store: stores.join("+"),
byStore,
droppedDeadPaths: partition.dropped.length,
deadPathCount: partition.deadPaths.length,
deliveredCount: partition.live.length,
statCount: partition.statCount,
samplePaths: partition.deadPaths.slice(0, MAX_LOGGED_DEAD_PATHS).map((deadPath) => toProjectRelativePath(deadPath, cwd)),
...partition.truncated ? { truncated: true } : {}
}
});
}
function emitStaleLineDemoteRecord(cwd, scannedAtByStore, partition, byStore) {
if (partition.stale.length === 0)
return;
const stores = Object.keys(byStore);
const single = stores.length === 1 ? scannedAtByStore[stores[0]] : void 0;
logLatency({
type: "phase",
phase: "finding_stale_line_demote",
filePath: cwd,
durationMs: 0,
metadata: {
store: stores.join("+"),
byStore,
demotedStalePaths: partition.stale.length,
stalePathCount: partition.stalePaths.length,
deliveredCount: partition.live.length,
statCount: partition.statCount,
samplePaths: partition.stalePaths.slice(0, MAX_LOGGED_DEAD_PATHS).map((stalePath) => toProjectRelativePath(stalePath, cwd)),
// One store: the flat `scannedAt` field, unchanged. Several: the
// per-store map, because the whole point of the fold is that they
// differ — one flattened timestamp here would be the same authority
// collapse the gate refuses to make on the findings themselves.
...single === void 0 ? {} : { scannedAt: single },
...stores.length > 1 ? {
scannedAtByStore: Object.fromEntries(stores.map((name) => [name, scannedAtByStore[name] ?? ""]))
} : {},
...partition.truncated ? { truncated: true } : {}
}
});
}
function emitStatBudgetRecord(cwd, memo) {
if (memo.starved.size === 0)
return;
const stores = [...memo.starved.keys()].sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
logLatency({
type: "phase",
phase: "finding_path_stat_budget_exhausted",
filePath: cwd,
durationMs: 0,
metadata: {
store: stores.join("+"),
byStore: Object.fromEntries(stores.map((name) => [name, memo.starved.get(name) ?? 0])),
maxUniquePaths: memo.limit,
statCount: memo.facts.size
}
});
}
function provenanceStamp(provenance) {
if (!isWellFormed(provenance))
return "session unknown / turn unknown / generation unknown";
return `session ${provenance.revision.sessionId} / turn ${provenance.revision.turnIndex} / generation ${provenance.revision.generation}`;
}
// dist/clients/runtime-context.js
var AUTOMATION_FRAMING = "[pi-lens automated check \u2014 not a user request] ";
var MAX_LOGGED_REASONS = 8;
function boundedReasons(reasons) {
if (reasons.length <= MAX_LOGGED_REASONS)
return reasons;
return [
...reasons.slice(0, MAX_LOGGED_REASONS),
`+${reasons.length - MAX_LOGGED_REASONS} more`
];
}
function logProvenanceDecision(validation, provenance, advisoryKind, cwd) {
logLatency({
type: "phase",
phase: "advisory_provenance_decision",
filePath: cwd,
durationMs: 0,
metadata: {
decision: validation.status === "current" ? "current" : "historical",
reasons: boundedReasons(validation.reasons),
changedPathCount: validation.changedPathCount,
provenanceStamp: provenanceStamp(provenance),
advisoryKind
}
});
}
function historicalPrefix(provenance) {
return `Historical finding; workspace changed since capture; re-run to confirm. (${provenanceStamp(provenance)})`;
}
function historicalTestContent(content, provenance) {
return content.startsWith("[from a prior turn") ? content : `${historicalPrefix(provenance)}
${content}`;
}
function turnEndMessage(content, current, provenance) {
return {
role: "user",
content: current ? `${AUTOMATION_FRAMING}Address \u{1F534} blockers before continuing; \u2139\uFE0F advisories are informational only.
${content}` : `${AUTOMATION_FRAMING}${historicalPrefix(provenance)}
${content}`
};
}
function peekTurnEndFindings(cacheManager, cwd, runtime, logDelivery = false) {
const findings = cacheManager.readCache("turn-end-findings", cwd);
if (!findings?.data?.content || findings.data.consumed === true)
return;
const validation = validateAdvisoryProvenance(findings.data, cwd, runtime);
if (logDelivery)
logProvenanceDecision(validation, findings.data.provenance, "turn-end", cwd);
if (validation.allFilesDeleted)
return;
return {
messages: [
turnEndMessage(findings.data.content, validation.status === "current", findings.data.provenance)
]
};
}
function consumeTurnEndFindings(cacheManager, cwd, runtime) {
const findings = cacheManager.readCache("turn-end-findings", cwd);
if (!findings?.data?.content || findings.data.consumed === true)
return;
const validation = validateAdvisoryProvenance(findings.data, cwd, runtime);
logProvenanceDecision(validation, findings.data.provenance, "turn-end", cwd);
if (findings.data.hasBlockers === true && typeof findings.data.sessionId === "string") {
cacheManager.writeCache("turn-end-findings", { ...findings.data, consumed: true }, cwd);
} else {
cacheManager.clearCache("turn-end-findings", cwd);
}
if (validation.allFilesDeleted)
return;
return {
messages: [
turnEndMessage(findings.data.content, validation.status === "current", findings.data.provenance)
]
};
}
function peekTestFindings(cacheManager, cwd, runtime, logDelivery = false) {
const findings = cacheManager.readCache("test-runner-findings", cwd);
if (!findings?.data?.content)
return;
const validation = validateAdvisoryProvenance(findings.data, cwd, runtime);
if (logDelivery)
logProvenanceDecision(validation, findings.data.provenance, "test-findings", cwd);
if (validation.allFilesDeleted)
return;
const current = validation.status === "current";
return {
messages: [
{
role: "user",
content: current ? `${AUTOMATION_FRAMING}${testFindingsCurrentPrefix(findings.data.runnerErrorOnly)}
${findings.data.content}` : `${AUTOMATION_FRAMING}${historicalTestContent(findings.data.content, findings.data.provenance)}`
}
]
};
}
function testFindingsCurrentPrefix(runnerErrorOnly) {
return runnerErrorOnly ? "Test runner could not complete last turn (advisory \u2014 not a failure introduced this turn):" : "Test failures detected last turn \u2014 fix before continuing:";
}
function retireTestFindings(cacheManager, cwd) {
const prior = cacheManager.readCache("test-runner-findings", cwd)?.data;
cacheManager.writeCache("test-runner-findings", {
content: "",
testRunGeneration: prior?.testRunGeneration,
deferredTargets: prior?.deferredTargets,
retiredTargets: prior?.retiredTargets
}, cwd);
}
function consumeTestFindings(cacheManager, cwd, runtime) {
const record = cacheManager.readCache("test-runner-findings", cwd);
if (!record?.data?.content)
return;
const findings = peekTestFindings(cacheManager, cwd, runtime, true);
if (!findings)
return;
retireTestFindings(cacheManager, cwd);
return findings;
}
function acknowledgeTurnEndFindings(cacheManager, cwd) {
const findings = cacheManager.readCache("turn-end-findings", cwd);
if (!findings?.data?.content || findings.data.consumed === true)
return;
if (findings.data.hasBlockers === true && typeof findings.data.sessionId === "string") {
cacheManager.writeCache("turn-end-findings", { ...findings.data, consumed: true }, cwd);
} else {
cacheManager.clearCache("turn-end-findings", cwd);
}
}
function acknowledgeTestFindings(cacheManager, cwd) {
const findings = cacheManager.readCache("test-runner-findings", cwd);
if (!findings?.data?.content)
return;
retireTestFindings(cacheManager, cwd);
}
function consumeSessionStartGuidance(cacheManager, cwd, host = "pi") {
const guidance = cacheManager.readCache("session-start-guidance", cwd);
if (!guidance?.data?.content)
return;
cacheManager.writeCache("session-start-guidance", { content: "" }, cwd);
return {
messages: [
{
role: "user",
// #2535: the stored guidance names pi tools; MCP agents receive
// the registry-translated rendering. The stored record keeps
// its shape — resolution happens here, at delivery.
content: `[pi-lens automated context \u2014 not a user request]
${translateGuidanceToolNames(guidance.data.content, host)}`
}
]
};
}
export {
MAX_ADVISORY_AFFECTED_FILES,
advisoryPathKey,
snapshotAdvisoryProvenance,
validateAdvisoryProvenance,
parseScannedAtMs,
gateFindingsByPathFreshness,
AUTOMATION_FRAMING,
peekTurnEndFindings,
consumeTurnEndFindings,
peekTestFindings,
consumeTestFindings,
acknowledgeTurnEndFindings,
acknowledgeTestFindings,
consumeSessionStartGuidance
};