@inerrata-corporation/errata 2.0.2-dev.682 → 2.0.2-dev.688
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 +143 -33
- package/package.json +1 -1
package/errata.mjs
CHANGED
|
@@ -52221,23 +52221,38 @@ init_review2();
|
|
|
52221
52221
|
import { closeSync, existsSync as existsSync12, fstatSync, openSync, readdirSync as readdirSync5, readSync, statSync as statSync3 } from "node:fs";
|
|
52222
52222
|
import { basename as basename3, dirname as dirname8, join as join15 } from "node:path";
|
|
52223
52223
|
import { homedir as homedir3 } from "node:os";
|
|
52224
|
-
function readFrom(path2, fromByte) {
|
|
52224
|
+
function readFrom(path2, fromByte, maxBytes) {
|
|
52225
52225
|
let fd;
|
|
52226
52226
|
try {
|
|
52227
52227
|
fd = openSync(path2, "r");
|
|
52228
52228
|
} catch {
|
|
52229
|
-
return { text: "", nextOffset: fromByte };
|
|
52229
|
+
return { text: "", nextOffset: fromByte, more: false };
|
|
52230
52230
|
}
|
|
52231
52231
|
try {
|
|
52232
52232
|
const size = fstatSync(fd).size;
|
|
52233
52233
|
const start2 = fromByte > 0 && fromByte <= size ? fromByte : 0;
|
|
52234
|
-
const
|
|
52235
|
-
if (
|
|
52236
|
-
|
|
52237
|
-
|
|
52238
|
-
|
|
52234
|
+
const remaining = size - start2;
|
|
52235
|
+
if (remaining <= 0) return { text: "", nextOffset: size, more: false };
|
|
52236
|
+
let cap = maxBytes && maxBytes > 0 ? maxBytes : remaining;
|
|
52237
|
+
for (; ; ) {
|
|
52238
|
+
const len = Math.min(cap, remaining);
|
|
52239
|
+
const buf = Buffer.allocUnsafe(len);
|
|
52240
|
+
readSync(fd, buf, 0, len, start2);
|
|
52241
|
+
if (len >= remaining) {
|
|
52242
|
+
return { text: buf.toString("utf8"), nextOffset: size, more: false };
|
|
52243
|
+
}
|
|
52244
|
+
const nl = buf.lastIndexOf(10);
|
|
52245
|
+
if (nl >= 0) {
|
|
52246
|
+
return {
|
|
52247
|
+
text: buf.subarray(0, nl + 1).toString("utf8"),
|
|
52248
|
+
nextOffset: start2 + nl + 1,
|
|
52249
|
+
more: true
|
|
52250
|
+
};
|
|
52251
|
+
}
|
|
52252
|
+
cap *= 2;
|
|
52253
|
+
}
|
|
52239
52254
|
} catch {
|
|
52240
|
-
return { text: "", nextOffset: fromByte };
|
|
52255
|
+
return { text: "", nextOffset: fromByte, more: false };
|
|
52241
52256
|
} finally {
|
|
52242
52257
|
closeSync(fd);
|
|
52243
52258
|
}
|
|
@@ -52333,9 +52348,9 @@ function isUserTurnBoundary(obj) {
|
|
|
52333
52348
|
function readAssistantTurns(transcriptPath, includeThinking = true, maxBytes = 2e6) {
|
|
52334
52349
|
return parseAssistantTurns(readTail(transcriptPath, maxBytes), includeThinking);
|
|
52335
52350
|
}
|
|
52336
|
-
function readAssistantTurnsFrom(transcriptPath, fromByte, includeThinking = true) {
|
|
52337
|
-
const { text, nextOffset } = readFrom(transcriptPath, fromByte);
|
|
52338
|
-
return { turns: parseAssistantTurns(text, includeThinking), nextOffset };
|
|
52351
|
+
function readAssistantTurnsFrom(transcriptPath, fromByte, includeThinking = true, maxBytes) {
|
|
52352
|
+
const { text, nextOffset, more } = readFrom(transcriptPath, fromByte, maxBytes);
|
|
52353
|
+
return { turns: parseAssistantTurns(text, includeThinking), nextOffset, more };
|
|
52339
52354
|
}
|
|
52340
52355
|
function parseAssistantTurns(raw2, includeThinking) {
|
|
52341
52356
|
if (!raw2) return [];
|
|
@@ -53558,7 +53573,12 @@ function passLedgerPath(configDir) {
|
|
|
53558
53573
|
function appendPassLedger(configDir, kind, durationMs, counts) {
|
|
53559
53574
|
const path2 = passLedgerPath(configDir);
|
|
53560
53575
|
try {
|
|
53561
|
-
const entry = {
|
|
53576
|
+
const entry = {
|
|
53577
|
+
ts: Date.now(),
|
|
53578
|
+
kind,
|
|
53579
|
+
durationMs,
|
|
53580
|
+
counts: { ...counts, rssMB: Math.round(process.memoryUsage().rss / 1048576) }
|
|
53581
|
+
};
|
|
53562
53582
|
appendFileSync2(path2, JSON.stringify(entry) + "\n");
|
|
53563
53583
|
const n = (appendsSinceTrim.get(path2) ?? 0) + 1;
|
|
53564
53584
|
if (n < TRIM_EVERY) {
|
|
@@ -53744,18 +53764,38 @@ function recordEpisode(store, workspaceId2, t, delta, causal) {
|
|
|
53744
53764
|
return id;
|
|
53745
53765
|
}
|
|
53746
53766
|
|
|
53767
|
+
// src/footprint.ts
|
|
53768
|
+
function numEnv(key, fallback) {
|
|
53769
|
+
const raw2 = process.env[key];
|
|
53770
|
+
if (raw2 == null || raw2.trim() === "") return fallback;
|
|
53771
|
+
const n = Number(raw2);
|
|
53772
|
+
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
53773
|
+
}
|
|
53774
|
+
var DAEMON_MAX_HEAP_MB = numEnv("ERRATA_MAX_HEAP_MB", 2048);
|
|
53775
|
+
var WORKER_MAX_HEAP_MB = numEnv("ERRATA_WORKER_MAX_HEAP_MB", 1024);
|
|
53776
|
+
var HARVEST_SLICE_BYTES = numEnv("ERRATA_HARVEST_SLICE_BYTES", 4e6);
|
|
53777
|
+
function workerResourceLimits() {
|
|
53778
|
+
return { maxOldGenerationSizeMb: WORKER_MAX_HEAP_MB };
|
|
53779
|
+
}
|
|
53780
|
+
function isWorkerOom(err2) {
|
|
53781
|
+
const e = err2;
|
|
53782
|
+
if (!e) return false;
|
|
53783
|
+
if (e.code === "ERR_WORKER_OUT_OF_MEMORY") return true;
|
|
53784
|
+
return /out of memory|ERR_WORKER_OUT_OF_MEMORY|heap limit/i.test(e.message ?? "");
|
|
53785
|
+
}
|
|
53786
|
+
|
|
53747
53787
|
// src/episode-retention.ts
|
|
53748
53788
|
var EPISODE_RETAIN_DAYS_DEFAULT = 90;
|
|
53749
53789
|
var EPISODE_RETIRE_BATCH_DEFAULT = 300;
|
|
53750
53790
|
var DAY_MS = 864e5;
|
|
53751
|
-
function
|
|
53791
|
+
function numEnv2(key, fallback) {
|
|
53752
53792
|
const raw2 = process.env[key];
|
|
53753
53793
|
if (raw2 == null || raw2.trim() === "") return fallback;
|
|
53754
53794
|
const n = Number(raw2);
|
|
53755
53795
|
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
53756
53796
|
}
|
|
53757
53797
|
function retireEpisodeProvenance(store, now, opts = {}) {
|
|
53758
|
-
const retainDays = opts.retainDays ??
|
|
53798
|
+
const retainDays = opts.retainDays ?? numEnv2("ERRATA_EPISODE_RETAIN_DAYS", EPISODE_RETAIN_DAYS_DEFAULT);
|
|
53759
53799
|
const batch = opts.batch ?? EPISODE_RETIRE_BATCH_DEFAULT;
|
|
53760
53800
|
const cutoff = now - retainDays * DAY_MS;
|
|
53761
53801
|
const due = store.findNodesByLabel("Episode").filter((ep) => {
|
|
@@ -53831,7 +53871,15 @@ var PassWorker = class {
|
|
|
53831
53871
|
ensureWorker() {
|
|
53832
53872
|
if (this.worker) return;
|
|
53833
53873
|
const workerUrl = new URL("./pass-worker.mjs", import.meta.url);
|
|
53834
|
-
const worker = new Worker(workerUrl, {
|
|
53874
|
+
const worker = new Worker(workerUrl, {
|
|
53875
|
+
workerData: this.init,
|
|
53876
|
+
execArgv: process.execArgv,
|
|
53877
|
+
// HZ-footprint-cap: hard heap ceiling per isolate. A pass that would
|
|
53878
|
+
// exceed it dies HERE, in the isolate built to be expendable — the
|
|
53879
|
+
// engine's fallback must then SKIP the pass (isWorkerOom), never re-run
|
|
53880
|
+
// it inline where the same allocation would take down the hook server.
|
|
53881
|
+
resourceLimits: workerResourceLimits()
|
|
53882
|
+
});
|
|
53835
53883
|
this.worker = worker;
|
|
53836
53884
|
this.ready = new Promise((resolve6, reject) => {
|
|
53837
53885
|
const onReady = (m) => {
|
|
@@ -55407,7 +55455,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
|
|
|
55407
55455
|
}
|
|
55408
55456
|
|
|
55409
55457
|
// src/engine.ts
|
|
55410
|
-
var DAEMON_VERSION = true ? "2.0.2-dev.
|
|
55458
|
+
var DAEMON_VERSION = true ? "2.0.2-dev.688" : "2.0.0-alpha.0";
|
|
55411
55459
|
var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
|
|
55412
55460
|
var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
|
|
55413
55461
|
var GIT_OP_MUTE_MS = 4e3;
|
|
@@ -55953,6 +56001,36 @@ function createWorkspaceEngine(opts) {
|
|
|
55953
56001
|
}
|
|
55954
56002
|
return { report: r.report, localSkills: r.localSkills };
|
|
55955
56003
|
} catch (err2) {
|
|
56004
|
+
if (isWorkerOom(err2)) {
|
|
56005
|
+
console.warn("[errata] nightly worker hit its heap ceiling \u2014 heavy pass skipped this fire");
|
|
56006
|
+
appendPassLedger(paths.configDir, "graph-rescore", 0, { oomSkipped: 1 });
|
|
56007
|
+
const sem = runSemanticMaintenance(store);
|
|
56008
|
+
return {
|
|
56009
|
+
report: {
|
|
56010
|
+
scoredNodes: 0,
|
|
56011
|
+
iterations: 0,
|
|
56012
|
+
converged: true,
|
|
56013
|
+
landmarks: 0,
|
|
56014
|
+
communities: 0,
|
|
56015
|
+
motifsPromoted: 0,
|
|
56016
|
+
techniques: 0,
|
|
56017
|
+
antipatterns: 0,
|
|
56018
|
+
skillsInduced: 0,
|
|
56019
|
+
skillsRefreshed: 0,
|
|
56020
|
+
pageRankWritten: 0,
|
|
56021
|
+
landmarkFlips: 0,
|
|
56022
|
+
mode: "incremental",
|
|
56023
|
+
regionSize: 0,
|
|
56024
|
+
designResolved: sem.designResolved,
|
|
56025
|
+
revisitsCleared: sem.revisitsCleared,
|
|
56026
|
+
fixCandidatesSettled: sem.fixCandidatesSettled,
|
|
56027
|
+
claimsEvaluated: sem.claimsEvaluated,
|
|
56028
|
+
claimsDerived: sem.claimsDerived,
|
|
56029
|
+
durationMs: sem.durationMs
|
|
56030
|
+
},
|
|
56031
|
+
localSkills: 0
|
|
56032
|
+
};
|
|
56033
|
+
}
|
|
55956
56034
|
console.warn(
|
|
55957
56035
|
"[errata] nightly worker unavailable, running inline:",
|
|
55958
56036
|
err2 instanceof Error ? err2.message : err2
|
|
@@ -56712,20 +56790,38 @@ function createWorkspaceEngine(opts) {
|
|
|
56712
56790
|
turns = readAssistantTurns(transcriptPath);
|
|
56713
56791
|
nextOffset = transcriptSize(transcriptPath);
|
|
56714
56792
|
} else {
|
|
56715
|
-
({ turns, nextOffset } = readAssistantTurnsFrom(transcriptPath, known));
|
|
56793
|
+
({ turns, nextOffset } = readAssistantTurnsFrom(transcriptPath, known, true, HARVEST_SLICE_BYTES));
|
|
56716
56794
|
}
|
|
56717
56795
|
} catch {
|
|
56718
56796
|
return;
|
|
56719
56797
|
}
|
|
56720
|
-
|
|
56721
|
-
|
|
56722
|
-
|
|
56798
|
+
for (let slice = 0; ; slice++) {
|
|
56799
|
+
const fresh = turnsSince(turns, lastTurnUuid.get(sessionId));
|
|
56800
|
+
turnOffset.set(sessionId, nextOffset);
|
|
56801
|
+
if (fresh.length > 0) {
|
|
56802
|
+
lastTurnUuid.set(sessionId, turns[turns.length - 1].uuid);
|
|
56803
|
+
await harvestTexts(sessionId, fresh);
|
|
56804
|
+
}
|
|
56723
56805
|
saveTurnCursors(turnCursorPath, lastTurnUuid, turnOffset);
|
|
56724
|
-
|
|
56806
|
+
if (slice >= 256) {
|
|
56807
|
+
console.warn("[errata] harvest slice guard hit \u2014 backlog resumes next tick");
|
|
56808
|
+
break;
|
|
56809
|
+
}
|
|
56810
|
+
let next;
|
|
56811
|
+
try {
|
|
56812
|
+
next = readAssistantTurnsFrom(transcriptPath, nextOffset, true, HARVEST_SLICE_BYTES);
|
|
56813
|
+
} catch {
|
|
56814
|
+
break;
|
|
56815
|
+
}
|
|
56816
|
+
if (next.turns.length === 0 && !next.more) {
|
|
56817
|
+
turnOffset.set(sessionId, next.nextOffset);
|
|
56818
|
+
saveTurnCursors(turnCursorPath, lastTurnUuid, turnOffset);
|
|
56819
|
+
break;
|
|
56820
|
+
}
|
|
56821
|
+
turns = next.turns;
|
|
56822
|
+
nextOffset = next.nextOffset;
|
|
56823
|
+
await new Promise((resolve6) => setImmediate(resolve6));
|
|
56725
56824
|
}
|
|
56726
|
-
lastTurnUuid.set(sessionId, turns[turns.length - 1].uuid);
|
|
56727
|
-
await harvestTexts(sessionId, fresh);
|
|
56728
|
-
saveTurnCursors(turnCursorPath, lastTurnUuid, turnOffset);
|
|
56729
56825
|
};
|
|
56730
56826
|
const seenMessageIds = /* @__PURE__ */ new Set();
|
|
56731
56827
|
const onMessage = (e) => {
|
|
@@ -58119,7 +58215,12 @@ var ConsolidateWorker = class {
|
|
|
58119
58215
|
ensureWorker() {
|
|
58120
58216
|
if (this.worker) return;
|
|
58121
58217
|
const workerUrl = new URL("./consolidate-worker.mjs", import.meta.url);
|
|
58122
|
-
const worker = new Worker2(workerUrl, {
|
|
58218
|
+
const worker = new Worker2(workerUrl, {
|
|
58219
|
+
workerData: {},
|
|
58220
|
+
execArgv: process.execArgv,
|
|
58221
|
+
// HZ-footprint-cap — see pass-worker-client.ts for the contract.
|
|
58222
|
+
resourceLimits: workerResourceLimits()
|
|
58223
|
+
});
|
|
58123
58224
|
this.worker = worker;
|
|
58124
58225
|
this.ready = new Promise((resolve6, reject) => {
|
|
58125
58226
|
const onReady = (m) => {
|
|
@@ -60248,7 +60349,7 @@ var DEFAULT_CONSOLIDATION_POLICY = {
|
|
|
60248
60349
|
quiescenceMs: 6e4
|
|
60249
60350
|
// sim preferred 60s over 90s (fresher, esp. heavy-code)
|
|
60250
60351
|
};
|
|
60251
|
-
function
|
|
60352
|
+
function numEnv3(env2, key, fallback) {
|
|
60252
60353
|
const raw2 = env2[key];
|
|
60253
60354
|
if (raw2 == null || raw2.trim() === "") return fallback;
|
|
60254
60355
|
const n = Number(raw2);
|
|
@@ -60256,12 +60357,12 @@ function numEnv2(env2, key, fallback) {
|
|
|
60256
60357
|
}
|
|
60257
60358
|
function consolidationPolicyFromEnv(env2 = process.env) {
|
|
60258
60359
|
return {
|
|
60259
|
-
baseFloorMs:
|
|
60260
|
-
dutyFactor:
|
|
60261
|
-
momentumThreshold:
|
|
60262
|
-
momentumRatio:
|
|
60263
|
-
momentumRatioCap:
|
|
60264
|
-
quiescenceMs:
|
|
60360
|
+
baseFloorMs: numEnv3(env2, "ERRATA_CONSOLIDATE_BASE_FLOOR_MS", DEFAULT_CONSOLIDATION_POLICY.baseFloorMs),
|
|
60361
|
+
dutyFactor: numEnv3(env2, "ERRATA_CONSOLIDATE_DUTY_FACTOR", DEFAULT_CONSOLIDATION_POLICY.dutyFactor),
|
|
60362
|
+
momentumThreshold: numEnv3(env2, "ERRATA_CONSOLIDATE_MOMENTUM", DEFAULT_CONSOLIDATION_POLICY.momentumThreshold),
|
|
60363
|
+
momentumRatio: numEnv3(env2, "ERRATA_CONSOLIDATE_MOMENTUM_RATIO", DEFAULT_CONSOLIDATION_POLICY.momentumRatio),
|
|
60364
|
+
momentumRatioCap: numEnv3(env2, "ERRATA_CONSOLIDATE_MOMENTUM_RATIO_CAP", DEFAULT_CONSOLIDATION_POLICY.momentumRatioCap),
|
|
60365
|
+
quiescenceMs: numEnv3(env2, "ERRATA_CONSOLIDATE_QUIESCENCE_MS", DEFAULT_CONSOLIDATION_POLICY.quiescenceMs)
|
|
60265
60366
|
};
|
|
60266
60367
|
}
|
|
60267
60368
|
function consolidationGapMs(lastPassMs, policy) {
|
|
@@ -62160,7 +62261,16 @@ function spawnDaemonDetached() {
|
|
|
62160
62261
|
detached: true,
|
|
62161
62262
|
stdio: ["ignore", out2, out2],
|
|
62162
62263
|
windowsHide: true,
|
|
62163
|
-
cwd: ROOT
|
|
62264
|
+
cwd: ROOT,
|
|
62265
|
+
// HZ-footprint-cap: hard V8 old-space ceiling on the daemon process. The
|
|
62266
|
+
// workers carry their own resourceLimits; this bounds the main isolate
|
|
62267
|
+
// (hook server + capture), whose bursts are slice-capped so the ceiling is
|
|
62268
|
+
// a backstop, not a working limit. NODE_OPTIONS so it applies however the
|
|
62269
|
+
// bundle re-executes.
|
|
62270
|
+
env: {
|
|
62271
|
+
...process.env,
|
|
62272
|
+
NODE_OPTIONS: `${process.env["NODE_OPTIONS"] ?? ""} --max-old-space-size=${DAEMON_MAX_HEAP_MB}`.trim()
|
|
62273
|
+
}
|
|
62164
62274
|
}).unref();
|
|
62165
62275
|
if (out2 !== "ignore") closeSync2(out2);
|
|
62166
62276
|
}
|