@inerrata-corporation/errata 2.0.6-dev.2592 → 2.0.6-dev.2608

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
@@ -33879,8 +33879,11 @@ async function scan(root, current, ignores, providers, out2) {
33879
33879
  const provider = providers.find((p) => p.fileExtensions.includes(ext)) ?? null;
33880
33880
  const rel = relative2(root, abs).split(sep).join("/");
33881
33881
  let size = 0;
33882
+ let mtimeMs;
33882
33883
  try {
33883
- size = statSync3(abs).size;
33884
+ const st = statSync3(abs);
33885
+ size = st.size;
33886
+ mtimeMs = st.mtimeMs;
33884
33887
  } catch {
33885
33888
  continue;
33886
33889
  }
@@ -33888,6 +33891,7 @@ async function scan(root, current, ignores, providers, out2) {
33888
33891
  absPath: abs,
33889
33892
  relPath: rel,
33890
33893
  size,
33894
+ mtimeMs,
33891
33895
  language: provider?.id ?? null,
33892
33896
  provider
33893
33897
  });
@@ -33928,8 +33932,11 @@ function gitListFiles(root, ignores, providers) {
33928
33932
  for (const rel of rels) {
33929
33933
  const abs = join12(root, rel);
33930
33934
  let size;
33935
+ let mtimeMs;
33931
33936
  try {
33932
- size = statSync3(abs).size;
33937
+ const st = statSync3(abs);
33938
+ size = st.size;
33939
+ mtimeMs = st.mtimeMs;
33933
33940
  } catch {
33934
33941
  continue;
33935
33942
  }
@@ -33939,6 +33946,7 @@ function gitListFiles(root, ignores, providers) {
33939
33946
  absPath: abs,
33940
33947
  relPath: rel,
33941
33948
  size,
33949
+ mtimeMs,
33942
33950
  language: provider?.id ?? null,
33943
33951
  provider
33944
33952
  });
@@ -33947,12 +33955,14 @@ function gitListFiles(root, ignores, providers) {
33947
33955
  }
33948
33956
  function upsertFile(store, id, f, workspaceId2, skip) {
33949
33957
  const now = nowTs();
33958
+ const existing = store.getNode(id);
33959
+ if (existing && fileUnchanged(existing.attrs, f, skip)) return;
33950
33960
  let contentHash;
33951
33961
  try {
33952
33962
  contentHash = createHash5("sha256").update(readFileSync11(f.absPath)).digest("hex");
33953
33963
  } catch {
33954
33964
  }
33955
- const carried = { ...store.getNode(id)?.attrs ?? {} };
33965
+ const carried = { ...existing?.attrs ?? {} };
33956
33966
  for (const owned of ["contentHash", "indexSkipped", "symbolCount"]) delete carried[owned];
33957
33967
  const node2 = {
33958
33968
  id,
@@ -33976,6 +33986,7 @@ function upsertFile(store, id, f, workspaceId2, skip) {
33976
33986
  absPath: f.absPath,
33977
33987
  relPath: f.relPath,
33978
33988
  size: f.size,
33989
+ ...f.mtimeMs !== void 0 ? { mtimeMs: f.mtimeMs } : {},
33979
33990
  language: f.language ?? null,
33980
33991
  workspaceId: workspaceId2,
33981
33992
  ...contentHash ? { contentHash } : {},
@@ -33984,6 +33995,15 @@ function upsertFile(store, id, f, workspaceId2, skip) {
33984
33995
  };
33985
33996
  store.mergeNode(node2);
33986
33997
  }
33998
+ function fileUnchanged(attrs, f, skip) {
33999
+ if (f.mtimeMs === void 0 || attrs["mtimeMs"] !== f.mtimeMs) return false;
34000
+ if (attrs["size"] !== f.size || typeof attrs["contentHash"] !== "string") return false;
34001
+ if (attrs["relPath"] !== f.relPath || attrs["absPath"] !== f.absPath) return false;
34002
+ if ((attrs["language"] ?? null) !== (f.language ?? null)) return false;
34003
+ const stored = attrs["indexSkipped"];
34004
+ const scanOwned = stored === "no-provider" || stored === "oversize" ? stored : void 0;
34005
+ return scanOwned === skip;
34006
+ }
33987
34007
  function stampIndexOutcome(store, fileId, outcome) {
33988
34008
  const fnode = store.getNode(fileId);
33989
34009
  if (!fnode) return;
@@ -41289,6 +41309,7 @@ __export(src_exports5, {
41289
41309
  defaultProviders: () => defaultProviders,
41290
41310
  edgeId: () => edgeId,
41291
41311
  fileNodeId: () => fileNodeId,
41312
+ fileUnchanged: () => fileUnchanged,
41292
41313
  findInnermostScope: () => findInnermostScope,
41293
41314
  folderNodeId: () => folderNodeId,
41294
41315
  fromHex: () => fromHex,
@@ -76011,6 +76032,10 @@ var PassWorker = class {
76011
76032
  reindex(opts) {
76012
76033
  return this.call("reindex", opts);
76013
76034
  }
76035
+ /** Run a full-scope `runIndexer` on the worker (workspace wake, `errata reindex`). */
76036
+ fullIndex(opts) {
76037
+ return this.call("fullIndex", opts);
76038
+ }
76014
76039
  call(kind, payload) {
76015
76040
  if (this.stopped) return Promise.reject(new Error("pass worker stopped"));
76016
76041
  return withHeavyPassSlot(`${kind}:${this.init.workspaceId}`, async () => {
@@ -78396,7 +78421,7 @@ function createWatchBreaker(deps) {
78396
78421
  }
78397
78422
 
78398
78423
  // src/engine.ts
78399
- var DAEMON_VERSION = true ? "2.0.6-dev.2592" : "2.0.0-alpha.0";
78424
+ var DAEMON_VERSION = true ? "2.0.6-dev.2608" : "2.0.0-alpha.0";
78400
78425
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
78401
78426
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
78402
78427
  var GIT_OP_MUTE_MS = 4e3;
@@ -79014,6 +79039,33 @@ function createWorkspaceEngine(opts) {
79014
79039
  intentRemoteInFlight = false;
79015
79040
  });
79016
79041
  };
79042
+ const runFullIndex = async (clean) => {
79043
+ if (passWorker) {
79044
+ try {
79045
+ return await passWorker.fullIndex({ clean });
79046
+ } catch (err2) {
79047
+ if (isWorkerOom(err2)) {
79048
+ console.warn("[errata] full-reindex worker hit its heap ceiling \u2014 pass skipped");
79049
+ throw new Error("full reindex skipped: pass worker hit its heap ceiling");
79050
+ }
79051
+ console.warn(
79052
+ "[errata] full-reindex worker unavailable, running inline:",
79053
+ err2 instanceof Error ? err2.message : err2
79054
+ );
79055
+ }
79056
+ }
79057
+ const done = markPass(`reindex-inline:${profile.name}`);
79058
+ try {
79059
+ return await runIndexer(store, {
79060
+ workspaceId: profile.id,
79061
+ rootPath: opts.workspaceRoot,
79062
+ providers: defaultProviders(),
79063
+ clean
79064
+ });
79065
+ } finally {
79066
+ done();
79067
+ }
79068
+ };
79017
79069
  const runNightly = async () => {
79018
79070
  if (passWorker) {
79019
79071
  try {
@@ -80557,12 +80609,7 @@ B: ${b}`,
80557
80609
  if (!reindexOpts?.quiet) {
80558
80610
  notifyEvent("index-started", `Indexing ${profile.name}\u2026`, { key: profile.id });
80559
80611
  }
80560
- const r = await runIndexer(store, {
80561
- workspaceId: profile.id,
80562
- rootPath: opts.workspaceRoot,
80563
- providers: defaultProviders(),
80564
- clean: reindexOpts?.clean ?? false
80565
- });
80612
+ const r = await runFullIndex(reindexOpts?.clean ?? false);
80566
80613
  const { report: nightly, localSkills } = await runNightly();
80567
80614
  let embeddedCodeNodes = 0;
80568
80615
  let embedMs = 0;
@@ -81908,7 +81955,7 @@ function compareVersions(a, b) {
81908
81955
  }
81909
81956
  return 0;
81910
81957
  }
81911
- async function latestPublished(channel, timeoutMs = 4e3) {
81958
+ async function lookupPublished(channel, timeoutMs = 4e3) {
81912
81959
  const ctrl = new AbortController();
81913
81960
  const timer = setTimeout(() => ctrl.abort(), timeoutMs);
81914
81961
  try {
@@ -81916,17 +81963,23 @@ async function latestPublished(channel, timeoutMs = 4e3) {
81916
81963
  headers: { accept: "application/vnd.npm.install-v1+json" },
81917
81964
  signal: ctrl.signal
81918
81965
  });
81919
- if (!res.ok) return null;
81966
+ if (!res.ok) return { error: `http ${res.status}` };
81920
81967
  const body2 = await res.json();
81921
- return body2["dist-tags"]?.[channel] ?? null;
81968
+ const version2 = body2["dist-tags"]?.[channel];
81969
+ return version2 ? { version: version2 } : { error: "no-tag" };
81922
81970
  } catch {
81923
- return null;
81971
+ return { error: ctrl.signal.aborted ? "timeout" : "unreachable" };
81924
81972
  } finally {
81925
81973
  clearTimeout(timer);
81926
81974
  }
81927
81975
  }
81928
- async function checkForUpdate(channel) {
81929
- const latest = await latestPublished(channel);
81976
+ async function checkForUpdate(channel, opts = {}) {
81977
+ let lookup = { error: "unreachable" };
81978
+ for (let i2 = 0; i2 < Math.max(1, opts.attempts ?? 1); i2++) {
81979
+ lookup = await lookupPublished(channel, opts.timeoutMs ?? 4e3);
81980
+ if ("version" in lookup || lookup.error !== "timeout" && lookup.error !== "unreachable") break;
81981
+ }
81982
+ const latest = "version" in lookup ? lookup.version : null;
81930
81983
  const direction = latest === null ? 0 : compareVersions(latest, DAEMON_VERSION);
81931
81984
  return {
81932
81985
  current: DAEMON_VERSION,
@@ -81934,7 +81987,8 @@ async function checkForUpdate(channel) {
81934
81987
  // `compareVersions` returns 0 for unparseable input, so an unrecognizable tag
81935
81988
  // is treated as "nothing to do" rather than gambling on a direction.
81936
81989
  updateAvailable: latest !== null && direction > 0,
81937
- downgradeAvailable: latest !== null && direction < 0
81990
+ downgradeAvailable: latest !== null && direction < 0,
81991
+ ..."error" in lookup ? { lookupError: lookup.error } : {}
81938
81992
  };
81939
81993
  }
81940
81994
  function pendingFromServerVerdict(latest, channel, current = DAEMON_VERSION) {
@@ -88002,10 +88056,11 @@ async function cmdUpdate(args2) {
88002
88056
  saveConfig({ ...cfg, updateChannel: channel });
88003
88057
  console.log(`update channel set to '${channel}'`);
88004
88058
  }
88005
- const status = await checkForUpdate(channel);
88059
+ const status = await checkForUpdate(channel, { timeoutMs: 15e3, attempts: 2 });
88006
88060
  if (status.latest === null) {
88061
+ const why = status.lookupError;
88007
88062
  console.log(
88008
- `no published build found on channel '${channel}' (offline, or nothing published there yet). current: ${status.current}`
88063
+ why === "no-tag" ? `no published build found on channel '${channel}'. current: ${status.current}` : why === "timeout" ? `the npm registry did not answer within 15s (tried twice), so channel '${channel}' could not be checked; nothing was changed. Retry, or check your network. current: ${status.current}` : `could not reach the npm registry (${why ?? "unreachable"}), so channel '${channel}' could not be checked; nothing was changed. current: ${status.current}`
88009
88064
  );
88010
88065
  return;
88011
88066
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.6-dev.2592",
3
+ "version": "2.0.6-dev.2608",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/pass-worker.mjs CHANGED
@@ -16895,8 +16895,11 @@ async function scan(root, current, ignores, providers, out2) {
16895
16895
  const provider = providers.find((p) => p.fileExtensions.includes(ext)) ?? null;
16896
16896
  const rel = relative(root, abs).split(sep).join("/");
16897
16897
  let size = 0;
16898
+ let mtimeMs;
16898
16899
  try {
16899
- size = statSync2(abs).size;
16900
+ const st = statSync2(abs);
16901
+ size = st.size;
16902
+ mtimeMs = st.mtimeMs;
16900
16903
  } catch {
16901
16904
  continue;
16902
16905
  }
@@ -16904,6 +16907,7 @@ async function scan(root, current, ignores, providers, out2) {
16904
16907
  absPath: abs,
16905
16908
  relPath: rel,
16906
16909
  size,
16910
+ mtimeMs,
16907
16911
  language: provider?.id ?? null,
16908
16912
  provider
16909
16913
  });
@@ -16944,8 +16948,11 @@ function gitListFiles(root, ignores, providers) {
16944
16948
  for (const rel of rels) {
16945
16949
  const abs = join2(root, rel);
16946
16950
  let size;
16951
+ let mtimeMs;
16947
16952
  try {
16948
- size = statSync2(abs).size;
16953
+ const st = statSync2(abs);
16954
+ size = st.size;
16955
+ mtimeMs = st.mtimeMs;
16949
16956
  } catch {
16950
16957
  continue;
16951
16958
  }
@@ -16955,6 +16962,7 @@ function gitListFiles(root, ignores, providers) {
16955
16962
  absPath: abs,
16956
16963
  relPath: rel,
16957
16964
  size,
16965
+ mtimeMs,
16958
16966
  language: provider?.id ?? null,
16959
16967
  provider
16960
16968
  });
@@ -16963,12 +16971,14 @@ function gitListFiles(root, ignores, providers) {
16963
16971
  }
16964
16972
  function upsertFile(store2, id, f, workspaceId, skip) {
16965
16973
  const now = nowTs();
16974
+ const existing = store2.getNode(id);
16975
+ if (existing && fileUnchanged(existing.attrs, f, skip)) return;
16966
16976
  let contentHash;
16967
16977
  try {
16968
16978
  contentHash = createHash3("sha256").update(readFileSync2(f.absPath)).digest("hex");
16969
16979
  } catch {
16970
16980
  }
16971
- const carried = { ...store2.getNode(id)?.attrs ?? {} };
16981
+ const carried = { ...existing?.attrs ?? {} };
16972
16982
  for (const owned of ["contentHash", "indexSkipped", "symbolCount"]) delete carried[owned];
16973
16983
  const node = {
16974
16984
  id,
@@ -16992,6 +17002,7 @@ function upsertFile(store2, id, f, workspaceId, skip) {
16992
17002
  absPath: f.absPath,
16993
17003
  relPath: f.relPath,
16994
17004
  size: f.size,
17005
+ ...f.mtimeMs !== void 0 ? { mtimeMs: f.mtimeMs } : {},
16995
17006
  language: f.language ?? null,
16996
17007
  workspaceId,
16997
17008
  ...contentHash ? { contentHash } : {},
@@ -17000,6 +17011,15 @@ function upsertFile(store2, id, f, workspaceId, skip) {
17000
17011
  };
17001
17012
  store2.mergeNode(node);
17002
17013
  }
17014
+ function fileUnchanged(attrs, f, skip) {
17015
+ if (f.mtimeMs === void 0 || attrs["mtimeMs"] !== f.mtimeMs) return false;
17016
+ if (attrs["size"] !== f.size || typeof attrs["contentHash"] !== "string") return false;
17017
+ if (attrs["relPath"] !== f.relPath || attrs["absPath"] !== f.absPath) return false;
17018
+ if ((attrs["language"] ?? null) !== (f.language ?? null)) return false;
17019
+ const stored = attrs["indexSkipped"];
17020
+ const scanOwned = stored === "no-provider" || stored === "oversize" ? stored : void 0;
17021
+ return scanOwned === skip;
17022
+ }
17003
17023
  function stampIndexOutcome(store2, fileId, outcome) {
17004
17024
  const fnode = store2.getNode(fileId);
17005
17025
  if (!fnode) return;
@@ -24305,6 +24325,7 @@ __export(src_exports, {
24305
24325
  defaultProviders: () => defaultProviders,
24306
24326
  edgeId: () => edgeId,
24307
24327
  fileNodeId: () => fileNodeId,
24328
+ fileUnchanged: () => fileUnchanged,
24308
24329
  findInnermostScope: () => findInnermostScope,
24309
24330
  folderNodeId: () => folderNodeId,
24310
24331
  fromHex: () => fromHex,
@@ -27523,6 +27544,13 @@ function runPass(msg) {
27523
27544
  }
27524
27545
  case "reindex":
27525
27546
  return runReindex(msg.payload);
27547
+ case "fullIndex":
27548
+ return runIndexer(store, {
27549
+ workspaceId: init2.workspaceId,
27550
+ rootPath: init2.workspaceRoot,
27551
+ providers: defaultProviders(),
27552
+ clean: msg.payload?.clean ?? false
27553
+ });
27526
27554
  default:
27527
27555
  throw new Error(`pass-worker: unknown pass "${msg.kind}"`);
27528
27556
  }