@inerrata-corporation/errata 2.0.16-dev.3355 → 2.0.16

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/errata.mjs CHANGED
@@ -139,7 +139,6 @@ var init_castalia = __esm({
139
139
  ROUTING_REASONS = [
140
140
  /** Harness org (CTF lane, e2e battery, canary) — forced org-private. */
141
141
  "harness-clamp",
142
- "witnessed-vulnerability-private",
143
142
  /** A signed gateway write target named the destination. */
144
143
  "signed-target-public",
145
144
  "signed-target-org",
@@ -960,7 +959,7 @@ function lemma(token) {
960
959
  }
961
960
  return token;
962
961
  }
963
- function tokenize(text, resolve7) {
962
+ function tokenize(text, resolve8) {
964
963
  const matches = text.normalize("NFC").toLowerCase().match(TOKEN_RE) ?? [];
965
964
  const out2 = /* @__PURE__ */ new Set();
966
965
  for (const raw2 of matches) {
@@ -970,7 +969,7 @@ function tokenize(text, resolve7) {
970
969
  out2.add(token);
971
970
  continue;
972
971
  }
973
- const canonical = resolve7(token);
972
+ const canonical = resolve8(token);
974
973
  out2.add(canonical ?? lemma(token));
975
974
  }
976
975
  return [...out2].sort();
@@ -1081,12 +1080,6 @@ function identityId(input) {
1081
1080
  }
1082
1081
  case "Claim":
1083
1082
  return canonicalClaimId(input.text, input.scope);
1084
- case "WitnessedVulnerability":
1085
- return genericCanonicalId("dvuln", {
1086
- workspaceId: input.workspaceId,
1087
- target: input.target.replace(/\\/g, "/").replace(/^\.\//, ""),
1088
- statement: canonicalClaimId(input.statement.trim())
1089
- });
1090
1083
  case "DesignProblem":
1091
1084
  return `${DESIGN_PROBLEM_PREFIX}${canonicalClaimId(input.statement.trim())}`.slice(
1092
1085
  0,
@@ -15856,7 +15849,7 @@ var init_edge_rules = __esm({
15856
15849
  // anchor anything at anything, e.g. at a public `Package`, quietly re-creating
15857
15850
  // the code↔collective bridge the membrane exists to prevent.
15858
15851
  ANCHORED_AT: {
15859
- from: ["Problem", "Vulnerability", "Solution", "RootCause", "Claim"],
15852
+ from: ["Problem", "Solution", "RootCause", "Claim"],
15860
15853
  to: ["Symbol"]
15861
15854
  },
15862
15855
  // ── Security spine (v1 taxonomy.ts SECURITY_FORWARD_ONLY commentary) ──
@@ -15901,7 +15894,7 @@ var init_edge_rules = __esm({
15901
15894
  // `Component` joined the target set with OM-agent-anchors: an agent-named
15902
15895
  // component ("React Router") is the same knowledge→named-unit anchor shape as
15903
15896
  // a Tool — the knowledge is ABOUT it, not dependent on it.
15904
- CONCERNS: { from: ["Problem", "Vulnerability", "Solution", "RootCause", "Claim", "Pattern"], to: ["Tool", "Component"] },
15897
+ CONCERNS: { from: ["Problem", "Solution", "RootCause", "Claim", "Pattern"], to: ["Tool", "Component"] },
15905
15898
  OPERATES_ON: { from: ["Algorithm"], to: ["DataStructure"] },
15906
15899
  INVOLVES: { from: ["Problem", "Solution"], to: ["DataStructure"] },
15907
15900
  // ── Taxonomy (Track 4 Stream C: MITRE CWE `ChildOf` → `Weakness —IS_A→ Weakness`) ──
@@ -17574,7 +17567,7 @@ var init_agents_md = __esm({
17574
17567
  });
17575
17568
 
17576
17569
  // ../../packages/context-writer/src/context-file.ts
17577
- import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
17570
+ import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, renameSync, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
17578
17571
  import { dirname as dirname2, join } from "node:path";
17579
17572
  function contextFilePath(root) {
17580
17573
  return join(root, ".errata", "context.md");
@@ -17584,7 +17577,13 @@ function writeContextFile(root, body2) {
17584
17577
  mkdirSync(dirname2(path2), { recursive: true });
17585
17578
  const text = `${body2.trim()}
17586
17579
  `;
17587
- writeFileSync2(path2, text, "utf8");
17580
+ const temp = `${path2}.${process.pid}.tmp`;
17581
+ try {
17582
+ writeFileSync2(temp, text, { encoding: "utf8", mode: 384 });
17583
+ renameSync(temp, path2);
17584
+ } finally {
17585
+ if (existsSync2(temp)) unlinkSync(temp);
17586
+ }
17588
17587
  return contextHash(text);
17589
17588
  }
17590
17589
  function contextHash(body2) {
@@ -18275,8 +18274,7 @@ CREATE INDEX IF NOT EXISTS nodes_live ON nodes(label, valid_to);
18275
18274
  -- its table row. Partial, so it maintains a few thousand rows instead of adding
18276
18275
  -- a write to every node write. Its label list is FINGERPRINT_INDEXED_LABELS.
18277
18276
  DROP INDEX IF EXISTS nodes_live_fingerprint;
18278
- DROP INDEX IF EXISTS nodes_instance_fingerprint;
18279
- CREATE INDEX IF NOT EXISTS nodes_instance_fingerprint_v2 ON nodes(label, last_updated_at, last_reinforced_at_seq, created_at_seq) WHERE valid_to IS NULL AND label IN ('Problem','Vulnerability','Solution','RootCause');
18277
+ CREATE INDEX IF NOT EXISTS nodes_instance_fingerprint ON nodes(label, last_updated_at, last_reinforced_at_seq, created_at_seq) WHERE valid_to IS NULL AND label IN ('Problem','Solution','RootCause');
18280
18278
  CREATE INDEX IF NOT EXISTS nodes_logical ON nodes(logical_id, version);
18281
18279
  CREATE INDEX IF NOT EXISTS edges_live ON edges(valid_to);
18282
18280
  -- Expression index on the per-node relPath (stored in attrs_json) so the
@@ -18290,7 +18288,7 @@ CREATE INDEX IF NOT EXISTS nodes_live_description ON nodes(description, label) W
18290
18288
  -- every edited file of every turn, and a scan parses every Problem's attrs.
18291
18289
  CREATE INDEX IF NOT EXISTS nodes_anchor_hint_path ON nodes(json_extract(attrs_json, '$.anchorHint.path')) WHERE valid_to IS NULL;
18292
18290
  `;
18293
- FINGERPRINT_INDEXED_LABELS = ["Problem", "Vulnerability", "Solution", "RootCause"];
18291
+ FINGERPRINT_INDEXED_LABELS = ["Problem", "Solution", "RootCause"];
18294
18292
  SqliteGraphStore = class {
18295
18293
  db;
18296
18294
  // Typed edge lookups build their SQL from the number of types asked for, and
@@ -18968,8 +18966,8 @@ CREATE INDEX IF NOT EXISTS nodes_anchor_hint_path ON nodes(json_extract(attrs_js
18968
18966
  const r2 = this.db.prepare(
18969
18967
  `SELECT COUNT(*) AS n, MAX(last_updated_at) AS t,
18970
18968
  MAX(COALESCE(last_reinforced_at_seq, created_at_seq, 0)) AS s
18971
- FROM nodes INDEXED BY nodes_instance_fingerprint_v2
18972
- WHERE valid_to IS NULL AND label IN ('Problem','Vulnerability','Solution','RootCause')`
18969
+ FROM nodes INDEXED BY nodes_instance_fingerprint
18970
+ WHERE valid_to IS NULL AND label IN ('Problem','Solution','RootCause')`
18973
18971
  ).get();
18974
18972
  return `${r2.n}:${r2.t ?? 0}:${r2.s ?? 0}`;
18975
18973
  }
@@ -20265,7 +20263,7 @@ function mergeContextEdge(store, from, to, type, ts) {
20265
20263
  function deriveContextFromAnchors(store, problemId, ts, pkgIndex) {
20266
20264
  const report = { languages: [], packages: [] };
20267
20265
  const p = store.getNode(problemId);
20268
- if (!p || p.label !== "Problem" && p.label !== "Vulnerability") return report;
20266
+ if (!p || p.label !== "Problem") return report;
20269
20267
  const files = store.outEdges(problemId, ["ANCHORED_AT"]).map((e) => store.getNode(e.to)).filter((n) => !!n && n.label === "File");
20270
20268
  if (files.length === 0) return report;
20271
20269
  const idx = pkgIndex ?? buildPackageIndex(store);
@@ -20903,81 +20901,6 @@ function ingestDesignProblem(store, flag, opts) {
20903
20901
  ...reopenedFromRegression ? { reopenedFromRegression } : {}
20904
20902
  };
20905
20903
  }
20906
- function ingestWitnessedVulnerability(store, flag, opts) {
20907
- const statement = flag.statement.trim();
20908
- if (isPlaceholderStatement(statement)) return { id: "", created: false, corroborated: false, upgraded: false, rejected: "statement" };
20909
- const target = flag.target?.trim().replace(/\\/g, "/").replace(/^\.\//, "") ?? "";
20910
- const file2 = target ? resolveFileNode(store, target, opts.workspaceId) : null;
20911
- const symbol2 = file2 && flag.line !== void 0 && flag.fileText !== void 0 ? resolveEnclosingSymbol(store, file2.id, flag.line, flag.fileText) : null;
20912
- const identityTarget = symbol2 ? String(symbol2.attrs["logicalId"] ?? `${target}#${symbol2.attrs["qname"] ?? symbol2.description}`) : target;
20913
- const compatibleTarget = (n) => !symbol2 || !n.attrs["identityTarget"] || n.attrs["identityTarget"] === target || n.attrs["identityTarget"] === identityTarget;
20914
- const bound = flag.existingId ? store.getNode(flag.existingId) : null;
20915
- if (flag.existingId && (!bound || bound.label !== "Vulnerability" || bound.attrs["workspaceId"] !== opts.workspaceId || target && bound.attrs["affectedTarget"] && bound.attrs["affectedTarget"] !== target || !compatibleTarget(bound))) {
20916
- return { id: "", created: false, corroborated: false, upgraded: false, rejected: "binding-conflict" };
20917
- }
20918
- const statementKey = identityId({ kind: "DesignProblem", statement });
20919
- const restated = bound ? null : store.findNodesByLabel("Vulnerability").find((n) => n.attrs["workspaceId"] === opts.workspaceId && n.attrs["affectedTarget"] === target && compatibleTarget(n) && [n.description, ...n.attrs["statementAliases"] ?? []].some((s) => identityId({ kind: "DesignProblem", statement: s }) === statementKey));
20920
- const id = bound?.id ?? restated?.id ?? identityId({ kind: "WitnessedVulnerability", workspaceId: opts.workspaceId, target: identityTarget, statement });
20921
- const existing = bound ?? store.getNode(id);
20922
- const sources = existing?.attrs["sources"] ?? [];
20923
- const corroborated = !!existing && !sources.includes(opts.source);
20924
- const upgraded = !!existing && existing.attrs["assessment"] !== "asserted" && flag.assessment === "asserted";
20925
- const assessment = existing?.attrs["assessment"] === "asserted" ? "asserted" : flag.assessment;
20926
- if (!existing) {
20927
- store.mergeNode(buildNode(store, id, "Vulnerability", statement, opts.ts, {
20928
- source: "convo",
20929
- sourceClass: "agent-observation",
20930
- privateFinding: id,
20931
- workspaceId: opts.workspaceId,
20932
- affectedTarget: target,
20933
- identityTarget,
20934
- assessment,
20935
- anchorDisposition: target ? "unresolved" : "absent",
20936
- provisional: true,
20937
- sources: [opts.source],
20938
- corroborations: 0,
20939
- ...opts.hostHarness ? { hostHarness: opts.hostHarness } : {}
20940
- }));
20941
- } else if (corroborated || upgraded || !existing.attrs["affectedTarget"] && target || symbol2 && existing.attrs["identityTarget"] !== identityTarget) {
20942
- store.updateNode(id, {
20943
- attrs: {
20944
- ...existing.attrs,
20945
- assessment,
20946
- ...upgraded && existing.description !== statement ? { statementAliases: [...existing.attrs["statementAliases"] ?? [], existing.description].slice(-5) } : {},
20947
- affectedTarget: existing.attrs["affectedTarget"] || target,
20948
- ...symbol2 ? { identityTarget } : {},
20949
- sources: corroborated ? [...sources, opts.source] : sources,
20950
- corroborations: (Number(existing.attrs["corroborations"]) || 0) + (corroborated ? 1 : 0)
20951
- },
20952
- // Assessment strengthens on evidence, but does not certify exploitability.
20953
- ...upgraded ? { description: statement } : {},
20954
- ...corroborated ? { cumulativeHits: existing.cumulativeHits + 1 } : {},
20955
- ...reinforcedSeqAttrs(store, "Vulnerability"),
20956
- lastUpdatedAt: opts.ts
20957
- });
20958
- }
20959
- return { id, created: !existing, corroborated, upgraded };
20960
- }
20961
- function recordVulnerabilityCause(store, findingId, causes, ts) {
20962
- const finding = store.getNode(findingId);
20963
- if (finding?.label !== "Vulnerability") return 0;
20964
- let from = findingId;
20965
- let count = 0;
20966
- for (const description of causes) {
20967
- if (isPlaceholderStatement(description)) continue;
20968
- const id = genericCanonicalId("dcause", { findingId, description: description.trim() });
20969
- if (!store.getNode(id)) store.mergeNode(buildNode(store, id, "RootCause", description.trim(), ts, {
20970
- source: "convo",
20971
- privateFinding: findingId,
20972
- sources: finding.attrs["sources"],
20973
- assessment: finding.attrs["assessment"]
20974
- }));
20975
- mergeEdge(store, from, id, "CAUSED_BY", ts);
20976
- from = id;
20977
- count++;
20978
- }
20979
- return count;
20980
- }
20981
20904
  function canonicalizePatternText(name2) {
20982
20905
  const stripped = name2.replace(/^\s*pattern(?:\s*[×x]\s*\d+)?\s*(?:\(exemplar\))?\s*[:—–-]\s*/i, "").replace(/\s+/g, " ").trim();
20983
20906
  return stripped.length > 0 ? stripped : name2.replace(/\s+/g, " ").trim();
@@ -21288,7 +21211,6 @@ function findReusableSolution(store, fixText) {
21288
21211
  const key = solutionTextKey(fixText);
21289
21212
  if (!key) return null;
21290
21213
  for (const s of store.findNodesByLabel("Solution")) {
21291
- if (s.attrs["privateFinding"]) continue;
21292
21214
  if (s.validTo != null) continue;
21293
21215
  if (isAutoMinted(s)) continue;
21294
21216
  if (solutionTextKey(s.description ?? "") === key) return s;
@@ -21313,7 +21235,7 @@ function priorsForFile(store, relPath) {
21313
21235
  for (const e of store.inEdges(file2.id, ["ANCHORED_AT"])) {
21314
21236
  const n = store.getNode(e.from);
21315
21237
  if (!n) continue;
21316
- if (n.label === "Problem" || n.label === "Vulnerability") {
21238
+ if (n.label === "Problem") {
21317
21239
  if (seenProblem.has(n.id)) continue;
21318
21240
  seenProblem.add(n.id);
21319
21241
  if (!n.attrs["resolvedAt"]) {
@@ -21383,8 +21305,7 @@ function solutionForProblem(store, problemId) {
21383
21305
  }
21384
21306
  function anchorProblemToWorkingFile(store, problemId, relPath, workspaceId2, ts, provenance = "edited", opts = {}) {
21385
21307
  const p = store.getNode(problemId);
21386
- if (!p || p.label !== "Problem" && p.label !== "Vulnerability") return false;
21387
- if (p.label === "Vulnerability" && provenance !== "witnessed") return false;
21308
+ if (!p || p.label !== "Problem") return false;
21388
21309
  if (!isAnchorableCodePath(relPath)) return false;
21389
21310
  const file2 = resolveFileNode(store, relPath, workspaceId2);
21390
21311
  if (!file2) return false;
@@ -21563,22 +21484,18 @@ function resolveDesignProblemById(store, id, fixNote, t) {
21563
21484
  }
21564
21485
  function closeDesignProblem(store, problemId, fixNote, t) {
21565
21486
  const p = store.getNode(problemId);
21566
- if (!p || p.label !== "Problem" && p.label !== "Vulnerability") return false;
21487
+ if (!p || p.label !== "Problem") return false;
21567
21488
  if (p.attrs["resolvedAs"]) return false;
21568
21489
  if (fixNote?.trim() && !isFragmentFixNote(fixNote)) {
21569
21490
  const sols = store.outEdges(problemId, ["SOLVED_BY"]).map((e) => store.getNode(e.to)).filter((n) => n !== null);
21570
21491
  if (sols.every((s) => isAutoMinted(s))) {
21571
- const reuse = p.label === "Vulnerability" ? null : findReusableSolution(store, fixNote);
21492
+ const reuse = findReusableSolution(store, fixNote);
21572
21493
  if (reuse) {
21573
21494
  mergeEdge(store, problemId, reuse.id, "SOLVED_BY", t);
21574
21495
  } else {
21575
21496
  const solId = `dfix_${digest({ problemId, fix: fixNote })}`.slice(0, 56);
21576
21497
  store.mergeNode(
21577
- buildNode(store, solId, "Solution", fixNote.trim(), t, {
21578
- source: "convo",
21579
- provisional: false,
21580
- ...p.label === "Vulnerability" ? { privateFinding: problemId, sources: p.attrs["sources"] } : {}
21581
- })
21498
+ buildNode(store, solId, "Solution", fixNote.trim(), t, { source: "convo", provisional: false })
21582
21499
  );
21583
21500
  mergeEdge(store, problemId, solId, "SOLVED_BY", t);
21584
21501
  }
@@ -21592,7 +21509,6 @@ function closeDesignProblem(store, problemId, fixNote, t) {
21592
21509
  // mints no Solution, and a legacy placeholder beside it reads as
21593
21510
  // all-heuristic either way.
21594
21511
  attrs: { ...p.attrs, provisional: false, resolvedAt: t, resolutionWitnessed: true },
21595
- ...reinforcedSeqAttrs(store, p.label),
21596
21512
  lastUpdatedAt: t
21597
21513
  });
21598
21514
  return true;
@@ -23944,7 +23860,7 @@ function detectLocalCommunities(store, opts = {}) {
23944
23860
  const ids = /* @__PURE__ */ new Set();
23945
23861
  for (const label of JOINT_COMMUNITY_LABELS) {
23946
23862
  for (const n of store.findNodesByLabel(label)) {
23947
- if (n.attrs["resolvedAs"] === PROBLEM_RESOLUTION.FALSE_POSITIVE) continue;
23863
+ if (label === "Problem" && n.attrs["resolvedAs"] === PROBLEM_RESOLUTION.FALSE_POSITIVE) continue;
23948
23864
  ids.add(n.id);
23949
23865
  }
23950
23866
  }
@@ -24081,7 +23997,6 @@ var init_community2 = __esm({
24081
23997
  init_src4();
24082
23998
  JOINT_COMMUNITY_LABELS = [
24083
23999
  "Problem",
24084
- "Vulnerability",
24085
24000
  "Solution",
24086
24001
  "RootCause",
24087
24002
  "Pattern"
@@ -24943,7 +24858,6 @@ __export(src_exports2, {
24943
24858
  induceAbstractions: () => induceAbstractions,
24944
24859
  induceTriage: () => induceTriage,
24945
24860
  ingestDesignProblem: () => ingestDesignProblem,
24946
- ingestWitnessedVulnerability: () => ingestWitnessedVulnerability,
24947
24861
  isConstraintProblem: () => isConstraintProblem,
24948
24862
  isFragmentFixNote: () => isFragmentFixNote,
24949
24863
  isPlaceholderStatement: () => isPlaceholderStatement,
@@ -24991,7 +24905,6 @@ __export(src_exports2, {
24991
24905
  recordMisreadPrior: () => recordMisreadPrior,
24992
24906
  recordRenderLedger: () => recordRenderLedger,
24993
24907
  recordTriageObservation: () => recordTriageObservation,
24994
- recordVulnerabilityCause: () => recordVulnerabilityCause,
24995
24908
  reinforceSameAnchorProblem: () => reinforceSameAnchorProblem,
24996
24909
  relateClaims: () => relateClaims,
24997
24910
  reopenAutoClosedProblems: () => reopenAutoClosedProblems,
@@ -25061,7 +24974,7 @@ function isPassiveInjectable(node2, atCommit) {
25061
24974
  return false;
25062
24975
  }
25063
24976
  function buildSnapshot(opts) {
25064
- const problems = [...opts.store.findNodesByLabel("Problem"), ...opts.store.findNodesByLabel("Vulnerability")].filter((p) => p.attrs["mergedInto"] === void 0 && historyReadPolicy(p, { mode: "passive", atCommit: opts.atCommit }).allow);
24977
+ const problems = opts.store.findNodesByLabel("Problem").filter((p) => p.attrs["mergedInto"] === void 0 && historyReadPolicy(p, { mode: "passive", atCommit: opts.atCommit }).allow);
25065
24978
  const stillOpen = problems.filter((p) => p.attrs["resolvedAt"] == null);
25066
24979
  const allProblems = stillOpen.filter((p) => !isConstraintProblem(p));
25067
24980
  allProblems.sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt);
@@ -25216,11 +25129,11 @@ function sliceForFile(store, relPath, atCommit) {
25216
25129
  const priors = fp ? {
25217
25130
  openProblems: fp.openProblems.slice(0, 3).map((p) => ({
25218
25131
  id: p.id,
25219
- description: p.label === "Vulnerability" ? `Vulnerability (${p.attrs["assessment"] ?? "unassessed"}): ${p.description}` : p.description,
25132
+ description: p.description,
25220
25133
  ...typeof p.attrs["fixCandidateFile"] === "string" ? { fixCandidateFile: p.attrs["fixCandidateFile"] } : {}
25221
25134
  })),
25222
25135
  constraints: fp.constraints.slice(0, 2).map((c) => ({ id: c.id, description: c.description })),
25223
- resolvedProblems: fp.resolvedProblems.slice(0, 3).map((p) => ({ id: p.id, description: p.label === "Vulnerability" ? `Vulnerability (${p.attrs["assessment"] ?? "unassessed"}): ${p.description}` : p.description })),
25136
+ resolvedProblems: fp.resolvedProblems.slice(0, 3).map((p) => ({ id: p.id, description: p.description })),
25224
25137
  related: fp.related.slice(0, 4).map((n) => ({ id: n.id, label: n.label, description: n.description })),
25225
25138
  solutionsByProblem: Object.fromEntries(
25226
25139
  [...fp.openProblems.slice(0, 3), ...fp.resolvedProblems.slice(0, 3)].map((p) => [
@@ -25338,8 +25251,7 @@ function renderSnapshot(s) {
25338
25251
  if (s.recentProblems.length > 0) {
25339
25252
  lines.push("_Open \u2014 recently observed here, derived from your work (a recency slice, not an inventory \u2014 burst an id for its full neighborhood):_");
25340
25253
  for (const r of s.recentProblems) {
25341
- const badge = r.node.label === "Vulnerability" ? `Vulnerability (${r.node.attrs["assessment"] ?? "unassessed"}): ` : "";
25342
- lines.push(`- **${badge}${r.node.description}** \u2014 \`${r.node.id}\`${tagOf(r.node)}`);
25254
+ lines.push(`- **${r.node.description}** \u2014 \`${r.node.id}\`${tagOf(r.node)}`);
25343
25255
  const hits = r.node.cumulativeHits ?? 0;
25344
25256
  const surprise = (r.node.cumulativeSurprise ?? 0).toFixed(2);
25345
25257
  lines.push(` - observed ${hits}\xD7 \xB7 cumulativeSurprise ${surprise}`);
@@ -25380,8 +25292,7 @@ function renderSnapshot(s) {
25380
25292
  if (s.recentResolved.length > 0) {
25381
25293
  lines.push("_Recently resolved \u2014 solved here; jumping-off points, not live defects:_");
25382
25294
  for (const r of s.recentResolved) {
25383
- const badge = r.node.label === "Vulnerability" ? `Vulnerability (${r.node.attrs["assessment"] ?? "unassessed"}): ` : "";
25384
- lines.push(`- \u2713 **${badge}${r.node.description}** \u2014 \`${r.node.id}\`${tagOf(r.node)}`);
25295
+ lines.push(`- \u2713 **${r.node.description}** \u2014 \`${r.node.id}\`${tagOf(r.node)}`);
25385
25296
  if (r.solution) {
25386
25297
  lines.push(` - solved: ${r.solution.description.slice(0, 160)} \u2014 \`${r.solution.id}\`${tagOf(r.solution)}`);
25387
25298
  }
@@ -27241,8 +27152,8 @@ var init_config = __esm({
27241
27152
  "use strict";
27242
27153
  init_paths();
27243
27154
  LEGACY_CLOUD_URL = "https://inerrata-gateway.onrender.com";
27244
- DEFAULT_CLOUD_URL = true ? "https://dev.inerrata.dev" : DEV_CLOUD_URL;
27245
- DEFAULT_UPDATE_CHANNEL = false ? "latest" : "dev";
27155
+ DEFAULT_CLOUD_URL = true ? "https://inerrata.dev" : DEV_CLOUD_URL;
27156
+ DEFAULT_UPDATE_CHANNEL = true ? "latest" : "dev";
27246
27157
  }
27247
27158
  });
27248
27159
 
@@ -31802,7 +31713,7 @@ var init_src8 = __esm({
31802
31713
 
31803
31714
  // src/agent-signals.ts
31804
31715
  function buildFileRecallInstruction(referent = "\u2026") {
31805
- return `Touching one? paste its token: ${TAG_EXAMPLE.fix(referent)} resolved \xB7 ${TAG_EXAMPLE.prior(referent)} leaned-on \xB7 ${TAG_EXAMPLE.problem()} new \xB7 ${TAG_EXAMPLE.vulnerability()} security. We harvest it.`;
31716
+ return `Touching one? paste its token: ${TAG_EXAMPLE.fix(referent)} resolved \xB7 ${TAG_EXAMPLE.prior(referent)} leaned-on \xB7 ${TAG_EXAMPLE.problem()} new. We harvest it.`;
31806
31717
  }
31807
31718
  function triageBullet(ref) {
31808
31719
  return [
@@ -31838,15 +31749,15 @@ function attemptBullet() {
31838
31749
  ].join("\n");
31839
31750
  }
31840
31751
  function buildAgentInstruction(opts = {}) {
31841
- const signals = opts.signals ?? ["prior", "problem", "vulnerability", "domain", "fix", "constraint", "triage", "attempt", "link"];
31752
+ const signals = opts.signals ?? ["prior", "problem", "domain", "fix", "constraint", "triage", "attempt", "link"];
31842
31753
  const ref = opts.referent ?? "its-handle";
31843
31754
  const token = (s) => s === "problem" ? TAG_EXAMPLE.problem() : s === "constraint" ? TAG_EXAMPLE.constraint() : s === "attempt" ? TAG_EXAMPLE.tried() : s === "domain" ? TAG_EXAMPLE.domain() : s === "link" ? TAG_EXAMPLE.pattern() : s === "verdict" ? TAG_EXAMPLE.confirm(ref) : TAG_EXAMPLE[s](ref);
31844
- const head2 = "Tag inline (no fences or backticks). Untagged findings are silently lost. Cite short handles; tag everything, we filter downstream. Requested errata-* answer blocks are the exception: keep their fences in your turn-final reply:";
31845
- const tail = "Restate mid-turn tags in your final reply; the harness can drop mid-turn text. EXCEPT one already shown with an id: cite [its-handle] instead of retyping (drift mints a duplicate).";
31755
+ const head2 = `We tag the priors we show you with a short handle like [${opts.handleExample ?? "chokidar-glob"}]. Untagged prose is silently lost. Put each tag inline in prose (no fences or backticks). Requested errata-* answer blocks are the exception: keep their fences in your turn-final reply. Tag everything, we filter downstream:`;
31756
+ const tail = "Mid-turn text (between tool calls) can be dropped by the harness \u2014 RESTATE every tag in your turn-final message; the final message is what reliably survives \u2014 EXCEPT one already shown above with an id: that is captured, so cite `[its-handle]` rather than retyping it (drift mints a duplicate).";
31846
31757
  return [
31847
31758
  head2,
31848
31759
  ...signals.map(
31849
- (s) => s === "triage" ? triageBullet(ref) : s === "vulnerability" ? ` \u2022 ${GLOSS.vulnerability} \u2192 ${TAG_EXAMPLE.vulnerability()}. Uncertain? ${TAG_EXAMPLE.suspectedVulnerability()}. Code review counts, even if immediately fixed. No CVE/exploit needed. For fixes: ${TAG_EXAMPLE.vulnerabilityThread()} ${TAG_EXAMPLE.fixThread("v")}.` : s === "attempt" ? attemptBullet() : s === "link" ? linkBullet(ref) : s === "verdict" ? verdictBullet(ref) : ` \u2022 ${GLOSS[s]} \u2192 ${token(s)}`
31760
+ (s) => s === "triage" ? triageBullet(ref) : s === "attempt" ? attemptBullet() : s === "link" ? linkBullet(ref) : s === "verdict" ? verdictBullet(ref) : ` \u2022 ${GLOSS[s]} \u2192 ${token(s)}`
31850
31761
  ),
31851
31762
  tail
31852
31763
  ].join("\n");
@@ -31858,9 +31769,6 @@ var init_agent_signals = __esm({
31858
31769
  TAG_EXAMPLE = {
31859
31770
  prior: (ref) => `([${ref}])`,
31860
31771
  problem: () => `[!one line on what's wrong]`,
31861
- vulnerability: () => `[!vuln: who can do what; observed basis @ path:line]`,
31862
- suspectedVulnerability: () => `[?vuln: consequence; missing check]`,
31863
- vulnerabilityThread: () => `[!#v vuln: finding here]`,
31864
31772
  fix: (ref) => `(fix:[${ref}])`,
31865
31773
  // CONSTRAINT — the native capture path for a DIFFUSE problem (a constraint/tension/
31866
31774
  // conflict that shaped a decision). Free text, attaches to the decision sentence the
@@ -31881,7 +31789,7 @@ var init_agent_signals = __esm({
31881
31789
  // tags never need one — the id is for diagnoses that unfold over distance.
31882
31790
  problemThread: () => `[!#slug one line on what's wrong]`,
31883
31791
  causeThread: () => `(cause:#slug the mechanism)`,
31884
- fixThread: (thread = "slug") => `(fix:#${thread} what you changed)`,
31792
+ fixThread: () => `(fix:#slug what you changed)`,
31885
31793
  // ATTEMPT arc — what's being tried and what didn't work. A failed attempt is a
31886
31794
  // witnessed NEGATIVE result: it saves the next agent the same dead end.
31887
31795
  tried: () => `(tried: the approach you're attempting)`,
@@ -31923,9 +31831,8 @@ var init_agent_signals = __esm({
31923
31831
  // problems, and narrated them as progress (a pick-best-before-scoring bug lost).
31924
31832
  // Every example here was diagnosis-framed, so nothing contradicted that sorting.
31925
31833
  // Kept to 29 chars: the instruction sits at the 2600 passive-budget ceiling.
31926
- problem: "any bug or finding, even in code you just wrote; name affected code: [!what's wrong @ path:42]",
31927
- vulnerability: "unauthorized actions, data exposure, or execution/disruption an attacker can trigger",
31928
- fix: "you fixed a shown finding; for your own non-security finding: (fix: what changed) \u2014 a fix resolves a stated problem, completing routine work is not a fix",
31834
+ problem: "a problem you notice or report \u2014 any bug, finding, or thing wrong, even in code you just wrote; about specific code? name the file: [!what's wrong @ src/file.ts:42]",
31835
+ fix: "your edit fixed a PROBLEM we showed you; for one you flagged yourself: (fix: what you changed) \u2014 a fix resolves a stated problem, completing routine work is not a fix",
31929
31836
  constraint: "a design decision forced by conflicting requirements or constraints \u2014 tag the tension, especially when your fix makes it 'only look' resolved",
31930
31837
  triage: "you diagnosed a cause \u2014 fold the chain into the flag with <- (caused-by), deepest sure mechanism rightmost",
31931
31838
  attempt: "attempting an approach / an attempt failed \u2014 a failed attempt is knowledge, it rules out a path",
@@ -31947,7 +31854,7 @@ var init_agent_signals = __esm({
31947
31854
  // Sim note (2026-08-02 shrink): domain emission held 4/4 with just the trigger
31948
31855
  // clause; the pattern convergence hook now rides here too (it fell 0/4 when it
31949
31856
  // only appeared in the link bullet's tail).
31950
- domain: "no single file? name the area (Title Case), and (pattern: the shape). No code anchor needed; two agents naming the same shape converge on one node",
31857
+ domain: "a problem with no single file \u2014 name the area it's about (Title Case). Also name the general shape it instantiates: (pattern: the shape) \u2014 needs no code anchor, and two agents naming the same shape converge on one node",
31951
31858
  link: "the problem you flagged is an instance of priors/abstractions you can name \u2014 (instance:[h1],[h2],\u2026) / (pattern: \u2026) / (aids:[h]); rendered whole via linkBullet",
31952
31859
  verdict: "you can judge a prior we SHOWED you \u2014 confirm it if it held, refute it (with why) if it didn't; rendered whole via verdictBullet"
31953
31860
  };
@@ -32169,7 +32076,11 @@ function contextCard(store, snapshot) {
32169
32076
  return deliveredCard(store, intent ? `${count} for \u201C${clipIntent(intent)}\u201D` : `${count} in context`, delivered);
32170
32077
  }
32171
32078
  function writeContextCard(root, hash2, card) {
32172
- writeFileSync6(join7(root, ".errata", CONTEXT_CARD_FILE), JSON.stringify({ hash: hash2, card }));
32079
+ writeFileSync6(join7(root, ".errata", CONTEXT_CARD_FILE), JSON.stringify({
32080
+ hash: hash2,
32081
+ card,
32082
+ line: card && recallCardEnabled() ? renderRecallLine(card) : null
32083
+ }));
32173
32084
  }
32174
32085
  function readContextCard(root, hash2) {
32175
32086
  const path2 = join7(root, ".errata", CONTEXT_CARD_FILE);
@@ -32184,7 +32095,7 @@ function readContextCard(root, hash2) {
32184
32095
  function openProblems(store, now = Date.now()) {
32185
32096
  const hit = openProblemsCache.get(store);
32186
32097
  if (hit && now - hit.at < OPEN_PROBLEMS_TTL_MS) return hit.nodes;
32187
- const nodes = [...store.findNodesByLabel("Problem"), ...store.findNodesByLabel("Vulnerability")].filter((p) => live(p) && !p.attrs["resolvedAt"] && p.attrs["mergedInto"] === void 0 && !isConstraintProblem(p));
32098
+ const nodes = store.findNodesByLabel("Problem").filter((p) => live(p) && !p.attrs["resolvedAt"] && p.attrs["mergedInto"] === void 0 && !isConstraintProblem(p));
32188
32099
  openProblemsCache.set(store, { at: now, nodes });
32189
32100
  return nodes;
32190
32101
  }
@@ -32634,7 +32545,7 @@ var init_process_identity = __esm({
32634
32545
  });
32635
32546
 
32636
32547
  // src/lock.ts
32637
- import { existsSync as existsSync12, readFileSync as readFileSync11, unlinkSync } from "node:fs";
32548
+ import { existsSync as existsSync12, readFileSync as readFileSync11, unlinkSync as unlinkSync2 } from "node:fs";
32638
32549
  function daemonLockPayload(lock) {
32639
32550
  return JSON.stringify({
32640
32551
  pid: lock.pid,
@@ -32715,7 +32626,7 @@ function stopLockedDaemon(lockPath) {
32715
32626
  }
32716
32627
  if (state === "unknown") return "unknown";
32717
32628
  if (state === "stale") {
32718
- unlinkSync(lockPath);
32629
+ unlinkSync2(lockPath);
32719
32630
  return "stale";
32720
32631
  }
32721
32632
  process.kill(lock.pid, "SIGTERM");
@@ -36116,13 +36027,13 @@ async function Module2(moduleArg = {}) {
36116
36027
  }
36117
36028
  readAsync = /* @__PURE__ */ __name(async (url2) => {
36118
36029
  if (isFileURI(url2)) {
36119
- return new Promise((resolve7, reject) => {
36030
+ return new Promise((resolve8, reject) => {
36120
36031
  var xhr = new XMLHttpRequest();
36121
36032
  xhr.open("GET", url2, true);
36122
36033
  xhr.responseType = "arraybuffer";
36123
36034
  xhr.onload = () => {
36124
36035
  if (xhr.status == 200 || xhr.status == 0 && xhr.response) {
36125
- resolve7(xhr.response);
36036
+ resolve8(xhr.response);
36126
36037
  return;
36127
36038
  }
36128
36039
  reject(xhr.status);
@@ -36318,9 +36229,9 @@ async function Module2(moduleArg = {}) {
36318
36229
  __name(receiveInstantiationResult, "receiveInstantiationResult");
36319
36230
  var info2 = getWasmImports();
36320
36231
  if (Module["instantiateWasm"]) {
36321
- return new Promise((resolve7, reject) => {
36232
+ return new Promise((resolve8, reject) => {
36322
36233
  Module["instantiateWasm"](info2, (mod, inst) => {
36323
- resolve7(receiveInstance(mod, inst));
36234
+ resolve8(receiveInstance(mod, inst));
36324
36235
  });
36325
36236
  });
36326
36237
  }
@@ -37651,8 +37562,8 @@ async function Module2(moduleArg = {}) {
37651
37562
  if (runtimeInitialized) {
37652
37563
  moduleRtn = Module;
37653
37564
  } else {
37654
- moduleRtn = new Promise((resolve7, reject) => {
37655
- readyPromiseResolve = resolve7;
37565
+ moduleRtn = new Promise((resolve8, reject) => {
37566
+ readyPromiseResolve = resolve8;
37656
37567
  readyPromiseReject = reject;
37657
37568
  });
37658
37569
  }
@@ -43571,9 +43482,47 @@ var init_mcp = __esm({
43571
43482
  init_reconcile();
43572
43483
  init_agent_signals();
43573
43484
  INSTRUCTIONS = [
43574
- "errata is a live code and knowledge graph, a SEPARATE DIAGNOSTIC AXIS alongside grep/read for callers, blast radius, rename lineage and prior fixes. Fall back to read/search on found:false (unresolved is not an empty answer), or for unindexed code. Confirm exact bytes before editing.",
43575
- buildAgentInstruction({ signals: ["prior", "problem", "vulnerability", "fix"], referent: "its-id" }),
43576
- "Read .errata/g/index for graph-as-files query paths. A tool result may ask for a root cause; answer via errata.annotate_resolution when known."
43485
+ // KEEP THIS UNDER THE CLIENT'S ~2 KB instruction-injection budget (the client
43486
+ // truncates `instructions` on connect, ~2008 chars observed). Capture no longer
43487
+ // depends on a standing emit-instruction block: the daemon derives problems/fixes
43488
+ // from your WORK deterministically, and the one thing it asks of the agent — a
43489
+ // one-line root cause at a fix — rides INLINE on the next tool response (the ⚡
43490
+ // errata line; see the dispatch's enrichment piggyback) instead of a fenced
43491
+ // protocol the agent has to remember. So this stays short: tool-routing steering
43492
+ // plus a single line about the inline nudge. Per-tool routing lives in each tool's
43493
+ // own `description` (delivered via tools/list). Don't re-add a tool table here.
43494
+ "errata is a LIVE, bi-temporal code graph of this workspace, kept current by a",
43495
+ "file watcher \u2014 a SEPARATE DIAGNOSTIC AXIS to run ALONGSIDE grep/glob/read, not",
43496
+ "instead of them. For STRUCTURAL questions (callers of callers, blast-radius,",
43497
+ "rename lineage) prefer it: faster, more complete, and it reaches relationships",
43498
+ "grep can't. Each tool's description says when to reach for it. Fall back to",
43499
+ 'grep/read when a tool returns found:false (ESPECIALLY reason:"unresolved" \u2014 the',
43500
+ "name was not recognized, NOT an empty answer; an empty list like callers:[] is a",
43501
+ 'confident "none" \u2014 trust it), when you need EXACT bytes before a high-stakes',
43502
+ "edit, or for an unindexed file/language. Always confirm exact bytes with a read",
43503
+ "before editing.",
43504
+ "",
43505
+ "errata is a cloud knowledge substrate \u2014 AND ALSO this workspace's code graph.",
43506
+ "The problems you hit and the fixes you make become shared knowledge other agents",
43507
+ "recall. That capture is automatic: derived from your edits/commands/commits \u2014 no",
43508
+ "emit needed. Just after you resolve something a tool result may carry a one-line",
43509
+ "\u26A1 errata note asking the ROOT CAUSE; answer via errata.annotate_resolution when",
43510
+ "you know it, skip it otherwise.",
43511
+ "",
43512
+ "Two things it CAN'T derive \u2014 only you witness them, and this is a CAPTURE",
43513
+ "PROTOCOL, not optional formatting: a problem or fix you state only in prose, a",
43514
+ "list, or your own numbering is SILENTLY LOST \u2014 the tag is the ONLY thing recorded.",
43515
+ "So the moment you state one, wrap it inline (no fence, no extra call):",
43516
+ // Signal tokens come from the single source (agent-signals.ts) so this always-on
43517
+ // block can't teach a grammar the parser rejects. Referent is "its-id" here — the
43518
+ // raw MCP channel has no primed handles, so the agent cites a tool-result id.
43519
+ ` \u2022 ${TAG_EXAMPLE.problem()} \u2014 EVERY problem you notice or report ([?\u2026] = TODO)`,
43520
+ ` \u2022 ${TAG_EXAMPLE.fix("its-id")} \u2014 your edit fixed a problem errata showed you`,
43521
+ "Tag every one you state \u2014 we filter downstream, so don't self-censor or batch.",
43522
+ "",
43523
+ "Graph-as-files: the graph is ALSO a virtual FS \u2014 Read (or `cat`)",
43524
+ ".errata/g/<verb>/<seed> to run a query as a file read (e.g. .errata/g/burst/",
43525
+ "<symbol>, /why/<sym>, /trace/<a>..<b>). Read .errata/g/index for the vocabulary."
43577
43526
  ].join("\n");
43578
43527
  AUDIT_RUN_THRESHOLD = 5;
43579
43528
  AUDIT_NUDGE_COOLDOWN = 12;
@@ -44165,7 +44114,7 @@ var init_mcp = __esm({
44165
44114
  },
44166
44115
  {
44167
44116
  name: "errata.problems",
44168
- description: "List the workspace's Problem and Vulnerability backlog \u2014 the triage view that search/why/impact can't give because they need a seed. status: 'open' (default) | 'resolved' (a real fix) | 'retracted' (false alarms \u2014 false_positive/dissolution) | 'constraint' (design TENSIONS captured via `(constraint: \u2026)` \u2014 standing context, not open work, so they're excluded from 'open') | 'all'. Also returns a `retracted` breakdown so the false-positive rate stays visible.",
44117
+ description: "List the workspace's Problem backlog \u2014 the triage view that search/why/impact can't give because they need a seed. status: 'open' (default) | 'resolved' (a real fix) | 'retracted' (false alarms \u2014 false_positive/dissolution) | 'constraint' (design TENSIONS captured via `(constraint: \u2026)` \u2014 standing context, not open work, so they're excluded from 'open') | 'all'. Also returns a `retracted` breakdown so the false-positive rate stays visible.",
44169
44118
  inputSchema: {
44170
44119
  type: "object",
44171
44120
  properties: {
@@ -44177,15 +44126,13 @@ var init_mcp = __esm({
44177
44126
  const want = String(args2["status"] ?? "open");
44178
44127
  const limit = Math.max(1, Math.min(200, Number(args2["limit"] ?? 50)));
44179
44128
  const RETRACTIONS = /* @__PURE__ */ new Set(["false_positive", "invalid", "duplicate"]);
44180
- const rows = [...store.findAllVersionsByLabel("Problem"), ...store.findAllVersionsByLabel("Vulnerability")].map((p) => {
44129
+ const rows = store.findAllVersionsByLabel("Problem").map((p) => {
44181
44130
  const ra = p.attrs["resolvedAs"];
44182
44131
  const status = ra && RETRACTIONS.has(ra) ? ra : p.attrs["resolvedAt"] != null ? "resolved" : isConstraintProblem(p) ? "constraint" : "open";
44183
44132
  const anchorEdge = store.outEdges(p.id, ["ANCHORED_AT"])[0];
44184
44133
  const anchor = anchorEdge ? store.getNode(anchorEdge.to)?.description : void 0;
44185
44134
  return {
44186
44135
  id: p.id,
44187
- label: p.label,
44188
- ...p.label === "Vulnerability" ? { assessment: p.attrs["assessment"] } : {},
44189
44136
  problem: p.description,
44190
44137
  status,
44191
44138
  createdAt: p.createdAt,
@@ -52553,8 +52500,8 @@ import {
52553
52500
  existsSync as existsSync17,
52554
52501
  readdirSync as readdirSync5,
52555
52502
  readFileSync as readFileSync16,
52556
- renameSync,
52557
- unlinkSync as unlinkSync2,
52503
+ renameSync as renameSync2,
52504
+ unlinkSync as unlinkSync3,
52558
52505
  writeFileSync as writeFileSync12
52559
52506
  } from "node:fs";
52560
52507
  import { join as join17 } from "node:path";
@@ -52573,7 +52520,7 @@ async function flushOutbox(paths, client, store) {
52573
52520
  let failed = 0;
52574
52521
  const quarantine = (abs, reason) => {
52575
52522
  try {
52576
- renameSync(abs, `${abs}.bad`);
52523
+ renameSync2(abs, `${abs}.bad`);
52577
52524
  console.warn(`[errata] outbox: quarantined ${abs} (${reason})`);
52578
52525
  } catch {
52579
52526
  }
@@ -52617,7 +52564,7 @@ async function flushOutbox(paths, client, store) {
52617
52564
  }
52618
52565
  }
52619
52566
  }
52620
- unlinkSync2(abs);
52567
+ unlinkSync3(abs);
52621
52568
  if (res.accepted === 0) uploaded++;
52622
52569
  } catch (err2) {
52623
52570
  failed++;
@@ -52750,8 +52697,7 @@ function errorRecallTerms(errorText) {
52750
52697
  }
52751
52698
  function recallLine(n, token) {
52752
52699
  const desc = (n.description ?? "").replace(/\s+/g, " ").slice(0, 120);
52753
- const label = n.label === "Vulnerability" ? `Vulnerability; ${n.attrs["assessment"] ?? "unassessed"}` : n.label;
52754
- return `- [${label}] ${desc} \u2192 ${token}`;
52700
+ return `- [${n.label}] ${desc} \u2192 ${token}`;
52755
52701
  }
52756
52702
  function recallForFile(store, relPath) {
52757
52703
  return fileRecall(store, relPath)?.context ?? null;
@@ -52772,7 +52718,7 @@ function fileRecall(store, relPath) {
52772
52718
  const shownRelated = priors.related.slice(0, 4);
52773
52719
  for (const p of shownProblems) {
52774
52720
  const hasCause = store.outEdges(p.id, ["CAUSED_BY"]).some((e) => store.getNode(e.to)?.label === "RootCause");
52775
- const causeCue = hasCause ? "" : ` \xB7 ${p.label === "Vulnerability" ? `(cause:#${p.id} the mechanism)` : TAG_EXAMPLE.triageMint()} to diagnose its cause`;
52721
+ const causeCue = hasCause ? "" : ` \xB7 ${TAG_EXAMPLE.triageMint()} to diagnose its cause`;
52776
52722
  lines.push(
52777
52723
  recallLine(p, `${TAG_EXAMPLE.fix(p.id)} if you resolved it \xB7 ${TAG_EXAMPLE.prior(p.id)} to cite${causeCue}`)
52778
52724
  );
@@ -55721,9 +55667,9 @@ var require_dispatcher_base = __commonJS({
55721
55667
  }
55722
55668
  close(callback) {
55723
55669
  if (callback === void 0) {
55724
- return new Promise((resolve7, reject) => {
55670
+ return new Promise((resolve8, reject) => {
55725
55671
  this.close((err2, data) => {
55726
- return err2 ? reject(err2) : resolve7(data);
55672
+ return err2 ? reject(err2) : resolve8(data);
55727
55673
  });
55728
55674
  });
55729
55675
  }
@@ -55761,12 +55707,12 @@ var require_dispatcher_base = __commonJS({
55761
55707
  err2 = null;
55762
55708
  }
55763
55709
  if (callback === void 0) {
55764
- return new Promise((resolve7, reject) => {
55710
+ return new Promise((resolve8, reject) => {
55765
55711
  this.destroy(err2, (err3, data) => {
55766
55712
  return err3 ? (
55767
55713
  /* istanbul ignore next: should never error */
55768
55714
  reject(err3)
55769
- ) : resolve7(data);
55715
+ ) : resolve8(data);
55770
55716
  });
55771
55717
  });
55772
55718
  }
@@ -58033,8 +57979,8 @@ var require_util2 = __commonJS({
58033
57979
  function createDeferredPromise() {
58034
57980
  let res;
58035
57981
  let rej;
58036
- const promise2 = new Promise((resolve7, reject) => {
58037
- res = resolve7;
57982
+ const promise2 = new Promise((resolve8, reject) => {
57983
+ res = resolve8;
58038
57984
  rej = reject;
58039
57985
  });
58040
57986
  return { promise: promise2, resolve: res, reject: rej };
@@ -60175,12 +60121,12 @@ upgrade: ${upgrade}\r
60175
60121
  cb();
60176
60122
  }
60177
60123
  }
60178
- const waitForDrain = () => new Promise((resolve7, reject) => {
60124
+ const waitForDrain = () => new Promise((resolve8, reject) => {
60179
60125
  assert2(callback === null);
60180
60126
  if (socket[kError]) {
60181
60127
  reject(socket[kError]);
60182
60128
  } else {
60183
- callback = resolve7;
60129
+ callback = resolve8;
60184
60130
  }
60185
60131
  });
60186
60132
  socket.on("close", onDrain).on("drain", onDrain);
@@ -60817,12 +60763,12 @@ var require_client_h2 = __commonJS({
60817
60763
  cb();
60818
60764
  }
60819
60765
  }
60820
- const waitForDrain = () => new Promise((resolve7, reject) => {
60766
+ const waitForDrain = () => new Promise((resolve8, reject) => {
60821
60767
  assert2(callback === null);
60822
60768
  if (socket[kError]) {
60823
60769
  reject(socket[kError]);
60824
60770
  } else {
60825
- callback = resolve7;
60771
+ callback = resolve8;
60826
60772
  }
60827
60773
  });
60828
60774
  h2stream.on("close", onDrain).on("drain", onDrain);
@@ -61299,16 +61245,16 @@ var require_client = __commonJS({
61299
61245
  return this[kNeedDrain] < 2;
61300
61246
  }
61301
61247
  async [kClose]() {
61302
- return new Promise((resolve7) => {
61248
+ return new Promise((resolve8) => {
61303
61249
  if (this[kSize]) {
61304
- this[kClosedResolve] = resolve7;
61250
+ this[kClosedResolve] = resolve8;
61305
61251
  } else {
61306
- resolve7(null);
61252
+ resolve8(null);
61307
61253
  }
61308
61254
  });
61309
61255
  }
61310
61256
  async [kDestroy](err2) {
61311
- return new Promise((resolve7) => {
61257
+ return new Promise((resolve8) => {
61312
61258
  const requests = this[kQueue].splice(this[kPendingIdx]);
61313
61259
  for (let i2 = 0; i2 < requests.length; i2++) {
61314
61260
  const request = requests[i2];
@@ -61319,7 +61265,7 @@ var require_client = __commonJS({
61319
61265
  this[kClosedResolve]();
61320
61266
  this[kClosedResolve] = null;
61321
61267
  }
61322
- resolve7(null);
61268
+ resolve8(null);
61323
61269
  };
61324
61270
  if (this[kHTTPContext]) {
61325
61271
  this[kHTTPContext].destroy(err2, callback);
@@ -61370,7 +61316,7 @@ var require_client = __commonJS({
61370
61316
  });
61371
61317
  }
61372
61318
  try {
61373
- const socket = await new Promise((resolve7, reject) => {
61319
+ const socket = await new Promise((resolve8, reject) => {
61374
61320
  client[kConnector]({
61375
61321
  host,
61376
61322
  hostname: hostname3,
@@ -61382,7 +61328,7 @@ var require_client = __commonJS({
61382
61328
  if (err2) {
61383
61329
  reject(err2);
61384
61330
  } else {
61385
- resolve7(socket2);
61331
+ resolve8(socket2);
61386
61332
  }
61387
61333
  });
61388
61334
  });
@@ -61718,8 +61664,8 @@ var require_pool_base = __commonJS({
61718
61664
  if (this[kQueue].isEmpty()) {
61719
61665
  await Promise.all(this[kClients].map((c) => c.close()));
61720
61666
  } else {
61721
- await new Promise((resolve7) => {
61722
- this[kClosedResolve] = resolve7;
61667
+ await new Promise((resolve8) => {
61668
+ this[kClosedResolve] = resolve8;
61723
61669
  });
61724
61670
  }
61725
61671
  }
@@ -62934,7 +62880,7 @@ var require_readable = __commonJS({
62934
62880
  if (this._readableState.closeEmitted) {
62935
62881
  return null;
62936
62882
  }
62937
- return await new Promise((resolve7, reject) => {
62883
+ return await new Promise((resolve8, reject) => {
62938
62884
  if (this[kContentLength] > limit) {
62939
62885
  this.destroy(new AbortError());
62940
62886
  }
@@ -62947,7 +62893,7 @@ var require_readable = __commonJS({
62947
62893
  if (signal?.aborted) {
62948
62894
  reject(signal.reason ?? new AbortError());
62949
62895
  } else {
62950
- resolve7(null);
62896
+ resolve8(null);
62951
62897
  }
62952
62898
  }).on("error", noop).on("data", function(chunk) {
62953
62899
  limit -= chunk.length;
@@ -62966,7 +62912,7 @@ var require_readable = __commonJS({
62966
62912
  }
62967
62913
  async function consume(stream, type) {
62968
62914
  assert2(!stream[kConsume]);
62969
- return new Promise((resolve7, reject) => {
62915
+ return new Promise((resolve8, reject) => {
62970
62916
  if (isUnusable(stream)) {
62971
62917
  const rState = stream._readableState;
62972
62918
  if (rState.destroyed && rState.closeEmitted === false) {
@@ -62983,7 +62929,7 @@ var require_readable = __commonJS({
62983
62929
  stream[kConsume] = {
62984
62930
  type,
62985
62931
  stream,
62986
- resolve: resolve7,
62932
+ resolve: resolve8,
62987
62933
  reject,
62988
62934
  length: 0,
62989
62935
  body: []
@@ -63053,18 +62999,18 @@ var require_readable = __commonJS({
63053
62999
  return buffer;
63054
63000
  }
63055
63001
  function consumeEnd(consume2) {
63056
- const { type, body: body2, resolve: resolve7, stream, length } = consume2;
63002
+ const { type, body: body2, resolve: resolve8, stream, length } = consume2;
63057
63003
  try {
63058
63004
  if (type === "text") {
63059
- resolve7(chunksDecode(body2, length));
63005
+ resolve8(chunksDecode(body2, length));
63060
63006
  } else if (type === "json") {
63061
- resolve7(JSON.parse(chunksDecode(body2, length)));
63007
+ resolve8(JSON.parse(chunksDecode(body2, length)));
63062
63008
  } else if (type === "arrayBuffer") {
63063
- resolve7(chunksConcat(body2, length).buffer);
63009
+ resolve8(chunksConcat(body2, length).buffer);
63064
63010
  } else if (type === "blob") {
63065
- resolve7(new Blob(body2, { type: stream[kContentType] }));
63011
+ resolve8(new Blob(body2, { type: stream[kContentType] }));
63066
63012
  } else if (type === "bytes") {
63067
- resolve7(chunksConcat(body2, length));
63013
+ resolve8(chunksConcat(body2, length));
63068
63014
  }
63069
63015
  consumeFinish(consume2);
63070
63016
  } catch (err2) {
@@ -63321,9 +63267,9 @@ var require_api_request = __commonJS({
63321
63267
  };
63322
63268
  function request(opts, callback) {
63323
63269
  if (callback === void 0) {
63324
- return new Promise((resolve7, reject) => {
63270
+ return new Promise((resolve8, reject) => {
63325
63271
  request.call(this, opts, (err2, data) => {
63326
- return err2 ? reject(err2) : resolve7(data);
63272
+ return err2 ? reject(err2) : resolve8(data);
63327
63273
  });
63328
63274
  });
63329
63275
  }
@@ -63546,9 +63492,9 @@ var require_api_stream = __commonJS({
63546
63492
  };
63547
63493
  function stream(opts, factory, callback) {
63548
63494
  if (callback === void 0) {
63549
- return new Promise((resolve7, reject) => {
63495
+ return new Promise((resolve8, reject) => {
63550
63496
  stream.call(this, opts, factory, (err2, data) => {
63551
- return err2 ? reject(err2) : resolve7(data);
63497
+ return err2 ? reject(err2) : resolve8(data);
63552
63498
  });
63553
63499
  });
63554
63500
  }
@@ -63833,9 +63779,9 @@ var require_api_upgrade = __commonJS({
63833
63779
  };
63834
63780
  function upgrade(opts, callback) {
63835
63781
  if (callback === void 0) {
63836
- return new Promise((resolve7, reject) => {
63782
+ return new Promise((resolve8, reject) => {
63837
63783
  upgrade.call(this, opts, (err2, data) => {
63838
- return err2 ? reject(err2) : resolve7(data);
63784
+ return err2 ? reject(err2) : resolve8(data);
63839
63785
  });
63840
63786
  });
63841
63787
  }
@@ -63927,9 +63873,9 @@ var require_api_connect = __commonJS({
63927
63873
  };
63928
63874
  function connect(opts, callback) {
63929
63875
  if (callback === void 0) {
63930
- return new Promise((resolve7, reject) => {
63876
+ return new Promise((resolve8, reject) => {
63931
63877
  connect.call(this, opts, (err2, data) => {
63932
- return err2 ? reject(err2) : resolve7(data);
63878
+ return err2 ? reject(err2) : resolve8(data);
63933
63879
  });
63934
63880
  });
63935
63881
  }
@@ -67791,7 +67737,7 @@ var require_fetch = __commonJS({
67791
67737
  function dispatch({ body: body2 }) {
67792
67738
  const url2 = requestCurrentURL(request);
67793
67739
  const agent = fetchParams.controller.dispatcher;
67794
- return new Promise((resolve7, reject) => agent.dispatch(
67740
+ return new Promise((resolve8, reject) => agent.dispatch(
67795
67741
  {
67796
67742
  path: url2.pathname + url2.search,
67797
67743
  origin: url2.origin,
@@ -67867,7 +67813,7 @@ var require_fetch = __commonJS({
67867
67813
  }
67868
67814
  }
67869
67815
  const onError = this.onError.bind(this);
67870
- resolve7({
67816
+ resolve8({
67871
67817
  status,
67872
67818
  statusText,
67873
67819
  headersList,
@@ -67913,7 +67859,7 @@ var require_fetch = __commonJS({
67913
67859
  for (let i2 = 0; i2 < rawHeaders.length; i2 += 2) {
67914
67860
  headersList.append(bufferToLowerCasedHeaderName(rawHeaders[i2]), rawHeaders[i2 + 1].toString("latin1"), true);
67915
67861
  }
67916
- resolve7({
67862
+ resolve8({
67917
67863
  status,
67918
67864
  statusText: STATUS_CODES[status],
67919
67865
  headersList,
@@ -71552,8 +71498,8 @@ var require_util8 = __commonJS({
71552
71498
  return true;
71553
71499
  }
71554
71500
  function delay(ms) {
71555
- return new Promise((resolve7) => {
71556
- setTimeout(resolve7, ms).unref();
71501
+ return new Promise((resolve8) => {
71502
+ setTimeout(resolve8, ms).unref();
71557
71503
  });
71558
71504
  }
71559
71505
  module2.exports = {
@@ -72700,8 +72646,8 @@ var init_report_render = __esm({
72700
72646
 
72701
72647
  // src/cli.ts
72702
72648
  init_src6();
72703
- import { closeSync as closeSync2, existsSync as existsSync36, openSync as openSync2, readFileSync as readFileSync38, renameSync as renameSync4, statSync as statSync10 } from "node:fs";
72704
- import { join as join44, resolve as pathResolve2 } from "node:path";
72649
+ import { closeSync as closeSync2, existsSync as existsSync37, openSync as openSync2, readFileSync as readFileSync40, renameSync as renameSync6, statSync as statSync11 } from "node:fs";
72650
+ import { join as join47, resolve as pathResolve2 } from "node:path";
72705
72651
  import { spawn as spawn5 } from "node:child_process";
72706
72652
 
72707
72653
  // src/daemon.ts
@@ -73135,7 +73081,7 @@ var responseViaResponseObject = async (res, outgoing, options = {}) => {
73135
73081
  });
73136
73082
  if (!chunk) {
73137
73083
  if (i2 === 1) {
73138
- await new Promise((resolve7) => setTimeout(resolve7));
73084
+ await new Promise((resolve8) => setTimeout(resolve8));
73139
73085
  maxReadCount = 3;
73140
73086
  continue;
73141
73087
  }
@@ -73282,10 +73228,11 @@ var serve = (options, listeningListener) => {
73282
73228
  init_cloud_auth();
73283
73229
  init_webui();
73284
73230
  init_config();
73231
+ init_lock();
73285
73232
 
73286
73233
  // src/engine.ts
73287
73234
  import { execFileSync as execFileSync5 } from "node:child_process";
73288
- import { existsSync as existsSync27, statSync as statSync8, appendFileSync as appendFileSync4, readdirSync as readdirSync10, renameSync as renameSync3, readFileSync as readFileSync31, writeFileSync as writeFileSync22 } from "node:fs";
73235
+ import { existsSync as existsSync27, statSync as statSync8, appendFileSync as appendFileSync4, readdirSync as readdirSync10, renameSync as renameSync4, readFileSync as readFileSync31, writeFileSync as writeFileSync22 } from "node:fs";
73289
73236
  import { basename as basename7, join as join35, relative as relative6, sep as sep4 } from "node:path";
73290
73237
 
73291
73238
  // src/source-watcher.ts
@@ -74022,9 +73969,9 @@ var NodeFsHandler = class {
74022
73969
  if (this.fsw.closed) {
74023
73970
  return;
74024
73971
  }
74025
- const dirname14 = sysPath.dirname(file2);
73972
+ const dirname15 = sysPath.dirname(file2);
74026
73973
  const basename9 = sysPath.basename(file2);
74027
- const parent = this.fsw._getWatchedDir(dirname14);
73974
+ const parent = this.fsw._getWatchedDir(dirname15);
74028
73975
  let prevStats = stats;
74029
73976
  if (parent.has(basename9))
74030
73977
  return;
@@ -74051,7 +73998,7 @@ var NodeFsHandler = class {
74051
73998
  prevStats = newStats2;
74052
73999
  }
74053
74000
  } catch (error48) {
74054
- this.fsw._remove(dirname14, basename9);
74001
+ this.fsw._remove(dirname15, basename9);
74055
74002
  }
74056
74003
  } else if (parent.has(basename9)) {
74057
74004
  const at = newStats.atimeMs;
@@ -74147,7 +74094,7 @@ var NodeFsHandler = class {
74147
74094
  this._addToNodeFs(path2, initialAdd, wh, depth + 1);
74148
74095
  }
74149
74096
  }).on(EV.ERROR, this._boundHandleError);
74150
- return new Promise((resolve7, reject) => {
74097
+ return new Promise((resolve8, reject) => {
74151
74098
  if (!stream)
74152
74099
  return reject();
74153
74100
  stream.once(STR_END, () => {
@@ -74156,7 +74103,7 @@ var NodeFsHandler = class {
74156
74103
  return;
74157
74104
  }
74158
74105
  const wasThrottled = throttler ? throttler.clear() : false;
74159
- resolve7(void 0);
74106
+ resolve8(void 0);
74160
74107
  previous.getChildren().filter((item2) => {
74161
74108
  return item2 !== directory && !current.has(item2);
74162
74109
  }).forEach((item2) => {
@@ -76041,7 +75988,7 @@ init_src5();
76041
75988
  import { readFileSync as readFileSync17, writeFileSync as writeFileSync13 } from "node:fs";
76042
75989
  var NEG = /n['']t|\b(?:not|never|no|none|neither|nor|unrelated|irrelevant|would|might|could|if)\b/i;
76043
75990
  var CITE_GROUP_RE = /\(((?:[^()\n]|\([^()\n]*\)){1,200})\)/g;
76044
- var HANDLE_RE = /\[([a-z0-9][\w-]{0,80})\]|((?:pat|sol|drc|dcause|dfix|dprob|dvuln|prob|claim|err)_[0-9a-f]{6,})/gi;
75991
+ var HANDLE_RE = /\[([a-z0-9][\w-]{0,80})\]|((?:pat|sol|drc|dcause|dfix|dprob|prob|claim|err)_[0-9a-f]{6,})/gi;
76045
75992
  var PLACEHOLDER_HANDLES = /* @__PURE__ */ new Set([
76046
75993
  "handle",
76047
75994
  "its-handle",
@@ -76056,7 +76003,7 @@ var PLACEHOLDER_HANDLES = /* @__PURE__ */ new Set([
76056
76003
  "b"
76057
76004
  ]);
76058
76005
  var isPlaceholderHandle = (h) => PLACEHOLDER_HANDLES.has(h.toLowerCase());
76059
- var FLAG_BODY_HANDLE_RE = /^(?:[a-z0-9]+(?:-[a-z0-9]+){2,}-(?=[0-9a-f]{0,3}\d)[0-9a-f]{4}|(?:pat|sol|drc|dcause|dfix|dprob|dvuln|prob|claim|err)_[0-9a-f]{6,})$/i;
76006
+ var FLAG_BODY_HANDLE_RE = /^(?:[a-z0-9]+(?:-[a-z0-9]+){2,}-(?=[0-9a-f]{0,3}\d)[0-9a-f]{4}|(?:pat|sol|drc|dcause|dfix|dprob|prob|claim|err)_[0-9a-f]{6,})$/i;
76060
76007
  var FIX_PREFIX = /^\s*fix:(?:#([a-z][\w-]{0,63}))?\s*/i;
76061
76008
  var CONSTRAINT_RE = /\(\s*constraint:\s*([^()\n]{8,}?)\s*\)/gi;
76062
76009
  var CAUSE_PREFIX = /^\s*cause:(?:#([a-z][\w-]{0,63}))?\s*/i;
@@ -76081,7 +76028,7 @@ var DOMAIN_RE = /\(\s*domain:\s*([^()\n]{3,}?)\s*\)/gi;
76081
76028
  var PACKAGE_RE = /\(\s*package:\s*([^()\n]{2,}?)\s*\)/gi;
76082
76029
  var COMPONENT_RE = /\(\s*component:\s*([^()\n]{2,}?)\s*\)/gi;
76083
76030
  var CAUSE_TEXT_MIN = 8;
76084
- var FLAG_RE = /\[([!?])(?:#([a-z][\w-]{0,63})\s+)?\s*((?:\[[^\]\n]*\]|[^\]\n])*?)\s*(?:@\s*([^\s\]]+?)(?::(\d+))?\s*)?\]/g;
76031
+ var FLAG_RE = /\[([!?])(?:#([a-z][\w-]{0,63})\s+)?\s*((?:\[[^\]\n]*\]|[^\]\n]){8,}?)\s*(?:@\s*([^\s\]]+?)(?::(\d+))?\s*)?\]/g;
76085
76032
  var CONSTRAINT_MIN = 8;
76086
76033
  function clauseBefore(sentence, idx) {
76087
76034
  const win = sentence.slice(Math.max(0, idx - 80), idx);
@@ -76225,7 +76172,6 @@ function cleanEmittedTags(tags) {
76225
76172
  }
76226
76173
  function emptyTagDispositions() {
76227
76174
  return {
76228
- invalidVulnerability: 0,
76229
76175
  oversizeSegments: 0,
76230
76176
  codeSpanNeutralized: 0,
76231
76177
  refuteTooShort: 0,
@@ -76238,7 +76184,6 @@ function emptyTagDispositions() {
76238
76184
  };
76239
76185
  }
76240
76186
  function addTagDispositions(a, b) {
76241
- a.invalidVulnerability += b.invalidVulnerability;
76242
76187
  a.oversizeSegments += b.oversizeSegments;
76243
76188
  a.codeSpanNeutralized += b.codeSpanNeutralized;
76244
76189
  a.refuteTooShort += b.refuteTooShort;
@@ -76252,31 +76197,12 @@ function addTagDispositions(a, b) {
76252
76197
  function parseInlineTags(text) {
76253
76198
  return parseInlineTagsWithDispositions(text).tags;
76254
76199
  }
76255
- function* captureSentences(text) {
76256
- for (const line of text.split(/\n+/)) {
76257
- if (line.length > 2e4) {
76258
- yield line;
76259
- continue;
76260
- }
76261
- const flags2 = [...line.matchAll(new RegExp(FLAG_RE.source, FLAG_RE.flags))].map((m) => ({ start: m.index, end: m.index + m[0].length }));
76262
- let flag = 0;
76263
- let start2 = 0;
76264
- for (const boundary of line.matchAll(/(?<=[.!?])\s+(?!\(fix:)/g)) {
76265
- const index = boundary.index;
76266
- while (flags2[flag] && flags2[flag].end <= index) flag++;
76267
- if (flags2[flag] && flags2[flag].start < index) continue;
76268
- yield line.slice(start2, index);
76269
- start2 = index + boundary[0].length;
76270
- }
76271
- yield line.slice(start2);
76272
- }
76273
- }
76274
76200
  function parseInlineTagsWithDispositions(text) {
76275
76201
  const out2 = [];
76276
76202
  const dispositions = emptyTagDispositions();
76277
76203
  const deFenced = text.replace(/```[\s\S]*?```/g, " ");
76278
76204
  let seqNo = -1;
76279
- for (const raw2 of captureSentences(deFenced)) {
76205
+ for (const raw2 of deFenced.split(/(?<=[.!?])\s+(?!\(fix:)|\n+/)) {
76280
76206
  seqNo++;
76281
76207
  if (raw2.length > 2e4) {
76282
76208
  dispositions.oversizeSegments++;
@@ -76502,29 +76428,6 @@ function parseInlineTagsWithDispositions(text) {
76502
76428
  while ((f = FLAG_RE.exec(sentence)) !== null) {
76503
76429
  const flagThread = f[2];
76504
76430
  const raw3 = f[3].trim();
76505
- if (/^vuln\b/i.test(raw3)) {
76506
- const typed = /^vuln:\s*([\s\S]*)$/i.exec(raw3);
76507
- const body2 = typed?.[1]?.trim() ?? "";
76508
- const parts2 = body2.split(CAUSE_ARROW).map((s) => s.trim());
76509
- const symptom = f[1] === "!" && parts2.length > 1 && !f[4] ? new RegExp(FLAG_RE.source, "i").exec(`[!${parts2[0]}]`) : null;
76510
- const statement = f[1] === "?" ? body2 : symptom?.[3]?.trim() ?? parts2[0];
76511
- if (statement.length < 8 || /^<[^>]*>$/.test(statement)) {
76512
- dispositions.invalidVulnerability++;
76513
- continue;
76514
- }
76515
- const path2 = (f[4] ?? symptom?.[4])?.trim();
76516
- const line = f[5] ?? symptom?.[5];
76517
- out2.push({
76518
- kind: "vulnerability",
76519
- statement,
76520
- assessment: f[1] === "!" ? "asserted" : "suspected",
76521
- ...f[1] === "!" && parts2.length > 1 ? { vulnerabilityCauses: parts2.slice(1).filter((s) => s.length >= CAUSE_TEXT_MIN) } : {},
76522
- ...path2 ? { location: { path: path2, ...line ? { line: Number(line) } : {} } } : {},
76523
- ...flagThread ? { threadId: flagThread } : {},
76524
- sentence: sfield
76525
- });
76526
- continue;
76527
- }
76528
76431
  if (FLAG_BODY_HANDLE_RE.test(raw3) && !f[4]) {
76529
76432
  dispositions.flagAsHandle++;
76530
76433
  out2.push({ kind: "prior", handle: raw3, sentence: sfield });
@@ -76895,7 +76798,7 @@ function harvestInlineTags(store, text, opts) {
76895
76798
  const parsed = parseInlineTagsWithDispositions(text);
76896
76799
  const tags = parsed.tags;
76897
76800
  plan.dispositions = parsed.dispositions;
76898
- const symptomSeqs = tags.filter((t) => (t.kind === "problem" || t.kind === "todo" || t.kind === "vulnerability") && t.statement).map((t) => ({ seq: t.seq ?? -1, statement: t.statement, threadId: t.threadId, kind: t.kind })).sort((a, b) => a.seq - b.seq);
76801
+ const symptomSeqs = tags.filter((t) => (t.kind === "problem" || t.kind === "todo") && t.statement).map((t) => ({ seq: t.seq ?? -1, statement: t.statement, threadId: t.threadId })).sort((a, b) => a.seq - b.seq);
76899
76802
  const bindSymptom = (seq, threadId) => {
76900
76803
  if (threadId) {
76901
76804
  const hit = symptomSeqs.find((s) => s.threadId === threadId);
@@ -76908,7 +76811,6 @@ function harvestInlineTags(store, text, opts) {
76908
76811
  if (seq == null) return { evidence: "inferred" };
76909
76812
  let best;
76910
76813
  for (const s of symptomSeqs) {
76911
- if (s.kind === "vulnerability") continue;
76912
76814
  if (s.seq <= seq) best = s;
76913
76815
  else break;
76914
76816
  }
@@ -76923,13 +76825,10 @@ function harvestInlineTags(store, text, opts) {
76923
76825
  return id;
76924
76826
  };
76925
76827
  for (const tag of tags) {
76926
- if (tag.kind === "problem" || tag.kind === "todo" || tag.kind === "vulnerability") {
76828
+ if (tag.kind === "problem" || tag.kind === "todo") {
76927
76829
  plan.problems.push({
76928
76830
  statement: tag.statement,
76929
76831
  kind: tag.kind,
76930
- ...tag.assessment ? { assessment: tag.assessment } : {},
76931
- ...tag.vulnerabilityCauses ? { vulnerabilityCauses: tag.vulnerabilityCauses } : {},
76932
- ...tag.kind === "vulnerability" && tag.seq !== void 0 ? { seq: tag.seq } : {},
76933
76832
  ...tag.location ? { location: tag.location } : {},
76934
76833
  ...tag.threadId ? { threadId: tag.threadId } : {}
76935
76834
  });
@@ -76952,7 +76851,7 @@ function harvestInlineTags(store, text, opts) {
76952
76851
  } else if (tag.threadId && !b.statement) {
76953
76852
  plan.fixes.push({ inScope: true, fixNote: tag.fixRationale, threadId: tag.threadId });
76954
76853
  } else if (b.statement && b.evidence === "witnessed") {
76955
- plan.fixes.push({ inScope: true, fixNote: tag.fixRationale, boundStatement: b.statement, ...tag.threadId ? { threadId: tag.threadId } : {} });
76854
+ plan.fixes.push({ inScope: true, fixNote: tag.fixRationale, boundStatement: b.statement });
76956
76855
  } else {
76957
76856
  plan.fixes.push({ inScope: true, fixNote: tag.fixRationale, ambiguous: true });
76958
76857
  }
@@ -77155,7 +77054,6 @@ function harvestInlineTags(store, text, opts) {
77155
77054
  }
77156
77055
  var CORROBORATABLE_LABELS = /* @__PURE__ */ new Set([
77157
77056
  "Problem",
77158
- "Vulnerability",
77159
77057
  "Solution",
77160
77058
  "RootCause",
77161
77059
  "Pattern",
@@ -77875,8 +77773,8 @@ var PassWorker = class {
77875
77773
  const id = this.nextId++;
77876
77774
  try {
77877
77775
  await this.ready;
77878
- return await new Promise((resolve7, reject) => {
77879
- this.pending.set(id, { resolve: resolve7, reject });
77776
+ return await new Promise((resolve8, reject) => {
77777
+ this.pending.set(id, { resolve: resolve8, reject });
77880
77778
  this.worker.postMessage({ id, kind, ...payload !== void 0 ? { payload } : {} });
77881
77779
  });
77882
77780
  } finally {
@@ -77910,12 +77808,12 @@ var PassWorker = class {
77910
77808
  });
77911
77809
  noteIsolateSpawned();
77912
77810
  this.worker = worker;
77913
- this.ready = new Promise((resolve7, reject) => {
77811
+ this.ready = new Promise((resolve8, reject) => {
77914
77812
  const onReady = (m) => {
77915
77813
  if (m?.ready) {
77916
77814
  worker.off("message", onReady);
77917
77815
  worker.off("error", onErr);
77918
- resolve7();
77816
+ resolve8();
77919
77817
  }
77920
77818
  };
77921
77819
  const onErr = (err2) => reject(err2);
@@ -79071,7 +78969,7 @@ function startGitSensor(gitDir, onEvent, opts = {}) {
79071
78969
  // src/git-delta.ts
79072
78970
  import { execFile } from "node:child_process";
79073
78971
  function git(repoDir, args2) {
79074
- return new Promise((resolve7, reject) => {
78972
+ return new Promise((resolve8, reject) => {
79075
78973
  execFile(
79076
78974
  "git",
79077
78975
  ["-C", repoDir, ...args2],
@@ -79082,7 +78980,7 @@ function git(repoDir, args2) {
79082
78980
  // git-sensor runs on every commit/checkout.
79083
78981
  windowsHide: true
79084
78982
  },
79085
- (err2, stdout) => err2 ? reject(err2) : resolve7(stdout)
78983
+ (err2, stdout) => err2 ? reject(err2) : resolve8(stdout)
79086
78984
  );
79087
78985
  });
79088
78986
  }
@@ -80240,12 +80138,12 @@ function headerPaths(diff) {
80240
80138
  return out2.filter((p) => p !== "/dev/null");
80241
80139
  }
80242
80140
  function git2(repoDir, args2) {
80243
- return new Promise((resolve7, reject) => {
80141
+ return new Promise((resolve8, reject) => {
80244
80142
  execFile2(
80245
80143
  "git",
80246
80144
  ["-C", repoDir, ...args2],
80247
80145
  { encoding: "utf8", maxBuffer: 64 * 1024 * 1024, windowsHide: true },
80248
- (err2, stdout) => err2 ? reject(err2) : resolve7(stdout)
80146
+ (err2, stdout) => err2 ? reject(err2) : resolve8(stdout)
80249
80147
  );
80250
80148
  });
80251
80149
  }
@@ -80518,7 +80416,7 @@ import {
80518
80416
  mkdirSync as mkdirSync7,
80519
80417
  readFileSync as readFileSync26,
80520
80418
  readdirSync as readdirSync8,
80521
- unlinkSync as unlinkSync3,
80419
+ unlinkSync as unlinkSync4,
80522
80420
  writeFileSync as writeFileSync17
80523
80421
  } from "node:fs";
80524
80422
  import { basename as basename6, join as join30 } from "node:path";
@@ -80579,7 +80477,7 @@ async function syncSkills(paths, client, seed, pins = [], techSeed) {
80579
80477
  if (!f.endsWith(".md")) continue;
80580
80478
  if (keep.has(basename6(f))) continue;
80581
80479
  try {
80582
- unlinkSync3(join30(paths.skillsDir, f));
80480
+ unlinkSync4(join30(paths.skillsDir, f));
80583
80481
  pruned++;
80584
80482
  } catch {
80585
80483
  }
@@ -80861,7 +80759,7 @@ init_paths();
80861
80759
  init_profile2();
80862
80760
 
80863
80761
  // src/witness-queue.ts
80864
- import { readFileSync as readFileSync28, renameSync as renameSync2, writeFileSync as writeFileSync19 } from "node:fs";
80762
+ import { readFileSync as readFileSync28, renameSync as renameSync3, writeFileSync as writeFileSync19 } from "node:fs";
80865
80763
  import { dirname as dirname11, join as join32 } from "node:path";
80866
80764
  var WITNESS_QUEUE_CAP = 500;
80867
80765
  var WITNESS_TTL_MS = 14 * 24 * 60 * 60 * 1e3;
@@ -80887,7 +80785,7 @@ function saveWitnessQueue(path2, queue) {
80887
80785
  try {
80888
80786
  const tmp = join32(dirname11(path2), `.${Date.now()}.witness-queue.tmp`);
80889
80787
  writeFileSync19(tmp, JSON.stringify(queue), "utf8");
80890
- renameSync2(tmp, path2);
80788
+ renameSync3(tmp, path2);
80891
80789
  } catch {
80892
80790
  }
80893
80791
  }
@@ -80934,9 +80832,9 @@ function partitionReplayable(queue, channel, isShipped) {
80934
80832
  function retireWitnesses(queue, channel, settledKeys) {
80935
80833
  return queue.filter((w) => !(w.channel === channel && settledKeys.has(w.witnessKey)));
80936
80834
  }
80937
- var WIRE_SHIPPED_LABELS = /* @__PURE__ */ new Set(["Problem", "Vulnerability", "Solution", "RootCause"]);
80835
+ var WIRE_SHIPPED_LABELS = /* @__PURE__ */ new Set(["Problem", "Solution", "RootCause"]);
80938
80836
  var MOTIF_ID_PREFIX = "motif_community_";
80939
- var CANONICAL_ID_RE = /^(?:pat|sol|drc|dcause|dfix|dprob|dvuln|prob|claim|err)_[0-9a-f]{6,}$/i;
80837
+ var CANONICAL_ID_RE = /^(?:pat|sol|drc|dcause|dfix|dprob|prob|claim|err)_[0-9a-f]{6,}$/i;
80940
80838
  function classifyWitnessTarget(nodeId, getNode) {
80941
80839
  if (nodeId.startsWith(MOTIF_ID_PREFIX)) {
80942
80840
  const exemplar = nodeId.slice(MOTIF_ID_PREFIX.length);
@@ -81506,7 +81404,7 @@ function createWatchBreaker(deps) {
81506
81404
  }
81507
81405
 
81508
81406
  // src/engine.ts
81509
- var DAEMON_VERSION = true ? "2.0.16-dev.3355" : "2.0.0-alpha.0";
81407
+ var DAEMON_VERSION = true ? "2.0.16" : "2.0.0-alpha.0";
81510
81408
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
81511
81409
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
81512
81410
  var GIT_OP_MUTE_MS = 4e3;
@@ -81519,7 +81417,7 @@ function appendIdentityAudit(path2, record2, line) {
81519
81417
  if (!record2.accepted && record2.score <= 0) return;
81520
81418
  try {
81521
81419
  if (existsSync27(path2) && statSync8(path2).size >= IDENTITY_AUDIT_MAX_BYTES) {
81522
- renameSync3(path2, `${path2}.1`);
81420
+ renameSync4(path2, `${path2}.1`);
81523
81421
  }
81524
81422
  appendFileSync4(path2, line);
81525
81423
  } catch {
@@ -82398,9 +82296,6 @@ function createWorkspaceEngine(opts) {
82398
82296
  const harvestTextsInner = async (sessionId, items) => {
82399
82297
  let minted = 0;
82400
82298
  let tagsSeen = 0;
82401
- let vulnerabilitiesSeen = 0;
82402
- let vulnerabilitiesMinted = 0;
82403
- let vulnerabilitiesSuspected = 0;
82404
82299
  let tagsRejected = 0;
82405
82300
  let tagsRestated = 0;
82406
82301
  let resolved = 0;
@@ -82631,73 +82526,10 @@ function createWorkspaceEngine(opts) {
82631
82526
  if ((toolRuns.get(sessionId)?.size ?? 0) > 0) touchedToolTurns++;
82632
82527
  const wf = workingFiles.get(sessionId);
82633
82528
  let inScopeProblemId;
82634
- const findingIds = /* @__PURE__ */ new Map();
82635
- const conflictingThreads = /* @__PURE__ */ new Set();
82636
82529
  const threads = sessionThreads.get(sessionId) ?? /* @__PURE__ */ new Map();
82637
82530
  sessionThreads.set(sessionId, threads);
82638
82531
  for (const p of plan.problems) {
82639
82532
  tagsSeen++;
82640
- if (p.kind === "vulnerability") {
82641
- vulnerabilitiesSeen++;
82642
- if (p.assessment === "suspected") vulnerabilitiesSuspected++;
82643
- let fileText;
82644
- let anchorReadFailed = false;
82645
- if (p.location) {
82646
- try {
82647
- fileText = readFileSync31(join35(opts.workspaceRoot, p.location.path), "utf8");
82648
- } catch {
82649
- anchorReadFailed = true;
82650
- }
82651
- }
82652
- const result = ingestWitnessedVulnerability(store, {
82653
- statement: p.statement,
82654
- assessment: p.assessment,
82655
- target: p.location?.path,
82656
- line: p.location?.line,
82657
- fileText,
82658
- existingId: p.threadId ? threads.get(p.threadId) ?? handleMap[p.threadId]?.id ?? store.getNode(p.threadId)?.id : void 0
82659
- }, { workspaceId: profile.id, source: sessionId, ts: t, hostHarness: turn.hostHarness });
82660
- let anchorDisposition = p.location ? "unresolved" : "absent";
82661
- if (result.rejected) {
82662
- tagsRejected++;
82663
- if (p.threadId) conflictingThreads.add(p.threadId);
82664
- } else {
82665
- findingIds.set(p.statement, result.id);
82666
- if (p.threadId) threads.set(p.threadId, result.id);
82667
- if (result.created || result.corroborated || result.upgraded) minted++;
82668
- else tagsRestated++;
82669
- if (result.created) vulnerabilitiesMinted++;
82670
- if (p.location) {
82671
- try {
82672
- const anchored = anchorProblemToWorkingFile(store, result.id, p.location.path, profile.id, t, "witnessed", { line: p.location.line, fileText });
82673
- anchorDisposition = anchored ? "resolved" : anchorReadFailed ? "error" : "unresolved";
82674
- if (anchored) deriveContextFromAnchors(store, result.id, t);
82675
- } catch {
82676
- anchorDisposition = "error";
82677
- }
82678
- }
82679
- const node2 = store.getNode(result.id);
82680
- store.updateNode(result.id, { attrs: { ...node2.attrs, anchorDisposition } });
82681
- if (p.vulnerabilityCauses) triaged += recordVulnerabilityCause(store, result.id, p.vulnerabilityCauses, t);
82682
- }
82683
- log.append({
82684
- kind: "vulnerability.witness",
82685
- workspaceId: profile.id,
82686
- ts: t,
82687
- digest: digest({ kind: "vulnerability.witness", workspaceId: profile.id, sessionId, turnId: turn.uuid, statement: p.statement, assessment: p.assessment, location: p.location }),
82688
- sessionId,
82689
- turnId: turn.uuid,
82690
- harness: turn.hostHarness,
82691
- findingId: result.id,
82692
- statement: p.statement,
82693
- assessment: p.assessment,
82694
- location: p.location,
82695
- seq: p.seq,
82696
- anchorDisposition,
82697
- disposition: result.rejected ?? (result.created ? "created" : result.upgraded ? "upgraded" : result.corroborated ? "corroborated" : "restated")
82698
- });
82699
- continue;
82700
- }
82701
82533
  const anchorPath = p.location?.path ?? editedTurnFile;
82702
82534
  const anchorProvenance = p.location?.path ? "witnessed" : "edited";
82703
82535
  const hintPath = anchorPath ? void 0 : turnFile ?? wf;
@@ -82792,10 +82624,10 @@ function createWorkspaceEngine(opts) {
82792
82624
  if (r.created || r.corroborated) {
82793
82625
  minted++;
82794
82626
  if (p.kind !== "constraint") {
82795
- sessionLastProblem.set(sessionId, r.problemId);
82796
- inScopeProblemId = r.problemId;
82627
+ sessionLastProblem.set(sessionId, designProblemId(p.statement));
82628
+ inScopeProblemId = designProblemId(p.statement);
82797
82629
  }
82798
- if (p.threadId) threads.set(p.threadId, r.problemId);
82630
+ if (p.threadId) threads.set(p.threadId, designProblemId(p.statement));
82799
82631
  try {
82800
82632
  if (anchorPath) {
82801
82633
  let enclosing = {};
@@ -82832,8 +82664,7 @@ function createWorkspaceEngine(opts) {
82832
82664
  );
82833
82665
  }
82834
82666
  for (const fx of plan.fixes) {
82835
- if (fx.threadId && conflictingThreads.has(fx.threadId)) continue;
82836
- const pid = fx.problemId ?? (fx.threadId ? threads.get(fx.threadId) : void 0) ?? (fx.boundStatement ? findingIds.get(fx.boundStatement) ?? designProblemId(fx.boundStatement) : void 0);
82667
+ const pid = fx.problemId ?? (fx.threadId ? threads.get(fx.threadId) : void 0) ?? (fx.boundStatement ? designProblemId(fx.boundStatement) : void 0);
82837
82668
  if (!pid) {
82838
82669
  if (fx.inScope) {
82839
82670
  console.warn(
@@ -82845,7 +82676,7 @@ function createWorkspaceEngine(opts) {
82845
82676
  const closed = resolveDesignProblemById(store, pid, fx.fixNote, t);
82846
82677
  if (!closed) {
82847
82678
  const n = store.getNode(pid);
82848
- if (!n || n.label !== "Problem" && n.label !== "Vulnerability" || n.attrs["resolvedAs"] != null) {
82679
+ if (!n || n.label !== "Problem" || n.attrs["resolvedAs"] != null) {
82849
82680
  console.warn(
82850
82681
  `[errata] fix tag went nowhere: ${!n ? `id ${pid.slice(0, 32)} unknown` : n.label !== "Problem" ? `${pid.slice(0, 32)} is a ${n.label}, not a Problem` : "the problem was retracted \u2014 a retraction is not fixable"} \u2014 the fix was NOT recorded`
82851
82682
  );
@@ -82864,23 +82695,10 @@ function createWorkspaceEngine(opts) {
82864
82695
  }
82865
82696
  }
82866
82697
  }
82867
- const vulnerabilityTriages = /* @__PURE__ */ new Set();
82868
- for (const tr of plan.triages) {
82869
- if (tr.threadId && conflictingThreads.has(tr.threadId)) {
82870
- vulnerabilityTriages.add(tr);
82871
- continue;
82872
- }
82873
- const id = tr.symptomProblemId ?? (tr.threadId ? threads.get(tr.threadId) : void 0) ?? (tr.symptomStatement ? findingIds.get(tr.symptomStatement) : void 0);
82874
- if (id && store.getNode(id)?.label === "Vulnerability") {
82875
- vulnerabilityTriages.add(tr);
82876
- if (tr.evidence === "witnessed" && tr.causeDescription) triaged += recordVulnerabilityCause(store, id, [tr.causeDescription], t);
82877
- }
82878
- }
82879
82698
  if (opts.sharedStore && plan.triages.length > 0) {
82880
82699
  const fallbackId = sessionLastProblem.get(sessionId);
82881
82700
  const fallbackStatement = fallbackId ? store.getNode(fallbackId)?.description : void 0;
82882
82701
  for (const tr of plan.triages) {
82883
- if (vulnerabilityTriages.has(tr)) continue;
82884
82702
  let presentingStatement = tr.symptomStatement;
82885
82703
  let evidence = tr.evidence;
82886
82704
  if (!presentingStatement && tr.threadId) {
@@ -82898,7 +82716,7 @@ function createWorkspaceEngine(opts) {
82898
82716
  evidence = "inferred";
82899
82717
  }
82900
82718
  if (!presentingStatement) continue;
82901
- const presentingId = tr.symptomProblemId ?? (tr.threadId ? threads.get(tr.threadId) : void 0) ?? designProblemId(presentingStatement);
82719
+ const presentingId = tr.symptomProblemId ?? designProblemId(presentingStatement);
82902
82720
  if (tr.causeId === presentingId) continue;
82903
82721
  try {
82904
82722
  recordTriageObservation(
@@ -82956,7 +82774,7 @@ function createWorkspaceEngine(opts) {
82956
82774
  }
82957
82775
  }
82958
82776
  for (const at of plan.attempts) {
82959
- const pid = at.problemId ?? (at.threadId ? threads.get(at.threadId) : void 0) ?? (at.boundStatement ? findingIds.get(at.boundStatement) ?? designProblemId(at.boundStatement) : void 0);
82777
+ const pid = at.problemId ?? (at.threadId ? threads.get(at.threadId) : void 0) ?? (at.boundStatement ? designProblemId(at.boundStatement) : void 0);
82960
82778
  const node2 = pid ? store.getNode(pid) : void 0;
82961
82779
  if (!pid || !node2) continue;
82962
82780
  try {
@@ -82972,7 +82790,7 @@ function createWorkspaceEngine(opts) {
82972
82790
  }
82973
82791
  }
82974
82792
  const bindPid = (tag) => {
82975
- const pid = tag.problemId ?? (tag.threadId ? threads.get(tag.threadId) : void 0) ?? (tag.boundStatement ? findingIds.get(tag.boundStatement) ?? designProblemId(tag.boundStatement) : void 0) ?? inScopeProblemId ?? sessionLastProblem.get(sessionId);
82793
+ const pid = tag.problemId ?? (tag.threadId ? threads.get(tag.threadId) : void 0) ?? (tag.boundStatement ? designProblemId(tag.boundStatement) : void 0) ?? inScopeProblemId ?? sessionLastProblem.get(sessionId);
82976
82794
  return pid && store.getNode(pid) ? pid : void 0;
82977
82795
  };
82978
82796
  const mintCiteEdge = (from, to, type, confidence, extraAttrs) => {
@@ -83126,12 +82944,7 @@ function createWorkspaceEngine(opts) {
83126
82944
  for (const [session, items2] of groups) {
83127
82945
  if (items2.length === 0) continue;
83128
82946
  try {
83129
- const wireItems = items2.map(({ nodeId, witnessKey }) => {
83130
- const node2 = store.getNode(nodeId);
83131
- const cloudId = node2?.attrs["privateFinding"] ? node2.attrs["cloudNodeId"] : void 0;
83132
- return { nodeId: typeof cloudId === "string" ? cloudId : nodeId, witnessKey };
83133
- });
83134
- const raw2 = await send(wireItems, session);
82947
+ const raw2 = await send(items2, session);
83135
82948
  const d = {
83136
82949
  recorded: raw2?.recorded ?? 0,
83137
82950
  unmatched: raw2?.unmatched ?? 0,
@@ -83144,8 +82957,8 @@ function createWorkspaceEngine(opts) {
83144
82957
  duplicate += d.duplicate;
83145
82958
  selfGated += d.selfGated;
83146
82959
  const missing = new Set(d.unmatchedIds);
83147
- for (const [index, it] of items2.entries()) {
83148
- if (missing.has(wireItems[index].nodeId)) stillUnmatched.push({ channel, ...it, sessionKey: session, counted: isShipped(it.nodeId) });
82960
+ for (const it of items2) {
82961
+ if (missing.has(it.nodeId)) stillUnmatched.push({ channel, ...it, sessionKey: session, counted: isShipped(it.nodeId) });
83149
82962
  else settled.add(it.witnessKey);
83150
82963
  }
83151
82964
  } catch (err2) {
@@ -83315,10 +83128,6 @@ function createWorkspaceEngine(opts) {
83315
83128
  // but nothing judgeable was cited.
83316
83129
  tagsVerdictNoHandle: dispositions.verdictNoHandle,
83317
83130
  tagsFlagAsHandle: dispositions.flagAsHandle,
83318
- tagsInvalidVulnerability: dispositions.invalidVulnerability,
83319
- vulnerabilitiesSeen,
83320
- vulnerabilitiesMinted,
83321
- vulnerabilitiesSuspected,
83322
83131
  unresolvedHandles,
83323
83132
  priorEdgesFlagOff,
83324
83133
  priorEdgesNoSource,
@@ -83386,7 +83195,7 @@ function createWorkspaceEngine(opts) {
83386
83195
  }
83387
83196
  turns = next.turns;
83388
83197
  nextOffset = next.nextOffset;
83389
- await new Promise((resolve7) => setImmediate(resolve7));
83198
+ await new Promise((resolve8) => setImmediate(resolve8));
83390
83199
  }
83391
83200
  };
83392
83201
  const seenMessageIds = /* @__PURE__ */ new Set();
@@ -84013,6 +83822,7 @@ B: ${b}`,
84013
83822
 
84014
83823
  // src/daemon.ts
84015
83824
  async function startDaemon(opts) {
83825
+ const processStartedAt = Date.now();
84016
83826
  const cfg = loadConfig();
84017
83827
  const merged = { ...cfg, cloudUrl: opts.cloudUrl ?? cfg.cloudUrl };
84018
83828
  if (opts.apiKey) {
@@ -84036,7 +83846,7 @@ async function startDaemon(opts) {
84036
83846
  const writeLockFile = (url2) => {
84037
83847
  writeFileSync23(
84038
83848
  engine.paths.daemonLock,
84039
- JSON.stringify({ pid: process.pid, webUiUrl: url2, startedAt: Date.now() }),
83849
+ daemonLockPayload({ pid: process.pid, webUiUrl: url2, processStartedAt, version: DAEMON_VERSION }),
84040
83850
  "utf8"
84041
83851
  );
84042
83852
  };
@@ -84085,9 +83895,9 @@ async function startDaemon(opts) {
84085
83895
  };
84086
83896
  }
84087
83897
  async function listenServer(fetchFn, port) {
84088
- return new Promise((resolve7) => {
83898
+ return new Promise((resolve8) => {
84089
83899
  const server = serve({ fetch: fetchFn, hostname: "127.0.0.1", port }, (info2) => {
84090
- resolve7({ server, port: info2.port });
83900
+ resolve8({ server, port: info2.port });
84091
83901
  });
84092
83902
  });
84093
83903
  }
@@ -84278,7 +84088,7 @@ function instanceReceiptSequence(n) {
84278
84088
  // src/multi.ts
84279
84089
  init_dist();
84280
84090
  init_src5();
84281
- import { existsSync as existsSync33, readFileSync as readFileSync35, unlinkSync as unlinkSync4, writeFileSync as writeFileSync25 } from "node:fs";
84091
+ import { existsSync as existsSync33, readFileSync as readFileSync35, unlinkSync as unlinkSync5, writeFileSync as writeFileSync25 } from "node:fs";
84282
84092
  import { resolve as pathResolve } from "node:path";
84283
84093
 
84284
84094
  // src/principle-sync.ts
@@ -84655,7 +84465,7 @@ function internalSymbolsFor(description, lexiconHas, publicIndex) {
84655
84465
 
84656
84466
  // src/instance-ingest.ts
84657
84467
  init_generalize_graph();
84658
- var INSTANCE_LABELS = ["Problem", "Vulnerability", "Solution", "RootCause"];
84468
+ var INSTANCE_LABELS = ["Problem", "Solution", "RootCause"];
84659
84469
  var INSTANCE_EDGES = ["CAUSED_BY", "SOLVED_BY"];
84660
84470
  var ANCHOR_EDGES = ["OCCURS_IN", "DEPENDS_ON", "PERTAIN_TO", "CONCERNS"];
84661
84471
  var ANCHOR_TARGET_LABELS = ["Language", "Package", "Domain", "Component"];
@@ -84790,10 +84600,6 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
84790
84600
  attrs: {
84791
84601
  scope: stripCodebaseScope(n.attrs["scope"]),
84792
84602
  ...n.attrs["kind"] ? { kind: n.attrs["kind"] } : {},
84793
- ...n.attrs["privateFinding"] ? { privateFinding: true } : {},
84794
- ...n.attrs["assessment"] ? { assessment: n.attrs["assessment"] } : {},
84795
- ...n.attrs["sourceClass"] ? { sourceClass: n.attrs["sourceClass"] } : {},
84796
- ...n.attrs["resolutionWitnessed"] ? { resolutionWitnessed: true, resolvedAt: n.attrs["resolvedAt"] } : {},
84797
84603
  ...n.attrs["resolvedAs"] ? { resolvedAs: n.attrs["resolvedAs"] } : {},
84798
84604
  ...preserved.length > 0 ? { preservedSymbols: preserved } : {},
84799
84605
  ...internal.length > 0 ? { internalSymbols: internal } : {}
@@ -84808,7 +84614,6 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
84808
84614
  const semanticEndpoints = /* @__PURE__ */ new Map();
84809
84615
  const includedByLabel = /* @__PURE__ */ new Map();
84810
84616
  const composition = {
84811
- privateTargetMissing: 0,
84812
84617
  candidates: 0,
84813
84618
  shipped: 0,
84814
84619
  cloudSourced: 0,
@@ -84854,15 +84659,11 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
84854
84659
  if (walked.has(n.id)) continue;
84855
84660
  walked.add(n.id);
84856
84661
  composition.candidates++;
84857
- if ((n.attrs["privateFinding"] || n.label === "Vulnerability") && !opts.project) {
84858
- composition.privateTargetMissing++;
84859
- continue;
84860
- }
84861
84662
  if (n.attrs["source"] === "cloud") {
84862
84663
  composition.cloudSourced++;
84863
84664
  continue;
84864
84665
  }
84865
- if (n.attrs["resolvedAs"] === PROBLEM_RESOLUTION.FALSE_POSITIVE) {
84666
+ if (label === "Problem" && n.attrs["resolvedAs"] === PROBLEM_RESOLUTION.FALSE_POSITIVE) {
84866
84667
  composition.falsePositive++;
84867
84668
  continue;
84868
84669
  }
@@ -84885,7 +84686,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
84885
84686
  composition.terminalRefused++;
84886
84687
  continue;
84887
84688
  }
84888
- if (!n.attrs["privateFinding"] && !noveltyAgainst(n, ref, noveltyOpts).ready) {
84689
+ if (!noveltyAgainst(n, ref, noveltyOpts).ready) {
84889
84690
  composition.notNovel++;
84890
84691
  continue;
84891
84692
  }
@@ -84928,7 +84729,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
84928
84729
  }
84929
84730
  if (targets.length === 0) continue;
84930
84731
  const shipped = shippedById.get(sourceId);
84931
- if (shipped && !shipped.attrs["privateFinding"]) {
84732
+ if (shipped) {
84932
84733
  const anchors = (label) => targets.filter((x) => x.t.label === label).map(
84933
84734
  (x) => label === "Language" ? langAnchor(x.t) : label === "Component" ? `component:${x.wireId}` : x.wireId
84934
84735
  ).filter((a) => a != null).sort();
@@ -85300,8 +85101,8 @@ var ConsolidateWorker = class {
85300
85101
  const id = this.nextId++;
85301
85102
  try {
85302
85103
  await this.ready;
85303
- return await new Promise((resolve7, reject) => {
85304
- this.pending.set(id, { resolve: resolve7, reject });
85104
+ return await new Promise((resolve8, reject) => {
85105
+ this.pending.set(id, { resolve: resolve8, reject });
85305
85106
  this.worker.postMessage({ ...req, id });
85306
85107
  });
85307
85108
  } finally {
@@ -85330,12 +85131,12 @@ var ConsolidateWorker = class {
85330
85131
  });
85331
85132
  noteIsolateSpawned();
85332
85133
  this.worker = worker;
85333
- this.ready = new Promise((resolve7, reject) => {
85134
+ this.ready = new Promise((resolve8, reject) => {
85334
85135
  const onReady = (m) => {
85335
85136
  if (m?.ready) {
85336
85137
  worker.off("message", onReady);
85337
85138
  worker.off("error", onErr);
85338
- resolve7();
85139
+ resolve8();
85339
85140
  }
85340
85141
  };
85341
85142
  const onErr = (err2) => {
@@ -85420,6 +85221,7 @@ init_config();
85420
85221
 
85421
85222
  // src/update.ts
85422
85223
  import { spawn as spawn2 } from "node:child_process";
85224
+ import { isAbsolute as isAbsolute5, join as join38 } from "node:path";
85423
85225
  var PACKAGE_NAME = "@inerrata-corporation/errata";
85424
85226
  function parse3(v) {
85425
85227
  const m = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z][0-9A-Za-z.-]*))?/.exec(v.trim());
@@ -85539,13 +85341,40 @@ function startUpdatePoller(opts) {
85539
85341
  };
85540
85342
  }
85541
85343
  function runNpmInstall(channel) {
85542
- return new Promise((resolve7) => {
85344
+ return new Promise((resolve8) => {
85543
85345
  const child = spawn2("npm", ["install", "-g", `${PACKAGE_NAME}@${channel}`], {
85544
85346
  stdio: "inherit",
85545
85347
  shell: true
85546
85348
  });
85547
- child.on("close", (code) => resolve7(code ?? 1));
85548
- child.on("error", () => resolve7(1));
85349
+ child.on("close", (code) => resolve8(code ?? 1));
85350
+ child.on("error", () => resolve8(1));
85351
+ });
85352
+ }
85353
+ async function runInstalledHookRefresh() {
85354
+ const root = await new Promise((resolve8) => {
85355
+ const child = spawn2("npm", ["root", "-g"], {
85356
+ shell: true,
85357
+ windowsHide: true,
85358
+ stdio: ["ignore", "pipe", "ignore"],
85359
+ timeout: 15e3
85360
+ });
85361
+ let output = "";
85362
+ child.stdout.on("data", (chunk) => {
85363
+ output += chunk;
85364
+ if (output.length > 32768) child.kill();
85365
+ });
85366
+ child.on("error", () => resolve8(""));
85367
+ child.on("close", (code) => resolve8(code === 0 ? output.trim() : ""));
85368
+ });
85369
+ if (!isAbsolute5(root)) return 1;
85370
+ return new Promise((resolve8) => {
85371
+ const child = spawn2(process.execPath, [join38(root, PACKAGE_NAME, "errata.mjs"), "install-hooks", "codex", "--refresh-existing"], {
85372
+ windowsHide: true,
85373
+ stdio: "inherit",
85374
+ timeout: 3e4
85375
+ });
85376
+ child.on("error", () => resolve8(1));
85377
+ child.on("close", (code) => resolve8(code ?? 1));
85549
85378
  });
85550
85379
  }
85551
85380
 
@@ -85560,7 +85389,7 @@ function liveCloudClient(src) {
85560
85389
  let current = null;
85561
85390
  let currentKey = "";
85562
85391
  let checkedAt = -Infinity;
85563
- const resolve7 = () => {
85392
+ const resolve8 = () => {
85564
85393
  const t = now();
85565
85394
  if (current && t - checkedAt < recheckMs) return current;
85566
85395
  checkedAt = t;
@@ -85580,7 +85409,7 @@ function liveCloudClient(src) {
85580
85409
  };
85581
85410
  return new Proxy({}, {
85582
85411
  get(_target, prop) {
85583
- const client = resolve7();
85412
+ const client = resolve8();
85584
85413
  const value = Reflect.get(client, prop, client);
85585
85414
  return typeof value === "function" ? value.bind(client) : value;
85586
85415
  }
@@ -86015,7 +85844,7 @@ async function syncHistoryRevision(opts) {
86015
85844
  changed += result.changed;
86016
85845
  remaining = result.remaining;
86017
85846
  if (!remaining || Date.now() - started2 >= (opts.budgetMs ?? 45e3)) break;
86018
- await new Promise((resolve7) => setImmediate(resolve7));
85847
+ await new Promise((resolve8) => setImmediate(resolve8));
86019
85848
  }
86020
85849
  return { changed, remaining };
86021
85850
  })();
@@ -86119,7 +85948,7 @@ init_src7();
86119
85948
  init_src();
86120
85949
  import { execFile as execFile5 } from "node:child_process";
86121
85950
  import { readFileSync as readFileSync34 } from "node:fs";
86122
- import { join as join38 } from "node:path";
85951
+ import { join as join39 } from "node:path";
86123
85952
  import { promisify as promisify3 } from "node:util";
86124
85953
 
86125
85954
  // src/history-walk.ts
@@ -86519,8 +86348,8 @@ var GitBlobReader = class {
86519
86348
  if (this.failed) return Promise.reject(this.failed);
86520
86349
  if (/[\r\n]/.test(spec)) return Promise.resolve(null);
86521
86350
  const proc = this.start();
86522
- return new Promise((resolve7, reject) => {
86523
- this.queue.push({ resolve: resolve7, reject });
86351
+ return new Promise((resolve8, reject) => {
86352
+ this.queue.push({ resolve: resolve8, reject });
86524
86353
  proc.stdin.write(`${spec}
86525
86354
  `);
86526
86355
  });
@@ -86534,14 +86363,14 @@ var GitBlobReader = class {
86534
86363
  const proc = this.proc;
86535
86364
  this.proc = null;
86536
86365
  if (!proc || proc.exitCode !== null) return Promise.resolve();
86537
- return new Promise((resolve7) => {
86366
+ return new Promise((resolve8) => {
86538
86367
  const timer = setTimeout(() => {
86539
86368
  proc.kill();
86540
- resolve7();
86369
+ resolve8();
86541
86370
  }, timeoutMs);
86542
86371
  proc.once("exit", () => {
86543
86372
  clearTimeout(timer);
86544
- resolve7();
86373
+ resolve8();
86545
86374
  });
86546
86375
  proc.stdin.end();
86547
86376
  });
@@ -86666,7 +86495,7 @@ async function defaultGit(root, args2) {
86666
86495
  function readIgnoreLines(root, profile) {
86667
86496
  let lines = [];
86668
86497
  try {
86669
- lines = readFileSync34(join38(root, ".errataignore"), "utf8").split("\n");
86498
+ lines = readFileSync34(join39(root, ".errataignore"), "utf8").split("\n");
86670
86499
  } catch {
86671
86500
  }
86672
86501
  return [...lines, ...profile.ignoreExtra ?? []].map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
@@ -87097,7 +86926,7 @@ init_cloud_auth();
87097
86926
  import { createHash as createHash19 } from "node:crypto";
87098
86927
  import { execFileSync as execFileSync6 } from "node:child_process";
87099
86928
  import { existsSync as existsSync31, writeFileSync as writeFileSync24 } from "node:fs";
87100
- import { join as join39 } from "node:path";
86929
+ import { join as join40 } from "node:path";
87101
86930
 
87102
86931
  // src/device-poll.ts
87103
86932
  var THROTTLED_POLL_MIN_WAIT_MS = 11e3;
@@ -87186,7 +87015,7 @@ async function loginOAuthDevice(opts) {
87186
87015
  let deviceSession = null;
87187
87016
  let approvedInstallation = null;
87188
87017
  while (Date.now() < deadline) {
87189
- await (opts.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms))))(waitMs);
87018
+ await (opts.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms))))(waitMs);
87190
87019
  waitMs = intervalMs;
87191
87020
  const poll = await fetchDevicePoll(fetchFn, device.token, {
87192
87021
  method: "POST",
@@ -87311,7 +87140,7 @@ async function loginOAuthLoopback(opts) {
87311
87140
  const state = randomState();
87312
87141
  const scope = opts.scope ?? DEFAULT_OAUTH_SCOPE;
87313
87142
  const { code, redirectUri, clientId } = await new Promise(
87314
- (resolve7, reject) => {
87143
+ (resolve8, reject) => {
87315
87144
  const server = createServer((req, res) => {
87316
87145
  const u = new URL(req.url ?? "/", "http://127.0.0.1");
87317
87146
  if (u.pathname !== "/callback") {
@@ -87334,7 +87163,7 @@ async function loginOAuthLoopback(opts) {
87334
87163
  res.writeHead(200, { "content-type": "text/html" });
87335
87164
  res.end("<html><body>inErrata: you're logged in. You can close this tab.</body></html>");
87336
87165
  server.close();
87337
- resolve7({ code: code2, redirectUri: `http://127.0.0.1:${port}/callback`, clientId: activeClientId });
87166
+ resolve8({ code: code2, redirectUri: `http://127.0.0.1:${port}/callback`, clientId: activeClientId });
87338
87167
  });
87339
87168
  let activeClientId = oauthClientId();
87340
87169
  server.on("error", reject);
@@ -87433,7 +87262,7 @@ async function linkThroughConsole(opts) {
87433
87262
  const deadline = Date.now() + Math.min(code.expires_in * 1e3, opts.timeoutMs ?? Number.POSITIVE_INFINITY);
87434
87263
  let intervalMs = Math.max(2, code.interval ?? 5) * 1e3;
87435
87264
  let waitMs = intervalMs;
87436
- const sleep = opts.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
87265
+ const sleep = opts.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
87437
87266
  while (Date.now() < deadline) {
87438
87267
  await sleep(waitMs);
87439
87268
  waitMs = intervalMs;
@@ -87555,7 +87384,7 @@ function applyLinkDecision(root, profile, config2, decision, now = Date.now) {
87555
87384
  delete nextProfile.projectLinkHold;
87556
87385
  let wroteIgnore = false;
87557
87386
  if (decision.writeIgnoreStarter !== false) {
87558
- const target = join39(root, ".errataignore");
87387
+ const target = join40(root, ".errataignore");
87559
87388
  if (!existsSync31(target)) {
87560
87389
  writeFileSync24(target, IGNORE_STARTER, "utf8");
87561
87390
  wroteIgnore = true;
@@ -87571,11 +87400,11 @@ init_registry();
87571
87400
  // src/adopt.ts
87572
87401
  init_git_remote();
87573
87402
  import { existsSync as existsSync32 } from "node:fs";
87574
- import { dirname as dirname12, join as join40 } from "node:path";
87403
+ import { dirname as dirname12, join as join41 } from "node:path";
87575
87404
  function findGitRoot(absPath) {
87576
87405
  let dir = absPath;
87577
87406
  for (let depth = 0; depth < 64; depth++) {
87578
- if (existsSync32(join40(dir, ".git"))) return dir;
87407
+ if (existsSync32(join41(dir, ".git"))) return dir;
87579
87408
  const parent = dirname12(dir);
87580
87409
  if (parent === dir) return null;
87581
87410
  dir = parent;
@@ -88868,7 +88697,7 @@ async function startMultiDaemon(opts = {}) {
88868
88697
  const packageAutoAll = async () => {
88869
88698
  const out2 = /* @__PURE__ */ new Map();
88870
88699
  for (const r of records) {
88871
- await new Promise((resolve7) => setImmediate(resolve7));
88700
+ await new Promise((resolve8) => setImmediate(resolve8));
88872
88701
  const done = markPass(`package-index:${r.entry.name}`);
88873
88702
  try {
88874
88703
  out2.set(
@@ -89607,7 +89436,7 @@ async function startMultiDaemon(opts = {}) {
89607
89436
  clearTimeout(codexWakeBoot);
89608
89437
  try {
89609
89438
  const cur = readFileSync35(lockPath, "utf8");
89610
- if (JSON.parse(cur).pid === process.pid) unlinkSync4(lockPath);
89439
+ if (JSON.parse(cur).pid === process.pid) unlinkSync5(lockPath);
89611
89440
  } catch {
89612
89441
  }
89613
89442
  await new Promise(
@@ -90030,7 +89859,7 @@ import { spawnSync } from "node:child_process";
90030
89859
 
90031
89860
  // src/codex-runtime.ts
90032
89861
  import { readdirSync as readdirSync12, statSync as statSync9 } from "node:fs";
90033
- import { join as join41 } from "node:path";
89862
+ import { join as join42 } from "node:path";
90034
89863
  function resolveCodexExecutable(options = {}) {
90035
89864
  const platform = options.platform ?? process.platform;
90036
89865
  const env2 = options.env ?? process.env;
@@ -90045,13 +89874,13 @@ function resolveCodexExecutable(options = {}) {
90045
89874
  };
90046
89875
  for (const dir of (env2["PATH"] ?? env2["Path"] ?? "").split(";")) {
90047
89876
  if (!dir.trim()) continue;
90048
- const path2 = join41(dir.trim().replace(/^"|"$/g, ""), "codex.exe");
89877
+ const path2 = join42(dir.trim().replace(/^"|"$/g, ""), "codex.exe");
90049
89878
  if (fileTime(path2) !== null) return path2;
90050
89879
  }
90051
89880
  if (!env2["LOCALAPPDATA"]) return "codex";
90052
- const root = join41(env2["LOCALAPPDATA"], "OpenAI", "Codex", "bin");
89881
+ const root = join42(env2["LOCALAPPDATA"], "OpenAI", "Codex", "bin");
90053
89882
  try {
90054
- const candidates = readdirSync12(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join41(root, entry.name, "codex.exe")).map((path2) => ({ path: path2, mtime: fileTime(path2) })).filter((candidate) => candidate.mtime !== null).sort((a, b) => b.mtime - a.mtime || a.path.localeCompare(b.path));
89883
+ const candidates = readdirSync12(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join42(root, entry.name, "codex.exe")).map((path2) => ({ path: path2, mtime: fileTime(path2) })).filter((candidate) => candidate.mtime !== null).sort((a, b) => b.mtime - a.mtime || a.path.localeCompare(b.path));
90055
89884
  return candidates[0]?.path ?? "codex";
90056
89885
  } catch {
90057
89886
  return "codex";
@@ -90167,12 +89996,14 @@ async function diagnoseInstallation(cfg, dependencies) {
90167
89996
 
90168
89997
  // src/codex-doctor.ts
90169
89998
  import { spawn as spawn4 } from "node:child_process";
89999
+ import { readFileSync as readFileSync37, statSync as statSync10 } from "node:fs";
90000
+ import { join as join44 } from "node:path";
90170
90001
  import { createInterface } from "node:readline";
90171
90002
 
90172
90003
  // src/codex-hooks.ts
90173
- import { existsSync as existsSync34, mkdirSync as mkdirSync9, readFileSync as readFileSync36, writeFileSync as writeFileSync26 } from "node:fs";
90004
+ import { existsSync as existsSync34, mkdirSync as mkdirSync9, readFileSync as readFileSync36, renameSync as renameSync5, unlinkSync as unlinkSync6, writeFileSync as writeFileSync26 } from "node:fs";
90174
90005
  import { homedir as homedir7 } from "node:os";
90175
- import { join as join42 } from "node:path";
90006
+ import { join as join43 } from "node:path";
90176
90007
 
90177
90008
  // src/hook-commands.ts
90178
90009
  function hookCurlCommand(port, path2 = "/api/hook") {
@@ -90211,9 +90042,12 @@ function buildCodexHooksToml(port, options) {
90211
90042
 
90212
90043
  // src/codex-hook-relay.ts
90213
90044
  var CODEX_RELAY_SOURCE = String.raw`// errata-managed: regenerated by errata install-hooks codex
90214
- import { readFileSync, statSync } from 'node:fs';
90045
+ import { mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from 'node:fs';
90046
+ import { createHash } from 'node:crypto';
90215
90047
  import { join } from 'node:path';
90216
90048
  const workspaceRoot = null;
90049
+ // Stdout closing early is not delivery. Never stamp a receipt on EPIPE.
90050
+ process.stdout.on('error', () => process.exit(0));
90217
90051
 
90218
90052
  try {
90219
90053
  const payload = JSON.parse(readFileSync(0, 'utf8') || '{}');
@@ -90225,6 +90059,19 @@ try {
90225
90059
  if (!Number.isInteger(port) || port < 1 || port > 65535) process.exit(0);
90226
90060
  const contexts = [];
90227
90061
  const messages = [];
90062
+ const hash = (text) => createHash('sha256').update(text).digest('hex');
90063
+ const boundedRead = (path, max) => statSync(path).size <= max ? readFileSync(path, 'utf8') : '';
90064
+ const stateDir = workspaceRoot && join(workspaceRoot, '.errata', 'codex-hook-state');
90065
+ const statePath = stateDir && typeof payload.session_id === 'string' && payload.session_id
90066
+ ? join(stateDir, hash(payload.session_id) + '.json') : null;
90067
+ let previous = {};
90068
+ try {
90069
+ if (statePath) {
90070
+ const saved = JSON.parse(boundedRead(statePath, 4096));
90071
+ if (saved && typeof saved === 'object') previous = saved;
90072
+ }
90073
+ } catch {}
90074
+ let receipt = null;
90228
90075
  const post = async (endpoint, relay = false) => {
90229
90076
  try {
90230
90077
  const response = await fetch('http://127.0.0.1:' + port + '/api/' + endpoint, {
@@ -90244,33 +90091,52 @@ try {
90244
90091
  if (event === 'PreToolUse' || event === 'PostToolUse') await post('hook', true);
90245
90092
  // Stop sees the final assistant message, including turns with no tools.
90246
90093
  if (event === 'PostToolUse' || event === 'Stop') await post('turn');
90247
- if (['SessionStart', 'UserPromptSubmit', 'PostToolUse'].includes(event)) {
90248
- // Run after capture, so changes already rendered by the daemon arrive now.
90249
- // Background extraction finishing later arrives at the next tool boundary.
90250
- const available = await post('context-inject', true);
90251
- // Startup/resume/compaction must not depend on the daemon's busy event loop.
90252
- // Read only the installer's resolved workspace, never a caller-supplied path
90253
- // or tracked AGENTS.md. SessionStart already re-injects unconditionally;
90254
- // prompt/tool refreshes retain daemon hash deduplication (no repeated copies).
90255
- if (!available && event === 'SessionStart' && workspaceRoot) {
90256
- try {
90257
- const path = join(workspaceRoot, '.errata', 'context.md');
90258
- if (statSync(path).size <= 128 * 1024) {
90259
- const body = readFileSync(path, 'utf8').trim();
90260
- if (body) {
90261
- contexts.push('Errata loaded the maintained local workspace context while the daemon response was unavailable. This block REPLACES any earlier errata context block in this conversation. It is the last saved snapshot; live updates resume when the daemon responds.\n\n' + body);
90262
- if (!/^(0|off|false|no)$/i.test((process.env.ERRATA_RECALL_CARD || '').trim())) messages.push('⚡ errata · saved workspace priors loaded');
90263
- }
90094
+ if (['SessionStart', 'UserPromptSubmit'].includes(event)) {
90095
+ // Wake/route the engine, bounded independently of context delivery. Older
90096
+ // daemons may stamp their HTTP dedup gate before a timed-out response arrives;
90097
+ // that gate is deliberately NOT authoritative for this file-based relay.
90098
+ await post('context-inject', !workspaceRoot);
90099
+ }
90100
+ if (workspaceRoot && ['SessionStart', 'UserPromptSubmit', 'PostToolUse'].includes(event)) {
90101
+ let body = '';
90102
+ try { body = boundedRead(join(workspaceRoot, '.errata', 'context.md'), 128 * 1024).trim(); } catch {}
90103
+ if (body) {
90104
+ const digest = hash(body.replace(/\r\n?/g, '\n')).slice(0, 16);
90105
+ // Receipt is local to this graph + session, survives daemon restarts, and
90106
+ // records stdout emission, not a server's intention to send a response.
90107
+ if (event === 'SessionStart' || previous.hash !== digest) {
90108
+ contexts.push('Errata loaded the maintained local workspace context. This block REPLACES any earlier errata context block in this conversation. It is the latest saved snapshot; the daemon maintains it as work proceeds.\n\n' + body);
90109
+ if (!/^(0|off|false|no)$/i.test((process.env.ERRATA_RECALL_CARD || '').trim())) {
90110
+ try {
90111
+ const card = JSON.parse(boundedRead(join(workspaceRoot, '.errata', 'context-card.json'), 128 * 1024));
90112
+ if (card.hash === digest && typeof card.line === 'string' && card.line) messages.push(card.line);
90113
+ } catch { /* Never show a card for a different snapshot. */ }
90264
90114
  }
90265
- } catch { /* No saved context is honest; never substitute another project. */ }
90115
+ receipt = { hash: digest, at: Date.now() };
90116
+ }
90117
+ } else if (event === 'SessionStart' || !previous.unavailable) {
90118
+ messages.push('errata: workspace priors are unavailable; run errata doctor in this checkout.');
90119
+ receipt = { unavailable: true, at: Date.now() };
90266
90120
  }
90121
+ } else if (!workspaceRoot && event === 'PostToolUse') {
90122
+ await post('context-inject', true);
90267
90123
  }
90268
90124
  if (contexts.length || messages.length) {
90269
- process.stdout.write(JSON.stringify({
90125
+ await new Promise((resolve) => process.stdout.write(JSON.stringify({
90270
90126
  ...(messages.length ? { systemMessage: messages.join('\n\n') } : {}),
90271
90127
  ...(contexts.length ? { hookSpecificOutput: {
90272
90128
  hookEventName: event, additionalContext: contexts.join('\n\n'),
90273
90129
  } } : {}),
90130
+ }), (error) => {
90131
+ if (!error && receipt && statePath) {
90132
+ try {
90133
+ mkdirSync(stateDir, { recursive: true });
90134
+ const temp = statePath + '.' + process.pid + '.tmp';
90135
+ writeFileSync(temp, JSON.stringify(receipt), { mode: 0o600 });
90136
+ renameSync(temp, statePath);
90137
+ } catch { /* Read-only workspace: retry next time rather than drop context. */ }
90138
+ }
90139
+ resolve();
90274
90140
  }));
90275
90141
  }
90276
90142
  } catch { /* Invalid input is also fail-open. */ }
@@ -90282,8 +90148,35 @@ function buildCodexRelaySource(workspaceRoot) {
90282
90148
  // src/codex-hooks.ts
90283
90149
  init_checkout_root();
90284
90150
  var CODEX_HOOK_REVIEW_HELP = "Codex desktop: Settings > Hooks, select this project, expand each event and Trust the Errata hook. CLI users can use /hooks.";
90285
- function hasCodexConfig(root, codexHome = process.env["CODEX_HOME"] || join42(homedir7(), ".codex")) {
90286
- return existsSync34(join42(root, ".codex")) || existsSync34(codexHome);
90151
+ function writeRelay(file2, source) {
90152
+ if (existsSync34(file2) && readFileSync36(file2, "utf8") === source) return;
90153
+ const temp = `${file2}.${process.pid}.tmp`;
90154
+ try {
90155
+ writeFileSync26(temp, source, { encoding: "utf8", mode: 384 });
90156
+ renameSync5(temp, file2);
90157
+ } finally {
90158
+ if (existsSync34(temp)) unlinkSync6(temp);
90159
+ }
90160
+ }
90161
+ function refreshCodexRelay(root) {
90162
+ const config2 = join43(root, ".codex", "config.toml");
90163
+ if (!existsSync34(config2)) return "absent";
90164
+ const existing = readFileSync36(config2, "utf8");
90165
+ if (!existing.includes(CODEX_HOOKS_BEGIN) && !existing.includes(CODEX_HOOKS_END)) return "absent";
90166
+ replaceCodexHooks(existing, "");
90167
+ const relay = join43(root, ".codex", "hooks", "errata-relay.mjs");
90168
+ const prior = existsSync34(relay) ? readFileSync36(relay, "utf8") : "";
90169
+ if (prior && !prior.startsWith("// errata-managed: regenerated by errata install-hooks codex")) {
90170
+ throw new Error("Codex relay is not Errata-managed; preserved it. Run errata install-hooks codex to explicitly replace it.");
90171
+ }
90172
+ const source = buildCodexRelaySource(checkoutRootOf(root));
90173
+ if (prior === source) return "current";
90174
+ mkdirSync9(join43(root, ".codex", "hooks"), { recursive: true });
90175
+ writeRelay(relay, source);
90176
+ return "updated";
90177
+ }
90178
+ function hasCodexConfig(root, codexHome = process.env["CODEX_HOME"] || join43(homedir7(), ".codex")) {
90179
+ return existsSync34(join43(root, ".codex")) || existsSync34(codexHome);
90287
90180
  }
90288
90181
  function replaceCodexHooks(existing, block) {
90289
90182
  const begin = existing.indexOf(CODEX_HOOKS_BEGIN);
@@ -90299,20 +90192,43 @@ function replaceCodexHooks(existing, block) {
90299
90192
  return existing.trimEnd() + (existing.trim() ? "\n\n" : "") + block;
90300
90193
  }
90301
90194
  function writeCodexHooks(root, port, nodePath = process.execPath) {
90302
- const dir = join42(root, ".codex");
90303
- const file2 = join42(dir, "config.toml");
90304
- const relay = join42(dir, "hooks", "errata-relay.mjs");
90195
+ const dir = join43(root, ".codex");
90196
+ const file2 = join43(dir, "config.toml");
90197
+ const relay = join43(dir, "hooks", "errata-relay.mjs");
90305
90198
  const block = buildCodexHooksToml(port, { nodePath, relayPath: relay });
90306
90199
  const final = replaceCodexHooks(existsSync34(file2) ? readFileSync36(file2, "utf8") : "", block);
90307
- mkdirSync9(join42(dir, "hooks"), { recursive: true });
90308
- writeFileSync26(relay, buildCodexRelaySource(checkoutRootOf(root)), "utf8");
90309
- writeFileSync26(file2, final, "utf8");
90200
+ mkdirSync9(join43(dir, "hooks"), { recursive: true });
90201
+ writeRelay(relay, buildCodexRelaySource(checkoutRootOf(root)));
90202
+ if (!existsSync34(file2) || readFileSync36(file2, "utf8") !== final) writeFileSync26(file2, final, "utf8");
90310
90203
  return file2;
90311
90204
  }
90312
90205
 
90313
90206
  // src/codex-doctor.ts
90207
+ init_checkout_root();
90208
+ function inspectCodexRelay(cwd) {
90209
+ const root = nearestGitRoot(cwd) ?? cwd;
90210
+ try {
90211
+ const source = readFileSync37(join44(root, ".codex", "hooks", "errata-relay.mjs"), "utf8");
90212
+ if (source === buildCodexRelaySource(checkoutRootOf(root))) {
90213
+ return { id: "codex_relay", status: "pass", detail: "Installed Codex relay matches this Errata version" };
90214
+ }
90215
+ } catch {
90216
+ }
90217
+ return { id: "codex_relay", status: "fail", detail: "Codex relay is missing or outdated; run errata install-hooks codex --refresh-existing, or errata install-hooks codex for a new setup" };
90218
+ }
90219
+ function inspectCodexContext(cwd) {
90220
+ const root = checkoutRootOf(nearestGitRoot(cwd) ?? cwd);
90221
+ try {
90222
+ const file2 = join44(root, ".errata", "context.md");
90223
+ if (statSync10(file2).size <= 128 * 1024 && readFileSync37(file2, "utf8").trim()) {
90224
+ return { id: "codex_context", status: "pass", detail: "Saved workspace context is readable by the relay; this does not prove live daemon freshness" };
90225
+ }
90226
+ } catch {
90227
+ }
90228
+ return { id: "codex_context", status: "fail", detail: "No usable saved workspace context; run errata start here and check the daemon log if context.md remains unavailable" };
90229
+ }
90314
90230
  function readCodexHookState(cwd, options = {}) {
90315
- return new Promise((resolve7, reject) => {
90231
+ return new Promise((resolve8, reject) => {
90316
90232
  const child = spawn4(options.bin ?? resolveCodexExecutable(), options.args ?? ["app-server", "--stdio"], {
90317
90233
  cwd,
90318
90234
  windowsHide: true,
@@ -90328,9 +90244,14 @@ function readCodexHookState(cwd, options = {}) {
90328
90244
  settled = true;
90329
90245
  clearTimeout(timer);
90330
90246
  lines.close();
90247
+ const reap = setTimeout(() => child.kill("SIGKILL"), 1e3);
90248
+ reap.unref();
90249
+ child.once("close", () => {
90250
+ clearTimeout(reap);
90251
+ if (error48) reject(error48);
90252
+ else resolve8({ ...hooks, features });
90253
+ });
90331
90254
  child.kill();
90332
- if (error48) reject(error48);
90333
- else resolve7({ ...hooks, features });
90334
90255
  };
90335
90256
  const timer = setTimeout(() => finish(new Error("Codex hook inspection timed out")), options.timeoutMs ?? 1e4);
90336
90257
  const send = (id, method, params) => {
@@ -90395,10 +90316,11 @@ function assessCodexHooks(state, platform = process.platform) {
90395
90316
  return checks.length ? checks : check2("pass", "Codex discovers enabled, trusted Errata hooks; verify live context and capture in a session");
90396
90317
  }
90397
90318
  async function diagnoseCodexHooks(cwd) {
90319
+ const local = [inspectCodexRelay(cwd), inspectCodexContext(cwd)];
90398
90320
  try {
90399
- return assessCodexHooks(await readCodexHookState(cwd));
90321
+ return [...local, ...assessCodexHooks(await readCodexHookState(cwd))];
90400
90322
  } catch (error48) {
90401
- return [{ id: "codex_hooks", status: "fail", detail: error48 instanceof Error ? error48.message : "Cannot verify Codex hooks" }];
90323
+ return [...local, { id: "codex_hooks", status: "fail", detail: error48 instanceof Error ? error48.message : "Cannot verify Codex hooks" }];
90402
90324
  }
90403
90325
  }
90404
90326
 
@@ -90472,10 +90394,51 @@ function consolidationGapMs(lastPassMs, policy) {
90472
90394
  return Math.max(policy.baseFloorMs, lastPassMs * policy.dutyFactor);
90473
90395
  }
90474
90396
 
90397
+ // src/codex-hook-refresh.ts
90398
+ init_checkout_root();
90399
+ import { existsSync as existsSync35, readdirSync as readdirSync13, readFileSync as readFileSync38 } from "node:fs";
90400
+ import { dirname as dirname13, join as join45, resolve as resolve7 } from "node:path";
90401
+ init_registry();
90402
+ function codexHookRoots(roots) {
90403
+ const found = new Set(roots.map((root) => resolve7(root)));
90404
+ for (const root of [...found]) {
90405
+ const main2 = mainCheckoutOfWorktree(root) ?? root;
90406
+ found.add(main2);
90407
+ const dir = join45(main2, ".git", "worktrees");
90408
+ if (!existsSync35(dir)) continue;
90409
+ let names;
90410
+ try {
90411
+ names = readdirSync13(dir);
90412
+ } catch {
90413
+ continue;
90414
+ }
90415
+ for (const name2 of names) {
90416
+ try {
90417
+ const checkout = dirname13(readFileSync38(join45(dir, name2, "gitdir"), "utf8").trim());
90418
+ if (mainCheckoutOfWorktree(checkout) === main2) found.add(checkout);
90419
+ } catch {
90420
+ }
90421
+ }
90422
+ }
90423
+ return [...found];
90424
+ }
90425
+ function refreshInstalledCodexRelays(roots = listWorkspaceEntries().map((entry) => entry.path)) {
90426
+ const results = [];
90427
+ for (const root of codexHookRoots(roots)) {
90428
+ try {
90429
+ const status = refreshCodexRelay(root);
90430
+ if (status !== "absent") results.push({ root, status });
90431
+ } catch (error48) {
90432
+ results.push({ root, status: "error", detail: error48 instanceof Error ? error48.message : String(error48) });
90433
+ }
90434
+ }
90435
+ return results;
90436
+ }
90437
+
90475
90438
  // src/claude-statusline.ts
90476
- import { existsSync as existsSync35, mkdirSync as mkdirSync10, readFileSync as readFileSync37, writeFileSync as writeFileSync27 } from "node:fs";
90439
+ import { existsSync as existsSync36, mkdirSync as mkdirSync10, readFileSync as readFileSync39, writeFileSync as writeFileSync27 } from "node:fs";
90477
90440
  import { homedir as homedir8 } from "node:os";
90478
- import { dirname as dirname13, join as join43 } from "node:path";
90441
+ import { dirname as dirname14, join as join46 } from "node:path";
90479
90442
  var STATUSLINE_RELAY_SOURCE = String.raw`// errata-managed: regenerated by errata init / install-hooks claude
90480
90443
  import { readFileSync } from 'node:fs';
90481
90444
 
@@ -90508,8 +90471,8 @@ function removeProjectStatusLine(settings) {
90508
90471
  return true;
90509
90472
  }
90510
90473
  function userClaudeSettingsPath() {
90511
- const base = process.env["CLAUDE_CONFIG_DIR"] ?? join43(homedir8(), ".claude");
90512
- return join43(base, "settings.json");
90474
+ const base = process.env["CLAUDE_CONFIG_DIR"] ?? join46(homedir8(), ".claude");
90475
+ return join46(base, "settings.json");
90513
90476
  }
90514
90477
  function ensureUserStatusLine(opts) {
90515
90478
  const env2 = opts.env ?? process.env;
@@ -90517,9 +90480,9 @@ function ensureUserStatusLine(opts) {
90517
90480
  if (off === "0" || off === "off" || off === "false" || off === "no") return "disabled";
90518
90481
  const settingsPath = opts.userSettingsPath ?? userClaudeSettingsPath();
90519
90482
  let settings = {};
90520
- if (existsSync35(settingsPath)) {
90483
+ if (existsSync36(settingsPath)) {
90521
90484
  try {
90522
- const parsed = JSON.parse(readFileSync37(settingsPath, "utf8"));
90485
+ const parsed = JSON.parse(readFileSync39(settingsPath, "utf8"));
90523
90486
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return "unreadable";
90524
90487
  settings = parsed;
90525
90488
  } catch {
@@ -90527,15 +90490,15 @@ function ensureUserStatusLine(opts) {
90527
90490
  }
90528
90491
  }
90529
90492
  if (settings["statusLine"] !== void 0 && !isErrataStatusLine(settings["statusLine"])) return "kept-own";
90530
- const relay = join43(opts.relayDir, STATUSLINE_RELAY_FILE);
90493
+ const relay = join46(opts.relayDir, STATUSLINE_RELAY_FILE);
90531
90494
  mkdirSync10(opts.relayDir, { recursive: true });
90532
- if (!existsSync35(relay) || readFileSync37(relay, "utf8") !== STATUSLINE_RELAY_SOURCE) {
90495
+ if (!existsSync36(relay) || readFileSync39(relay, "utf8") !== STATUSLINE_RELAY_SOURCE) {
90533
90496
  writeFileSync27(relay, STATUSLINE_RELAY_SOURCE, "utf8");
90534
90497
  }
90535
90498
  const next = buildStatusLine(opts.port, opts.nodePath ?? process.execPath, relay);
90536
90499
  if (JSON.stringify(settings["statusLine"]) === JSON.stringify(next)) return "unchanged";
90537
90500
  settings["statusLine"] = next;
90538
- mkdirSync10(dirname13(settingsPath), { recursive: true });
90501
+ mkdirSync10(dirname14(settingsPath), { recursive: true });
90539
90502
  writeFileSync27(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
90540
90503
  return "installed";
90541
90504
  }
@@ -90959,14 +90922,14 @@ async function cmdInit() {
90959
90922
  if (!skipHooks) {
90960
90923
  console.log("");
90961
90924
  console.log("installing harness hooks...");
90962
- const { existsSync: existsSync37 } = await import("node:fs");
90963
- const { join: join45 } = await import("node:path");
90925
+ const { existsSync: existsSync38 } = await import("node:fs");
90926
+ const { join: join48 } = await import("node:path");
90964
90927
  try {
90965
90928
  await installClaudeHooks(port);
90966
90929
  } catch (err2) {
90967
90930
  console.warn(` \u26A0\uFE0F Claude hook install failed: ${err2 instanceof Error ? err2.message : err2}`);
90968
90931
  }
90969
- if (existsSync37(join45(ROOT, ".cursor"))) {
90932
+ if (existsSync38(join48(ROOT, ".cursor"))) {
90970
90933
  try {
90971
90934
  await installCursorMcpConfig();
90972
90935
  } catch (err2) {
@@ -91050,6 +91013,7 @@ async function cmdNotifyTest() {
91050
91013
  }
91051
91014
  }
91052
91015
  async function cmdStart() {
91016
+ refreshExistingCodexHooks();
91053
91017
  if (!loadProfile(ROOT)) {
91054
91018
  const cfg = loadConfig();
91055
91019
  const locator = detectRepoLocator(ROOT);
@@ -91159,9 +91123,9 @@ async function cmdStatus() {
91159
91123
  console.log(` stack: ${profile.stack.join(", ") || "(none)"}`);
91160
91124
  console.log(` languages: ${profile.languages.join(", ") || "(none)"}`);
91161
91125
  }
91162
- console.log(` graph db: ${existsSync36(paths.castalia) ? "yes" : "no"} (${paths.castalia})`);
91163
- console.log(` event log: ${existsSync36(paths.eventLog) ? "yes" : "no"} (${paths.eventLog})`);
91164
- if (existsSync36(paths.castalia)) {
91126
+ console.log(` graph db: ${existsSync37(paths.castalia) ? "yes" : "no"} (${paths.castalia})`);
91127
+ console.log(` event log: ${existsSync37(paths.eventLog) ? "yes" : "no"} (${paths.eventLog})`);
91128
+ if (existsSync37(paths.castalia)) {
91165
91129
  try {
91166
91130
  const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src5(), src_exports2));
91167
91131
  const store = openGraphStore2({ path: paths.castalia });
@@ -91497,6 +91461,9 @@ async function cmdUpdate(args2) {
91497
91461
  `);
91498
91462
  const code = await runNpmInstall(channel);
91499
91463
  if (code === 0) {
91464
+ if (await runInstalledHookRefresh() !== 0) {
91465
+ console.warn("[!] Package updated, but some Codex relays could not be refreshed. Run `errata install-hooks codex --refresh-existing`; daemon startup also retries.");
91466
+ }
91500
91467
  console.log(`
91501
91468
  [OK] updated to ${status.latest}. Restart the daemon to run it:`);
91502
91469
  console.log(` errata stop (autostart relaunches it at next logon, or run 'errata dash')`);
@@ -92129,11 +92096,11 @@ async function cmdInstallationProfile(args2) {
92129
92096
  }
92130
92097
  async function cmdReview() {
92131
92098
  const paths = workspacePaths(ROOT);
92132
- if (!existsSync36(paths.reviewQueue)) {
92099
+ if (!existsSync37(paths.reviewQueue)) {
92133
92100
  console.log("(review queue empty)");
92134
92101
  return;
92135
92102
  }
92136
- const queue = JSON.parse(readFileSync38(paths.reviewQueue, "utf8"));
92103
+ const queue = JSON.parse(readFileSync40(paths.reviewQueue, "utf8"));
92137
92104
  if (queue.length === 0) {
92138
92105
  console.log("(review queue empty)");
92139
92106
  return;
@@ -92804,7 +92771,7 @@ async function gatherRepo(store, ws) {
92804
92771
  };
92805
92772
  }
92806
92773
  async function gatherReportData(generatedAt) {
92807
- const { existsSync: existsSync37 } = await import("node:fs");
92774
+ const { existsSync: existsSync38 } = await import("node:fs");
92808
92775
  const { openGraphStore: openGraphStore2 } = await Promise.resolve().then(() => (init_src5(), src_exports2));
92809
92776
  const cfg = loadConfig();
92810
92777
  const outbound = cfg.consent.sync ? "auto" : "off";
@@ -92812,7 +92779,7 @@ async function gatherReportData(generatedAt) {
92812
92779
  for (const ws of listWorkspaces()) {
92813
92780
  if (ws.missing) continue;
92814
92781
  const dbPath = workspacePaths(ws.path).castalia;
92815
- if (!existsSync37(dbPath)) continue;
92782
+ if (!existsSync38(dbPath)) continue;
92816
92783
  let store = null;
92817
92784
  try {
92818
92785
  store = openGraphStore2({ path: dbPath });
@@ -92856,8 +92823,8 @@ async function cmdReport(args2) {
92856
92823
  const outDir = workspacePaths(ROOT).configDir;
92857
92824
  mkdirSync11(outDir, { recursive: true });
92858
92825
  const files = renderReport2(data, { includeFutureVerbs });
92859
- for (const f of files) writeFileSync28(join44(outDir, f.name), f.html, "utf8");
92860
- const indexPath = join44(outDir, "report.html");
92826
+ for (const f of files) writeFileSync28(join47(outDir, f.name), f.html, "utf8");
92827
+ const indexPath = join47(outDir, "report.html");
92861
92828
  console.log(`report \u2192 ${indexPath}`);
92862
92829
  console.log(
92863
92830
  ` ${data.repos.length} repo(s) \xB7 ${files.length} file(s) \xB7 ${data.rollup.nodes.toLocaleString("en-US")} nodes`
@@ -92901,7 +92868,7 @@ function spawnDaemonDetached(options = {}) {
92901
92868
  try {
92902
92869
  const logPath = daemonLogPath();
92903
92870
  try {
92904
- if (statSync10(logPath).size > 5 * 1024 * 1024) renameSync4(logPath, `${logPath}.1`);
92871
+ if (statSync11(logPath).size > 5 * 1024 * 1024) renameSync6(logPath, `${logPath}.1`);
92905
92872
  } catch {
92906
92873
  }
92907
92874
  out2 = openSync2(logPath, "a");
@@ -92951,6 +92918,10 @@ async function cmdInstallHooks(args2) {
92951
92918
  await installCursorMcpConfig();
92952
92919
  return;
92953
92920
  case "codex":
92921
+ if (args2.includes("--refresh-existing")) {
92922
+ if (!refreshExistingCodexHooks()) process.exitCode = 1;
92923
+ return;
92924
+ }
92954
92925
  await installCodexHooks(port);
92955
92926
  return;
92956
92927
  case "opencode":
@@ -92966,24 +92937,24 @@ async function cmdInstallHooks(args2) {
92966
92937
  }
92967
92938
  }
92968
92939
  async function installOpencodePlugin(port) {
92969
- const { mkdirSync: mkdirSync11, existsSync: existsSync37, readFileSync: readFileSync39, writeFileSync: writeFileSync28 } = await import("node:fs");
92970
- const { join: join45 } = await import("node:path");
92971
- const dir = join45(ROOT, ".opencode", "plugin");
92972
- if (!existsSync37(dir)) mkdirSync11(dir, { recursive: true });
92973
- const file2 = join45(dir, "inerrata.js");
92974
- if (existsSync37(file2)) {
92975
- const head2 = readFileSync39(file2, "utf8").split("\n")[0] ?? "";
92940
+ const { mkdirSync: mkdirSync11, existsSync: existsSync38, readFileSync: readFileSync41, writeFileSync: writeFileSync28 } = await import("node:fs");
92941
+ const { join: join48 } = await import("node:path");
92942
+ const dir = join48(ROOT, ".opencode", "plugin");
92943
+ if (!existsSync38(dir)) mkdirSync11(dir, { recursive: true });
92944
+ const file2 = join48(dir, "inerrata.js");
92945
+ if (existsSync38(file2)) {
92946
+ const head2 = readFileSync41(file2, "utf8").split("\n")[0] ?? "";
92976
92947
  if (head2.trim() !== OPENCODE_PLUGIN_MARKER) {
92977
92948
  console.error(`refusing to overwrite ${file2}: not an errata-managed plugin (line 1 lacks the marker)`);
92978
92949
  process.exit(2);
92979
92950
  }
92980
92951
  }
92981
92952
  writeFileSync28(file2, buildOpencodePluginSource(port, { version: DAEMON_VERSION }), "utf8");
92982
- const cfgFile = join45(ROOT, "opencode.json");
92953
+ const cfgFile = join48(ROOT, "opencode.json");
92983
92954
  let cfg = { $schema: "https://opencode.ai/config.json" };
92984
- if (existsSync37(cfgFile)) {
92955
+ if (existsSync38(cfgFile)) {
92985
92956
  try {
92986
- cfg = JSON.parse(readFileSync39(cfgFile, "utf8"));
92957
+ cfg = JSON.parse(readFileSync41(cfgFile, "utf8"));
92987
92958
  } catch {
92988
92959
  console.error(`refusing to touch invalid JSON at ${cfgFile} \u2014 fix it and re-run`);
92989
92960
  process.exit(2);
@@ -93021,15 +92992,15 @@ function ensureStatusLine(port) {
93021
92992
  }
93022
92993
  }
93023
92994
  async function installClaudeHooks(port) {
93024
- const { mkdirSync: mkdirSync11, existsSync: existsSync37, readFileSync: readFileSync39, writeFileSync: writeFileSync28 } = await import("node:fs");
93025
- const { join: join45 } = await import("node:path");
93026
- const dir = join45(ROOT, ".claude");
93027
- if (!existsSync37(dir)) mkdirSync11(dir, { recursive: true });
93028
- const file2 = join45(dir, "settings.json");
92995
+ const { mkdirSync: mkdirSync11, existsSync: existsSync38, readFileSync: readFileSync41, writeFileSync: writeFileSync28 } = await import("node:fs");
92996
+ const { join: join48 } = await import("node:path");
92997
+ const dir = join48(ROOT, ".claude");
92998
+ if (!existsSync38(dir)) mkdirSync11(dir, { recursive: true });
92999
+ const file2 = join48(dir, "settings.json");
93029
93000
  let settings = {};
93030
- if (existsSync37(file2)) {
93001
+ if (existsSync38(file2)) {
93031
93002
  try {
93032
- settings = JSON.parse(readFileSync39(file2, "utf8"));
93003
+ settings = JSON.parse(readFileSync41(file2, "utf8"));
93033
93004
  } catch {
93034
93005
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
93035
93006
  process.exit(2);
@@ -93081,7 +93052,7 @@ async function installClaudeHooks(port) {
93081
93052
  console.log(`installed Claude Code hooks \u2192 ${file2}`);
93082
93053
  console.log(` ${ensureStatusLine(port)}`);
93083
93054
  await installClaudeMcpConfig();
93084
- const claudeMd = join45(ROOT, "CLAUDE.md");
93055
+ const claudeMd = join48(ROOT, "CLAUDE.md");
93085
93056
  const recall = writeManagedBlock(claudeMd, { body: RECALL_FIRST_BLOCK });
93086
93057
  if (recall.kind === "collision") {
93087
93058
  console.warn(
@@ -93093,15 +93064,15 @@ async function installClaudeHooks(port) {
93093
93064
  console.log(` endpoint: http://127.0.0.1:${port}/api/hook`);
93094
93065
  }
93095
93066
  async function installClaudeMcpConfig() {
93096
- const { mkdirSync: mkdirSync11, existsSync: existsSync37, readFileSync: readFileSync39, writeFileSync: writeFileSync28 } = await import("node:fs");
93097
- const { join: join45, dirname: dirname14 } = await import("node:path");
93098
- const file2 = join45(ROOT, ".mcp.json");
93099
- const dir = dirname14(file2);
93100
- if (!existsSync37(dir)) mkdirSync11(dir, { recursive: true });
93067
+ const { mkdirSync: mkdirSync11, existsSync: existsSync38, readFileSync: readFileSync41, writeFileSync: writeFileSync28 } = await import("node:fs");
93068
+ const { join: join48, dirname: dirname15 } = await import("node:path");
93069
+ const file2 = join48(ROOT, ".mcp.json");
93070
+ const dir = dirname15(file2);
93071
+ if (!existsSync38(dir)) mkdirSync11(dir, { recursive: true });
93101
93072
  let cfg = {};
93102
- if (existsSync37(file2)) {
93073
+ if (existsSync38(file2)) {
93103
93074
  try {
93104
- cfg = JSON.parse(readFileSync39(file2, "utf8"));
93075
+ cfg = JSON.parse(readFileSync41(file2, "utf8"));
93105
93076
  } catch {
93106
93077
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
93107
93078
  process.exit(2);
@@ -93115,15 +93086,15 @@ async function installClaudeMcpConfig() {
93115
93086
  console.log(` Tools: errata.search / locate / neighbors / callers_of / what_uses / show / similar`);
93116
93087
  }
93117
93088
  async function installCursorMcpConfig() {
93118
- const { mkdirSync: mkdirSync11, existsSync: existsSync37, readFileSync: readFileSync39, writeFileSync: writeFileSync28 } = await import("node:fs");
93119
- const { join: join45 } = await import("node:path");
93120
- const dir = join45(ROOT, ".cursor");
93121
- if (!existsSync37(dir)) mkdirSync11(dir, { recursive: true });
93122
- const file2 = join45(dir, "mcp.json");
93089
+ const { mkdirSync: mkdirSync11, existsSync: existsSync38, readFileSync: readFileSync41, writeFileSync: writeFileSync28 } = await import("node:fs");
93090
+ const { join: join48 } = await import("node:path");
93091
+ const dir = join48(ROOT, ".cursor");
93092
+ if (!existsSync38(dir)) mkdirSync11(dir, { recursive: true });
93093
+ const file2 = join48(dir, "mcp.json");
93123
93094
  let cfg = {};
93124
- if (existsSync37(file2)) {
93095
+ if (existsSync38(file2)) {
93125
93096
  try {
93126
- cfg = JSON.parse(readFileSync39(file2, "utf8"));
93097
+ cfg = JSON.parse(readFileSync41(file2, "utf8"));
93127
93098
  } catch {
93128
93099
  console.error(`refusing to overwrite invalid JSON at ${file2}`);
93129
93100
  process.exit(2);
@@ -93142,7 +93113,7 @@ async function installCodexHooks(port) {
93142
93113
  const gitRoot = nearestGitRoot(process.cwd());
93143
93114
  const hookRoot = gitRoot && mainCheckoutOfWorktree(gitRoot) ? gitRoot : ROOT;
93144
93115
  const file2 = writeCodexHooks(hookRoot, port);
93145
- const instructions = writeManagedBlock(join44(hookRoot, "AGENTS.md"), {
93116
+ const instructions = writeManagedBlock(join47(hookRoot, "AGENTS.md"), {
93146
93117
  body: AGENTS_POINTER_BODY,
93147
93118
  stable: true
93148
93119
  });
@@ -93160,6 +93131,21 @@ async function installCodexHooks(port) {
93160
93131
  console.log(" Run `errata doctor` here to check hook discovery and trust; installation alone is not activation.");
93161
93132
  console.log(" Respect an explicit [features] hooks = false setting (older builds use codex_hooks).");
93162
93133
  console.log(" Older Codex versions may not support every event; update if hooks do not fire.");
93134
+ for (const check2 of await diagnoseCodexHooks(hookRoot)) {
93135
+ console.log(` ${check2.status === "pass" ? "\u2713" : "!"} ${check2.detail}`);
93136
+ }
93137
+ }
93138
+ function refreshExistingCodexHooks() {
93139
+ let ok = true;
93140
+ const roots = [...listWorkspaceEntries().map((entry) => entry.path), process.cwd()];
93141
+ for (const result of refreshInstalledCodexRelays(roots)) {
93142
+ if (result.status === "updated") console.log(`updated existing Codex relay \u2192 ${result.root} (hook definitions preserved)`);
93143
+ if (result.status === "error") {
93144
+ ok = false;
93145
+ console.warn(`Codex relay refresh needs attention in ${result.root}: ${result.detail}`);
93146
+ }
93147
+ }
93148
+ return ok;
93163
93149
  }
93164
93150
  async function refreshLinkedCodexHooks(args2) {
93165
93151
  if (args2.includes("--skip-hooks") || !hasCodexConfig(ROOT)) return;
@@ -93314,6 +93300,7 @@ function installLogTimestamps() {
93314
93300
  async function cmdDash(args2) {
93315
93301
  process.title = "errata-daemon";
93316
93302
  installLogTimestamps();
93303
+ refreshExistingCodexHooks();
93317
93304
  const portIdx = args2.indexOf("--port");
93318
93305
  const port = portIdx >= 0 && args2[portIdx + 1] ? Number(args2[portIdx + 1]) : 7891;
93319
93306
  const reindexOnStart = !args2.includes("--no-reindex");
@@ -93468,7 +93455,7 @@ async function cmdDash(args2) {
93468
93455
  const hotspotScanMark = /* @__PURE__ */ new Map();
93469
93456
  const HOTSPOT_FANIN = 20;
93470
93457
  const runNightlyPass = async (dueIds) => {
93471
- const yieldToLoop2 = () => new Promise((resolve7) => setImmediate(resolve7));
93458
+ const yieldToLoop2 = () => new Promise((resolve8) => setImmediate(resolve8));
93472
93459
  const fullReclusterPass = nightlyCount++ % FULL_RECLUSTER_EVERY === 0;
93473
93460
  const m = dueIds ? await handle2.nightlyFor(dueIds) : await handle2.nightlyAll();
93474
93461
  let skillsLearned = 0;
@@ -93546,9 +93533,9 @@ async function cmdDash(args2) {
93546
93533
  await yieldToLoop2();
93547
93534
  try {
93548
93535
  const items = selectDurableMemory(handle2.sharedStore, r.engine.profile, {
93549
- atCommit: readGitRefState(join44(r.root, ".git")).sha ?? void 0
93536
+ atCommit: readGitRefState(join47(r.root, ".git")).sha ?? void 0
93550
93537
  });
93551
- const res = bleedRules(join44(r.root, ".claude", "rules"), items);
93538
+ const res = bleedRules(join47(r.root, ".claude", "rules"), items);
93552
93539
  if (res.written || res.pruned) {
93553
93540
  console.log(
93554
93541
  `[rules:${r.entry.name}] ${res.written} collective principle(s) \u2192 .claude/rules` + (res.pruned ? `, ${res.pruned} pruned` : "")