@hivelore/core 0.57.4 → 0.57.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -50,6 +50,14 @@ var SensorSchema = z.object({
50
50
  absent: z.string().optional(),
51
51
  /** Regex flags (e.g. "i", "m"). Ignored for non-regex kinds. */
52
52
  flags: z.string().optional(),
53
+ /**
54
+ * kind=regex only: flip the sensor into a REQUIRED-PRESENCE invariant. `pattern` then names a line
55
+ * that must REMAIN present in the anchored file's final content; the sensor FIRES when a change
56
+ * removes it. A normal regex sensor scans ADDED lines and so structurally cannot see a deletion —
57
+ * this catches the "someone deleted the critical line" class (e.g. a `TimeZone.setDefault(UTC)`
58
+ * guard) that a diff-of-additions sensor misses (field report §3.5).
59
+ */
60
+ require_present: z.boolean().optional(),
53
61
  /** Shell/test command to run (for kind=shell|test). Executed by the CLI, never by core. */
54
62
  command: z.string().optional(),
55
63
  /** Max runtime for kind=shell|test commands (default 120000). The executor kills on expiry. */
@@ -135,7 +143,17 @@ var MemoryFrontmatterSchema = z.object({
135
143
  * null when the memory is not yet validated, or on legacy memories written before this field.
136
144
  * Lets a human distinguish reviewed knowledge from AI/auto-trusted knowledge.
137
145
  */
138
- validated_by: z.enum(["human", "agent", "auto"]).nullable().default(null)
146
+ validated_by: z.enum(["human", "agent", "auto"]).nullable().default(null),
147
+ /**
148
+ * Does this memory describe code that EXISTS today, or a decision not yet built? Orthogonal to
149
+ * `confidence`/`status`: a `planned` decision can be fully trusted AS a decision while being false
150
+ * AS a description of the current code. Surfaced distinctly in briefings so an agent does not write
151
+ * code against a cookie/route/Node version that was only decided, never implemented (field report §3.3).
152
+ * applied — reflected in the code now (the default when omitted)
153
+ * planned — decided/intended, NOT yet implemented
154
+ * abandoned — considered and rejected; kept so it is not re-attempted
155
+ */
156
+ lifecycle: z.enum(["applied", "planned", "abandoned"]).optional()
139
157
  }).refine(
140
158
  (data) => data.scope !== "module" || !!data.module,
141
159
  { message: "module name is required when scope is 'module'", path: ["module"] }
@@ -153,15 +171,30 @@ var CrossRepoProvenanceSchema = z.object({
153
171
 
154
172
  // src/parser.ts
155
173
  import matter from "gray-matter";
174
+ import "zod";
156
175
  var PRIVATE_BLOCK_RE = /<private>[\s\S]*?<\/private>/g;
157
176
  function stripPrivate(body) {
158
177
  return body.replace(PRIVATE_BLOCK_RE, "").trimEnd();
159
178
  }
179
+ function formatFrontmatterError(err) {
180
+ const issue = err.issues[0];
181
+ if (!issue) return "invalid frontmatter";
182
+ const field = issue.path.length > 0 ? issue.path.join(".") : "frontmatter";
183
+ if (issue.code === "invalid_enum_value") {
184
+ const got = JSON.stringify(issue.received);
185
+ const allowed = issue.options.join(" | ");
186
+ return `invalid ${field}: ${got} is not a supported value \u2014 expected one of: ${allowed}`;
187
+ }
188
+ return `invalid ${field}: ${issue.message}`;
189
+ }
160
190
  function parseMemory(raw) {
161
191
  const parsed = matter(raw);
162
- const frontmatter = MemoryFrontmatterSchema.parse(parsed.data);
192
+ const result = MemoryFrontmatterSchema.safeParse(parsed.data);
193
+ if (!result.success) {
194
+ throw new Error(formatFrontmatterError(result.error));
195
+ }
163
196
  return {
164
- frontmatter,
197
+ frontmatter: result.data,
165
198
  body: stripPrivate(parsed.content.trim())
166
199
  };
167
200
  }
@@ -210,6 +243,7 @@ function buildFrontmatter(input) {
210
243
  topic: input.topic,
211
244
  sensor: input.sensor,
212
245
  activation: input.activation,
246
+ lifecycle: input.lifecycle,
213
247
  revision_count: 0,
214
248
  related_ids: input.relatedIds ?? []
215
249
  });
@@ -910,7 +944,7 @@ function applyFeedbackAdjustment(fm, adjustment, now = /* @__PURE__ */ new Date(
910
944
  }
911
945
 
912
946
  // src/prevention.ts
913
- import { appendFile, mkdir as mkdir2, readFile as readFile4 } from "fs/promises";
947
+ import { appendFile, mkdir as mkdir2, readFile as readFile4, writeFile as writeFile2 } from "fs/promises";
914
948
  import { existsSync as existsSync4 } from "fs";
915
949
  import path6 from "path";
916
950
  function preventionLogPath(paths) {
@@ -938,8 +972,22 @@ async function recordPreventionHits(paths, firedIds, source, now = /* @__PURE__
938
972
  await appendPreventionEvent(paths, { at, id, source, ...details[id] }).catch(() => {
939
973
  });
940
974
  }
975
+ await stampSensorLastFired(paths, recordedIds, at).catch(() => {
976
+ });
941
977
  return recordedIds;
942
978
  }
979
+ async function stampSensorLastFired(paths, ids, at) {
980
+ if (ids.length === 0 || !existsSync4(paths.memoriesDir)) return;
981
+ const wanted = new Set(ids);
982
+ const loaded = await loadMemoriesFromDir(paths.memoriesDir);
983
+ for (const { memory, filePath } of loaded) {
984
+ const fm = memory.frontmatter;
985
+ if (!wanted.has(fm.id) || !fm.sensor || fm.sensor.last_fired === at) continue;
986
+ const next = { ...memory, frontmatter: { ...fm, sensor: { ...fm.sensor, last_fired: at } } };
987
+ await writeFile2(filePath, serializeMemory(next), "utf8").catch(() => {
988
+ });
989
+ }
990
+ }
943
991
  function buildPreventionReceipt(events, memories, usage, options) {
944
992
  const now = options.now ?? /* @__PURE__ */ new Date();
945
993
  const sinceMs = options.since.getTime();
@@ -990,6 +1038,11 @@ function buildPreventionReceipt(events, memories, usage, options) {
990
1038
  events: rows
991
1039
  };
992
1040
  }
1041
+ function trendClause(total, previous) {
1042
+ const counts = `${total} this window vs ${previous} previous window`;
1043
+ if (total + previous < 3) return counts;
1044
+ return `${counts} (${total <= previous ? "recurrences declining" : "recurrences rising"})`;
1045
+ }
993
1046
  function renderPreventionReceipt(receipt) {
994
1047
  const lines = [
995
1048
  `Hivelore prevention receipt \u2014 last ${receipt.window_days} days`,
@@ -1007,9 +1060,7 @@ function renderPreventionReceipt(receipt) {
1007
1060
  const red = row.red_proven ? " \u2713 RED-proven" : "";
1008
1061
  lines.push(` \u2717\u2192\u2713 ${row.at.slice(0, 10)} ${row.id.padEnd(32)} (${kind}${exit}${stage})${incident}${red}`);
1009
1062
  }
1010
- lines.push(
1011
- ` Trend: ${receipt.total} this window vs ${receipt.previous_total} previous window (${receipt.total <= receipt.previous_total ? "recurrences declining" : "recurrences rising"}).`
1012
- );
1063
+ lines.push(` Trend: ${trendClause(receipt.total, receipt.previous_total)}.`);
1013
1064
  return lines.join("\n");
1014
1065
  }
1015
1066
  var HIVELORE_ATTRIBUTION = "\u{1F6E1}\uFE0F Generated by [Hivelore](https://github.com/Doucs91/hivelore) \u2014 the deterministic policy gate for agent-written code.";
@@ -1036,7 +1087,7 @@ function renderPreventionReceiptShare(receipt) {
1036
1087
  }
1037
1088
  lines.push(
1038
1089
  "",
1039
- `_Trend: ${receipt.total} this window vs ${receipt.previous_total} previous window (${receipt.total <= receipt.previous_total ? "recurrences declining" : "recurrences rising"})._`
1090
+ `_Trend: ${trendClause(receipt.total, receipt.previous_total)}._`
1040
1091
  );
1041
1092
  }
1042
1093
  lines.push("", `<sub>${HIVELORE_ATTRIBUTION}</sub>`);
@@ -1195,7 +1246,7 @@ function renderCaughtForYou(summary) {
1195
1246
 
1196
1247
  // src/context-throttle.ts
1197
1248
  import { createHash } from "crypto";
1198
- import { mkdir as mkdir3, readFile as readFile5, writeFile as writeFile2 } from "fs/promises";
1249
+ import { mkdir as mkdir3, readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
1199
1250
  import { existsSync as existsSync5 } from "fs";
1200
1251
  import path7 from "path";
1201
1252
  var PROJECT_CONTEXT_THROTTLE_MS = 8 * 60 * 1e3;
@@ -1220,12 +1271,12 @@ async function recordProjectContextEmission(paths, hash, now = Date.now()) {
1220
1271
  const file = throttleMarkerPath(paths);
1221
1272
  await mkdir3(path7.dirname(file), { recursive: true }).catch(() => {
1222
1273
  });
1223
- await writeFile2(file, JSON.stringify({ hash, at: new Date(now).toISOString() }), "utf8").catch(() => {
1274
+ await writeFile3(file, JSON.stringify({ hash, at: new Date(now).toISOString() }), "utf8").catch(() => {
1224
1275
  });
1225
1276
  }
1226
1277
 
1227
1278
  // src/gate-reminder.ts
1228
- import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile3 } from "fs/promises";
1279
+ import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile4 } from "fs/promises";
1229
1280
  import { existsSync as existsSync6 } from "fs";
1230
1281
  import path8 from "path";
1231
1282
  var GATE_REMINDER_WINDOW_MS = 24 * 60 * 60 * 1e3;
@@ -1254,7 +1305,7 @@ async function recordGateReminder(paths, key, now = Date.now()) {
1254
1305
  markers[key] = new Date(now).toISOString();
1255
1306
  await mkdir4(path8.dirname(file), { recursive: true }).catch(() => {
1256
1307
  });
1257
- await writeFile3(file, JSON.stringify(markers, null, 2), "utf8").catch(() => {
1308
+ await writeFile4(file, JSON.stringify(markers, null, 2), "utf8").catch(() => {
1258
1309
  });
1259
1310
  }
1260
1311
 
@@ -1752,9 +1803,13 @@ function generateBridges(memories, sensors, opts) {
1752
1803
 
1753
1804
  // src/sensors.ts
1754
1805
  function sensorPatternBrittleness(pattern) {
1755
- const literal = pattern.replace(/\[[^\]]*\]/g, "").replace(/\{[^}]*\}/g, "");
1756
- if (/\d{2,}\s*-\s*\d{2,}/.test(literal)) return "hardcoded line/number range \u2014 rots when code shifts";
1757
- if (/\d{3,}/.test(literal)) return "hardcoded numeric literal (likely a line number) \u2014 rots when code shifts";
1806
+ const literal = pattern.replace(/\\[a-zA-Z]/g, " ").replace(/\[[^\]]*\]/g, " ").replace(/\{[^}]*\}/g, " ").replace(/\b\d{1,3}(?:\s*\\?\.\s*\d{1,3}){2,3}\b/g, " ");
1807
+ const range = literal.match(/\d{2,}\s*-\s*\d{2,}/);
1808
+ if (range) return `hardcoded line/number range "${range[0].replace(/\s+/g, "")}" \u2014 rots when code shifts`;
1809
+ const numeric = literal.match(/\d{3,}/);
1810
+ if (numeric) {
1811
+ return `hardcoded numeric literal "${numeric[0]}" (likely a line number) \u2014 rots when code shifts; if it is a real constant, put it in a character class ([0-9]) or anchor it to a stable token`;
1812
+ }
1758
1813
  return null;
1759
1814
  }
1760
1815
  function normalizeProjectPath(value) {
@@ -1824,6 +1879,7 @@ function runSensors(memories, targets) {
1824
1879
  for (const memory of memories) {
1825
1880
  const sensor = memory.frontmatter.sensor;
1826
1881
  if (!sensor || sensor.kind !== "regex") continue;
1882
+ if (sensor.require_present) continue;
1827
1883
  const anchorPaths = memory.frontmatter.anchor.paths;
1828
1884
  for (const target of targets) {
1829
1885
  if (!sensorAppliesToPath(sensor, anchorPaths, target.path)) continue;
@@ -1833,6 +1889,47 @@ function runSensors(memories, targets) {
1833
1889
  }
1834
1890
  return hits;
1835
1891
  }
1892
+ function changedPathsFromDiff(diff) {
1893
+ const out = /* @__PURE__ */ new Set();
1894
+ for (const m of diff.matchAll(/^diff --git a\/(.+?) b\/(.+)$/gm)) {
1895
+ out.add((m[2] ?? m[1] ?? "").trim());
1896
+ }
1897
+ for (const m of diff.matchAll(/^\+\+\+ b\/(.+)$/gm)) {
1898
+ const p = (m[1] ?? "").trim();
1899
+ if (p && p !== "/dev/null") out.add(p);
1900
+ }
1901
+ return [...out];
1902
+ }
1903
+ function runPresenceSensors(memories, finalTargets) {
1904
+ const hits = [];
1905
+ for (const memory of memories) {
1906
+ const sensor = memory.frontmatter.sensor;
1907
+ if (!sensor || sensor.kind !== "regex" || !sensor.require_present || !sensor.pattern) continue;
1908
+ let re;
1909
+ try {
1910
+ const flags = new Set(["m", ...(sensor.flags ?? "").split("")].filter(Boolean));
1911
+ re = new RegExp(sensor.pattern, [...flags].join(""));
1912
+ } catch {
1913
+ continue;
1914
+ }
1915
+ const anchorPaths = memory.frontmatter.anchor.paths;
1916
+ for (const target of finalTargets) {
1917
+ if (!sensorAppliesToPath(sensor, anchorPaths, target.path)) continue;
1918
+ re.lastIndex = 0;
1919
+ if (!re.test(target.content)) {
1920
+ hits.push({
1921
+ memory_id: memory.frontmatter.id,
1922
+ sensor,
1923
+ file: target.path,
1924
+ message: sensor.message,
1925
+ severity: sensor.severity
1926
+ });
1927
+ break;
1928
+ }
1929
+ }
1930
+ }
1931
+ return hits;
1932
+ }
1836
1933
  var COMMAND_ENV_EXACT = /* @__PURE__ */ new Set([
1837
1934
  "PATH",
1838
1935
  "HOME",
@@ -2074,14 +2171,14 @@ function sensorSelfCheck(sensor, input) {
2074
2171
  function judgeProposedSensor(sensor, input) {
2075
2172
  const brittle = sensor.kind === "regex" && sensor.pattern ? sensorPatternBrittleness(sensor.pattern) : null;
2076
2173
  const self_check = sensorSelfCheck(sensor, input);
2174
+ if (self_check.fires_on_correct === true) {
2175
+ return { accepted: false, reason: "fires-on-correct", self_check, brittle };
2176
+ }
2077
2177
  if (sensor.severity === "block") {
2078
2178
  if (brittle) return { accepted: false, reason: "brittle", self_check, brittle };
2079
2179
  if (input.currentTargets.length > 0 && !self_check.silent_on_current) {
2080
2180
  return { accepted: false, reason: "fires-on-current", self_check, brittle };
2081
2181
  }
2082
- if (self_check.fires_on_correct === true) {
2083
- return { accepted: false, reason: "fires-on-correct", self_check, brittle };
2084
- }
2085
2182
  if (self_check.fires_on_bad === false) {
2086
2183
  return { accepted: false, reason: "missed-bad-example", self_check, brittle };
2087
2184
  }
@@ -3069,7 +3166,7 @@ function allocateBudget(parts, maxTokens) {
3069
3166
  }
3070
3167
 
3071
3168
  // src/code-map.ts
3072
- import { mkdir as mkdir5, readFile as readFile7, readdir as readdir3, stat as stat2, writeFile as writeFile4 } from "fs/promises";
3169
+ import { mkdir as mkdir5, readFile as readFile7, readdir as readdir3, stat as stat2, writeFile as writeFile5 } from "fs/promises";
3073
3170
  import { createHash as createHash2 } from "crypto";
3074
3171
  import { existsSync as existsSync7 } from "fs";
3075
3172
  import { spawnSync } from "child_process";
@@ -3512,10 +3609,10 @@ async function saveCodeMap(paths, map) {
3512
3609
  const current = existsSync7(file) ? await readFile7(file, "utf8").catch(() => null) : null;
3513
3610
  if (current === payload) return;
3514
3611
  await mkdir5(path9.dirname(file), { recursive: true });
3515
- await writeFile4(file, payload, "utf8");
3612
+ await writeFile5(file, payload, "utf8");
3516
3613
  await mkdir5(paths.runtimeDir, { recursive: true }).catch(() => {
3517
3614
  });
3518
- await writeFile4(
3615
+ await writeFile5(
3519
3616
  codeMapMetaPath(paths),
3520
3617
  `${JSON.stringify({ generated_at: (/* @__PURE__ */ new Date()).toISOString(), content_hash: codeMapContentHash(map) }, null, 2)}
3521
3618
  `,
@@ -3884,7 +3981,7 @@ function queryCodeMap(map, options) {
3884
3981
  // src/config.ts
3885
3982
  import { existsSync as existsSync8 } from "fs";
3886
3983
  import { readFileSync } from "fs";
3887
- import { readFile as readFile8, rm, writeFile as writeFile5 } from "fs/promises";
3984
+ import { readFile as readFile8, rm, writeFile as writeFile6 } from "fs/promises";
3888
3985
  import path10 from "path";
3889
3986
  var CONFIG_FILE = "hivelore.config.json";
3890
3987
  var LEGACY_CONFIG_FILE = "haive.config.json";
@@ -4016,7 +4113,7 @@ function loadConfigSync(paths) {
4016
4113
  }
4017
4114
  }
4018
4115
  async function saveConfig(paths, config) {
4019
- await writeFile5(configPath(paths), JSON.stringify(config, null, 2) + "\n", "utf8");
4116
+ await writeFile6(configPath(paths), JSON.stringify(config, null, 2) + "\n", "utf8");
4020
4117
  const legacy = path10.join(paths.haiveDir, LEGACY_CONFIG_FILE);
4021
4118
  if (existsSync8(legacy)) {
4022
4119
  try {
@@ -4042,7 +4139,7 @@ function mergeConfig(base, override) {
4042
4139
 
4043
4140
  // src/cross-repo.ts
4044
4141
  import { existsSync as existsSync9 } from "fs";
4045
- import { mkdir as mkdir6, readFile as readFile9, writeFile as writeFile6 } from "fs/promises";
4142
+ import { mkdir as mkdir6, readFile as readFile9, writeFile as writeFile7 } from "fs/promises";
4046
4143
  import path11 from "path";
4047
4144
  import { spawnSync as spawnSync2 } from "child_process";
4048
4145
  async function loadImportMap(cacheDir) {
@@ -4055,7 +4152,7 @@ async function loadImportMap(cacheDir) {
4055
4152
  }
4056
4153
  }
4057
4154
  async function saveImportMap(cacheDir, map) {
4058
- await writeFile6(path11.join(cacheDir, "import-map.json"), JSON.stringify(map, null, 2) + "\n", "utf8");
4155
+ await writeFile7(path11.join(cacheDir, "import-map.json"), JSON.stringify(map, null, 2) + "\n", "utf8");
4059
4156
  }
4060
4157
  async function pullCrossRepoSources(paths, config, projectRoot) {
4061
4158
  const sources = config.crossRepoSources ?? [];
@@ -4141,7 +4238,7 @@ async function pullFromSource(paths, source, projectRoot) {
4141
4238
  }
4142
4239
  const updatedBody = importedBodyPrefix + memory.body;
4143
4240
  if (existingEntry) {
4144
- await writeFile6(
4241
+ await writeFile7(
4145
4242
  existingLocalPath,
4146
4243
  serializeMemory({ frontmatter: existingEntry.memory.frontmatter, body: updatedBody }),
4147
4244
  "utf8"
@@ -4166,7 +4263,7 @@ async function pullFromSource(paths, source, projectRoot) {
4166
4263
  });
4167
4264
  const body = importedBodyPrefix + memory.body;
4168
4265
  const destPath = path11.join(destDir, `${newFm.id}.md`);
4169
- await writeFile6(destPath, serializeMemory({ frontmatter: newFm, body }), "utf8");
4266
+ await writeFile7(destPath, serializeMemory({ frontmatter: newFm, body }), "utf8");
4170
4267
  importMap[sourceId] = destPath;
4171
4268
  dirty = true;
4172
4269
  report.imported.push(sourceId);
@@ -4204,7 +4301,7 @@ async function cloneOrFetchGitSource(source, paths, report) {
4204
4301
 
4205
4302
  // src/dep-tracker.ts
4206
4303
  import { existsSync as existsSync10 } from "fs";
4207
- import { readFile as readFile10, writeFile as writeFile7, mkdir as mkdir7 } from "fs/promises";
4304
+ import { readFile as readFile10, writeFile as writeFile8, mkdir as mkdir7 } from "fs/promises";
4208
4305
  import path12 from "path";
4209
4306
  function parsePackageJson(content) {
4210
4307
  try {
@@ -4317,7 +4414,7 @@ async function trackDependencies(projectRoot, haiveDir, manifestFiles) {
4317
4414
  captured_at: (/* @__PURE__ */ new Date()).toISOString(),
4318
4415
  deps: currentDeps
4319
4416
  };
4320
- await writeFile7(lockPath, JSON.stringify(snapshot2, null, 2) + "\n", "utf8");
4417
+ await writeFile8(lockPath, JSON.stringify(snapshot2, null, 2) + "\n", "utf8");
4321
4418
  continue;
4322
4419
  }
4323
4420
  const snapshot = JSON.parse(await readFile10(lockPath, "utf8"));
@@ -4340,7 +4437,7 @@ async function trackDependencies(projectRoot, haiveDir, manifestFiles) {
4340
4437
  captured_at: (/* @__PURE__ */ new Date()).toISOString(),
4341
4438
  deps: currentDeps
4342
4439
  };
4343
- await writeFile7(lockPath, JSON.stringify(updated, null, 2) + "\n", "utf8");
4440
+ await writeFile8(lockPath, JSON.stringify(updated, null, 2) + "\n", "utf8");
4344
4441
  }
4345
4442
  }
4346
4443
  return results;
@@ -4348,7 +4445,7 @@ async function trackDependencies(projectRoot, haiveDir, manifestFiles) {
4348
4445
 
4349
4446
  // src/contract-watcher.ts
4350
4447
  import { existsSync as existsSync11 } from "fs";
4351
- import { readFile as readFile11, writeFile as writeFile8, mkdir as mkdir8 } from "fs/promises";
4448
+ import { readFile as readFile11, writeFile as writeFile9, mkdir as mkdir8 } from "fs/promises";
4352
4449
  import path13 from "path";
4353
4450
  import crypto from "crypto";
4354
4451
  function sha256(content) {
@@ -4571,7 +4668,7 @@ async function snapshotContract(projectRoot, haiveDir, contract) {
4571
4668
  };
4572
4669
  const contractsDir = path13.join(haiveDir, "contracts");
4573
4670
  await mkdir8(contractsDir, { recursive: true });
4574
- await writeFile8(contractLockPath(haiveDir, contract.name), JSON.stringify(snapshot, null, 2) + "\n", "utf8");
4671
+ await writeFile9(contractLockPath(haiveDir, contract.name), JSON.stringify(snapshot, null, 2) + "\n", "utf8");
4575
4672
  return snapshot;
4576
4673
  }
4577
4674
  async function diffContract(projectRoot, haiveDir, contract) {
@@ -4595,7 +4692,7 @@ async function diffContract(projectRoot, haiveDir, contract) {
4595
4692
  };
4596
4693
  const changes = diffSnapshots(beforeSnapshot, afterSnapshot);
4597
4694
  if (changes.length > 0) {
4598
- await writeFile8(lockPath, JSON.stringify(afterSnapshot, null, 2) + "\n", "utf8");
4695
+ await writeFile9(lockPath, JSON.stringify(afterSnapshot, null, 2) + "\n", "utf8");
4599
4696
  }
4600
4697
  return {
4601
4698
  contract: contract.name,
@@ -4689,7 +4786,7 @@ async function usageLogSize(paths) {
4689
4786
  }
4690
4787
 
4691
4788
  // src/friction.ts
4692
- import { appendFile as appendFile3, mkdir as mkdir10, readFile as readFile13, writeFile as writeFile9 } from "fs/promises";
4789
+ import { appendFile as appendFile3, mkdir as mkdir10, readFile as readFile13, writeFile as writeFile10 } from "fs/promises";
4693
4790
  import { existsSync as existsSync13 } from "fs";
4694
4791
  import { createHash as createHash3 } from "crypto";
4695
4792
  import path15 from "path";
@@ -4789,7 +4886,7 @@ async function loadFrictionState(paths) {
4789
4886
  }
4790
4887
  async function saveFrictionState(paths, state) {
4791
4888
  if (!existsSync13(paths.runtimeDir)) await mkdir10(paths.runtimeDir, { recursive: true });
4792
- await writeFile9(frictionStatePath(paths), JSON.stringify(state, null, 2) + "\n", "utf8");
4889
+ await writeFile10(frictionStatePath(paths), JSON.stringify(state, null, 2) + "\n", "utf8");
4793
4890
  }
4794
4891
  async function setFrictionStatus(paths, fingerprint, status, url) {
4795
4892
  const state = await loadFrictionState(paths);
@@ -5249,7 +5346,7 @@ async function readRuntimeJournalTail(paths, limit) {
5249
5346
  }
5250
5347
 
5251
5348
  // src/enforcement.ts
5252
- import { mkdir as mkdir12, readdir as readdir4, readFile as readFile15, writeFile as writeFile10 } from "fs/promises";
5349
+ import { mkdir as mkdir12, readdir as readdir4, readFile as readFile15, writeFile as writeFile11 } from "fs/promises";
5253
5350
  import { existsSync as existsSync16 } from "fs";
5254
5351
  import path18 from "path";
5255
5352
  var BRIEFING_MARKER_TTL_MS = 12 * 60 * 60 * 1e3;
@@ -5290,7 +5387,7 @@ async function writeBriefingMarker(paths, input) {
5290
5387
  root: paths.root
5291
5388
  };
5292
5389
  await mkdir12(briefingMarkersDir(paths), { recursive: true });
5293
- await writeFile10(
5390
+ await writeFile11(
5294
5391
  briefingMarkerPath(paths, marker.session_id),
5295
5392
  JSON.stringify(marker, null, 2) + "\n",
5296
5393
  "utf8"
@@ -5401,7 +5498,7 @@ function isRetiredMemory(fm, body = "", now = /* @__PURE__ */ new Date()) {
5401
5498
  // src/sensor-ledger.ts
5402
5499
  import { createHash as createHash4 } from "crypto";
5403
5500
  import { existsSync as existsSync17, readFileSync as readFileSync2 } from "fs";
5404
- import { appendFile as appendFile5, mkdir as mkdir13, readFile as readFile16, rename, writeFile as writeFile11 } from "fs/promises";
5501
+ import { appendFile as appendFile5, mkdir as mkdir13, readFile as readFile16, rename, writeFile as writeFile12 } from "fs/promises";
5405
5502
  import path19 from "path";
5406
5503
  var MAX_LINES = 1e4;
5407
5504
  var RETAINED_LINES = 8e3;
@@ -5424,7 +5521,7 @@ async function appendSensorEvaluations(paths, evaluations) {
5424
5521
  const lines = raw.split("\n").filter(Boolean);
5425
5522
  if (lines.length > MAX_LINES) {
5426
5523
  const temp = `${file}.${process.pid}.tmp`;
5427
- await writeFile11(temp, lines.slice(-RETAINED_LINES).join("\n") + "\n", "utf8");
5524
+ await writeFile12(temp, lines.slice(-RETAINED_LINES).join("\n") + "\n", "utf8");
5428
5525
  await rename(temp, file);
5429
5526
  }
5430
5527
  } catch {
@@ -7125,7 +7222,7 @@ ${trimmed}`;
7125
7222
  }
7126
7223
 
7127
7224
  // src/handoff.ts
7128
- import { writeFile as writeFile12, readFile as readFile18, stat as stat4 } from "fs/promises";
7225
+ import { writeFile as writeFile13, readFile as readFile18, stat as stat4 } from "fs/promises";
7129
7226
  import { existsSync as existsSync19 } from "fs";
7130
7227
  import path21 from "path";
7131
7228
  var HANDOFF_FILENAME = "NEXT.md";
@@ -7179,7 +7276,7 @@ function buildHandoffMarkdown(data) {
7179
7276
  }
7180
7277
  async function writeSessionHandoff(root, data) {
7181
7278
  const file = handoffFilePath(root);
7182
- await writeFile12(file, buildHandoffMarkdown(data), "utf8");
7279
+ await writeFile13(file, buildHandoffMarkdown(data), "utf8");
7183
7280
  return file;
7184
7281
  }
7185
7282
  async function readSessionHandoff(root) {
@@ -7404,6 +7501,7 @@ export {
7404
7501
  buildProposeCommand,
7405
7502
  buildReport,
7406
7503
  bumpRead,
7504
+ changedPathsFromDiff,
7407
7505
  churnForAnchors,
7408
7506
  classifyGithubRelease,
7409
7507
  classifyMemoryPriority,
@@ -7589,6 +7687,7 @@ export {
7589
7687
  retirementSignal,
7590
7688
  revertedShaFromCommit,
7591
7689
  reviewLearningsToDrafts,
7690
+ runPresenceSensors,
7592
7691
  runRegexSensor,
7593
7692
  runSensors,
7594
7693
  runTierContract,