@inerrata-corporation/errata 2.0.2-dev.1066 → 2.0.2-dev.1109

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.
Files changed (2) hide show
  1. package/errata.mjs +228 -37
  2. package/package.json +1 -1
package/errata.mjs CHANGED
@@ -19166,7 +19166,8 @@ function ingestDesignProblem(store, flag, opts) {
19166
19166
  designProblemId: problemId,
19167
19167
  sources: [opts.source],
19168
19168
  corroborations: 0,
19169
- scope: {}
19169
+ scope: {},
19170
+ ...opts.hostHarness ? { hostHarness: opts.hostHarness } : {}
19170
19171
  })
19171
19172
  );
19172
19173
  }
@@ -53100,7 +53101,64 @@ function readAssistantTurnsFrom(transcriptPath, fromByte, includeThinking = true
53100
53101
  losses: { parseFailures: parsed.parseFailures, ioFailed, bytesUnreachable: 0 }
53101
53102
  };
53102
53103
  }
53104
+ function detectHostHarness(raw2) {
53105
+ const lines = raw2.split(/\r?\n/);
53106
+ let checked = 0;
53107
+ for (const line of lines) {
53108
+ if (!line.trim()) continue;
53109
+ if (checked++ >= 5) break;
53110
+ let obj;
53111
+ try {
53112
+ obj = JSON.parse(line);
53113
+ } catch {
53114
+ continue;
53115
+ }
53116
+ if (obj.type === "response_item" || obj.type === "event_msg" || obj.type === "session_meta" || obj.type === "turn_context" || obj.type === "world_state") {
53117
+ return "codex";
53118
+ }
53119
+ if (obj.type === "user" || obj.type === "assistant" || obj.type === "summary") {
53120
+ return "claude_code";
53121
+ }
53122
+ }
53123
+ return "claude_code";
53124
+ }
53103
53125
  function parseAssistantTurns(raw2, includeThinking, skipFirstLineParseFailure = false) {
53126
+ if (!raw2) return { turns: [], parseFailures: 0 };
53127
+ return detectHostHarness(raw2) === "codex" ? parseCodexAssistantTurns(raw2, skipFirstLineParseFailure) : parseClaudeAssistantTurns(raw2, includeThinking, skipFirstLineParseFailure);
53128
+ }
53129
+ function parseCodexAssistantTurns(raw2, skipFirstLineParseFailure = false) {
53130
+ const turns = [];
53131
+ const lines = raw2.split(/\r?\n/);
53132
+ let parseFailures = 0;
53133
+ for (let i2 = 0; i2 < lines.length; i2++) {
53134
+ const line = lines[i2];
53135
+ if (!line) continue;
53136
+ let obj;
53137
+ try {
53138
+ obj = JSON.parse(line);
53139
+ } catch {
53140
+ if (!(i2 === 0 && skipFirstLineParseFailure)) parseFailures++;
53141
+ continue;
53142
+ }
53143
+ if (obj.type !== "response_item") continue;
53144
+ const payload = obj.payload;
53145
+ if (!payload || payload["type"] !== "message" || payload["role"] !== "assistant") continue;
53146
+ const content = payload["content"];
53147
+ if (!Array.isArray(content)) continue;
53148
+ const parts2 = [];
53149
+ for (const b of content) {
53150
+ if (b["type"] === "output_text" && typeof b["text"] === "string") parts2.push(b["text"]);
53151
+ }
53152
+ if (parts2.length === 0) continue;
53153
+ turns.push({
53154
+ uuid: typeof payload["id"] === "string" ? payload["id"] : `codex-${i2}`,
53155
+ text: parts2.join("\n\n"),
53156
+ hostHarness: "codex"
53157
+ });
53158
+ }
53159
+ return { turns, parseFailures };
53160
+ }
53161
+ function parseClaudeAssistantTurns(raw2, includeThinking, skipFirstLineParseFailure = false) {
53104
53162
  if (!raw2) return { turns: [], parseFailures: 0 };
53105
53163
  const turns = [];
53106
53164
  const lines = raw2.split(/\r?\n/);
@@ -53154,6 +53212,7 @@ function parseAssistantTurns(raw2, includeThinking, skipFirstLineParseFailure =
53154
53212
  turns.push({
53155
53213
  uuid: String(obj.uuid ?? i2),
53156
53214
  text: parts2.join("\n\n"),
53215
+ hostHarness: "claude_code",
53157
53216
  ...lastFile ? { workingFile: lastFile } : {},
53158
53217
  ...provenance ? { workingFileProvenance: provenance } : {},
53159
53218
  ...editedFile && editedFileTurnSeq === turnSeq ? { editedFile } : {},
@@ -53221,6 +53280,91 @@ function subagentTranscripts(mainTranscriptPath, sessionId) {
53221
53280
  refs.sort((a, b) => b.mtimeMs - a.mtimeMs);
53222
53281
  return refs;
53223
53282
  }
53283
+ function codexSessionRoots(env2 = process.env, home = homedir4()) {
53284
+ const roots = /* @__PURE__ */ new Set();
53285
+ const explicit = env2["ERRATA_CODEX_SESSIONS_DIRS"];
53286
+ if (explicit) {
53287
+ for (const d of explicit.split(":")) if (d.trim()) roots.add(d.trim());
53288
+ }
53289
+ if (env2["CODEX_HOME"]) roots.add(join16(env2["CODEX_HOME"], "sessions"));
53290
+ roots.add(join16(home, ".codex", "sessions"));
53291
+ return [...roots];
53292
+ }
53293
+ function readCodexRolloutCwd(path2) {
53294
+ let fd;
53295
+ try {
53296
+ fd = openSync(path2, "r");
53297
+ const CAP = 1e6;
53298
+ const chunk = 65536;
53299
+ let acc = Buffer.alloc(0);
53300
+ let pos = 0;
53301
+ let nl = -1;
53302
+ while (acc.length < CAP) {
53303
+ const buf = Buffer.allocUnsafe(chunk);
53304
+ const n = readSync(fd, buf, 0, chunk, pos);
53305
+ if (n <= 0) break;
53306
+ acc = acc.length === 0 ? buf.subarray(0, n) : Buffer.concat([acc, buf.subarray(0, n)]);
53307
+ pos += n;
53308
+ nl = acc.indexOf(10);
53309
+ if (nl >= 0) break;
53310
+ }
53311
+ const firstLine = acc.toString("utf8", 0, nl >= 0 ? nl : acc.length);
53312
+ try {
53313
+ const o = JSON.parse(firstLine);
53314
+ const meta3 = o?.type === "session_meta" ? o.payload ?? o : o.payload ?? o;
53315
+ const cwd = meta3?.cwd ?? o?.cwd;
53316
+ if (typeof cwd === "string") return cwd;
53317
+ } catch {
53318
+ }
53319
+ const m = firstLine.match(/"cwd"\s*:\s*"((?:[^"\\]|\\.)*)"/);
53320
+ return m ? JSON.parse(`"${m[1]}"`) : null;
53321
+ } catch {
53322
+ return null;
53323
+ } finally {
53324
+ if (fd !== void 0) try {
53325
+ closeSync(fd);
53326
+ } catch {
53327
+ }
53328
+ }
53329
+ }
53330
+ function codexRolloutsForCwd(cwd, opts = {}) {
53331
+ const sinceMs = opts.sinceMs ?? 0;
53332
+ const limit = opts.limit ?? 25;
53333
+ const out2 = [];
53334
+ for (const root of codexSessionRoots(opts.env, opts.home)) {
53335
+ if (!existsSync13(root)) continue;
53336
+ const stack = [{ dir: root, depth: 0 }];
53337
+ while (stack.length > 0) {
53338
+ const { dir, depth } = stack.pop();
53339
+ let names;
53340
+ try {
53341
+ names = readdirSync6(dir);
53342
+ } catch {
53343
+ continue;
53344
+ }
53345
+ for (const name2 of names) {
53346
+ const full = join16(dir, name2);
53347
+ let st;
53348
+ try {
53349
+ st = statSync4(full);
53350
+ } catch {
53351
+ continue;
53352
+ }
53353
+ if (st.isDirectory()) {
53354
+ if (depth < 3) stack.push({ dir: full, depth: depth + 1 });
53355
+ continue;
53356
+ }
53357
+ if (depth < 3) continue;
53358
+ if (!name2.startsWith("rollout-") || !name2.endsWith(".jsonl")) continue;
53359
+ if (st.mtimeMs < sinceMs) continue;
53360
+ if (readCodexRolloutCwd(full) !== cwd) continue;
53361
+ out2.push({ path: full, sessionId: `codex:${basename3(name2, ".jsonl")}`, mtimeMs: st.mtimeMs });
53362
+ }
53363
+ }
53364
+ }
53365
+ out2.sort((a, b) => b.mtimeMs - a.mtimeMs);
53366
+ return out2.slice(0, limit);
53367
+ }
53224
53368
 
53225
53369
  // src/prior-tags.ts
53226
53370
  init_src();
@@ -56442,13 +56586,14 @@ function createLivenessWatch(deps) {
56442
56586
  }
56443
56587
 
56444
56588
  // src/engine.ts
56445
- var DAEMON_VERSION = true ? "2.0.2-dev.1066" : "2.0.0-alpha.0";
56589
+ var DAEMON_VERSION = true ? "2.0.2-dev.1109" : "2.0.0-alpha.0";
56446
56590
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
56447
56591
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
56448
56592
  var GIT_OP_MUTE_MS = 4e3;
56449
56593
  var REINDEX_DEBOUNCE_MS = 200;
56450
56594
  var IDENTITY_AUDIT_MAX_BYTES = 16 * 1024 * 1024;
56451
56595
  var TURN_REPLAY_LOOKBACK_MS = 7 * 24 * 60 * 6e4;
56596
+ var CODEX_SWEEP_LOOKBACK_MS = 10 * 6e4;
56452
56597
  function appendIdentityAudit(path2, record2, line) {
56453
56598
  if (!record2.accepted && record2.score <= 0) return;
56454
56599
  try {
@@ -57308,7 +57453,8 @@ function createWorkspaceEngine(opts) {
57308
57453
  const r = ingestDesignProblem(store, flag, {
57309
57454
  workspaceId: profile.id,
57310
57455
  source: sessionId,
57311
- ts: t
57456
+ ts: t,
57457
+ hostHarness: turn.hostHarness
57312
57458
  });
57313
57459
  if (r.created || r.corroborated) minted++;
57314
57460
  else if (r.rejected) tagsRejected++;
@@ -57427,7 +57573,8 @@ function createWorkspaceEngine(opts) {
57427
57573
  const r = ingestDesignProblem(store, { problem: p.statement, kind: p.kind }, {
57428
57574
  workspaceId: profile.id,
57429
57575
  source: sessionId,
57430
- ts: t
57576
+ ts: t,
57577
+ hostHarness: turn.hostHarness
57431
57578
  });
57432
57579
  if (r.created || r.corroborated) {
57433
57580
  minted++;
@@ -57965,10 +58112,35 @@ function createWorkspaceEngine(opts) {
57965
58112
  await yieldToLoop();
57966
58113
  await harvestSession(ref.sessionId, ref.path);
57967
58114
  }
58115
+ const codexRefs = codexRolloutsForCwd(opts.workspaceRoot, {
58116
+ sinceMs: Date.now() - TURN_REPLAY_LOOKBACK_MS,
58117
+ limit: 25
58118
+ });
58119
+ for (const ref of codexRefs) {
58120
+ await yieldToLoop();
58121
+ await harvestSession(ref.sessionId, ref.path);
58122
+ }
57968
58123
  } catch {
57969
58124
  }
57970
58125
  })();
57971
58126
  });
58127
+ const sweepCodexRollouts = () => {
58128
+ setImmediate(() => {
58129
+ void (async () => {
58130
+ try {
58131
+ const refs = codexRolloutsForCwd(opts.workspaceRoot, {
58132
+ sinceMs: Date.now() - CODEX_SWEEP_LOOKBACK_MS,
58133
+ limit: 10
58134
+ });
58135
+ for (const ref of refs) {
58136
+ await yieldToLoop();
58137
+ await harvestSession(ref.sessionId, ref.path);
58138
+ }
58139
+ } catch {
58140
+ }
58141
+ })();
58142
+ });
58143
+ };
57972
58144
  const designRollup = opts.designRollup ?? (process.env["ERRATA_ROLLUP"] === "1" ? haikuDesignRollup(process.env["ANTHROPIC_API_KEY"] ?? null) : void 0);
57973
58145
  const onSessionEnd = (e) => {
57974
58146
  if (!designRollup) return;
@@ -58107,6 +58279,7 @@ function createWorkspaceEngine(opts) {
58107
58279
  refreshContextNow();
58108
58280
  },
58109
58281
  async tick() {
58282
+ sweepCodexRollouts();
58110
58283
  maybeRefreshRemotePriors();
58111
58284
  const report = {
58112
58285
  generalizerEventsProcessed: 0,
@@ -61767,6 +61940,45 @@ function consolidationGapMs(lastPassMs, policy) {
61767
61940
  return Math.max(policy.baseFloorMs, lastPassMs * policy.dutyFactor);
61768
61941
  }
61769
61942
 
61943
+ // src/hook-commands.ts
61944
+ function hookCurlCommand(port, path2 = "/api/hook") {
61945
+ const url2 = `http://127.0.0.1:${port}${path2}`;
61946
+ return process.platform === "win32" ? `cmd //c "curl -s --connect-timeout 1 --max-time 3 -X POST -H \\"Content-Type: application/json\\" --data-binary @- ${url2} >NUL 2>NUL || exit /b 0"` : `curl -s --connect-timeout 1 --max-time 3 -X POST -H 'Content-Type: application/json' --data-binary @- ${url2} >/dev/null 2>&1 || true`;
61947
+ }
61948
+ function hookRelayCommand(port, path2) {
61949
+ const url2 = `http://127.0.0.1:${port}${path2}`;
61950
+ return process.platform === "win32" ? `cmd //c "curl -s --connect-timeout 1 --max-time 2 -X POST -H \\"Content-Type: application/json\\" --data-binary @- ${url2} 2>NUL || echo {}"` : `curl -s --connect-timeout 1 --max-time 2 -X POST -H 'Content-Type: application/json' --data-binary @- ${url2} 2>/dev/null || echo '{}'`;
61951
+ }
61952
+ var CODEX_HOOKS_BEGIN = `# >>> errata hooks (errata-managed)`;
61953
+ var CODEX_HOOKS_END = `# <<< errata hooks`;
61954
+ function buildCodexHooksToml(port) {
61955
+ const cmd2 = hookCurlCommand(port).replace(/"/g, '\\"');
61956
+ const turnCmd = hookCurlCommand(port, "/api/turn").replace(/"/g, '\\"');
61957
+ return `${CODEX_HOOKS_BEGIN}
61958
+ [[hooks.PreToolUse]]
61959
+ matcher = ".*"
61960
+
61961
+ [[hooks.PreToolUse.hooks]]
61962
+ type = "command"
61963
+ command = "${cmd2}"
61964
+ timeout = 10
61965
+
61966
+ [[hooks.PostToolUse]]
61967
+ matcher = ".*"
61968
+
61969
+ [[hooks.PostToolUse.hooks]]
61970
+ type = "command"
61971
+ command = "${cmd2}"
61972
+ timeout = 10
61973
+
61974
+ [[hooks.PostToolUse.hooks]]
61975
+ type = "command"
61976
+ command = "${turnCmd}"
61977
+ timeout = 10
61978
+ ${CODEX_HOOKS_END}
61979
+ `;
61980
+ }
61981
+
61770
61982
  // src/cli.ts
61771
61983
  var exitCleanOnEpipe = (err2) => {
61772
61984
  if (err2.code === "EPIPE") process.exit(0);
@@ -63858,14 +64070,6 @@ function errataMcpInvocation() {
63858
64070
  const { cmd: cmd2, args: args2 } = selfArgv("mcp");
63859
64071
  return { command: cmd2, args: args2 };
63860
64072
  }
63861
- function hookCurlCommand(port, path2 = "/api/hook") {
63862
- const url2 = `http://127.0.0.1:${port}${path2}`;
63863
- return process.platform === "win32" ? `cmd //c "curl -s --connect-timeout 1 --max-time 3 -X POST -H \\"Content-Type: application/json\\" --data-binary @- ${url2} >NUL 2>NUL || exit /b 0"` : `curl -s --connect-timeout 1 --max-time 3 -X POST -H 'Content-Type: application/json' --data-binary @- ${url2} >/dev/null 2>&1 || true`;
63864
- }
63865
- function hookRelayCommand(port, path2) {
63866
- const url2 = `http://127.0.0.1:${port}${path2}`;
63867
- return process.platform === "win32" ? `cmd //c "curl -s --connect-timeout 1 --max-time 2 -X POST -H \\"Content-Type: application/json\\" --data-binary @- ${url2} 2>NUL || echo {}"` : `curl -s --connect-timeout 1 --max-time 2 -X POST -H 'Content-Type: application/json' --data-binary @- ${url2} 2>/dev/null || echo '{}'`;
63868
- }
63869
64073
  async function installClaudeHooks(port) {
63870
64074
  const { mkdirSync: mkdirSync9, existsSync: existsSync31, readFileSync: readFileSync29, writeFileSync: writeFileSync23 } = await import("node:fs");
63871
64075
  const { join: join33 } = await import("node:path");
@@ -63987,45 +64191,32 @@ async function installCodexHooks(port) {
63987
64191
  const dir = join33(ROOT, ".codex");
63988
64192
  if (!existsSync31(dir)) mkdirSync9(dir, { recursive: true });
63989
64193
  const file2 = join33(dir, "config.toml");
63990
- const BEGIN = `# >>> errata hooks (errata-managed)`;
63991
- const END = `# <<< errata hooks`;
63992
64194
  let existing = "";
63993
64195
  if (existsSync31(file2)) {
63994
64196
  existing = readFileSync29(file2, "utf8");
63995
- const beginIdx = existing.indexOf(BEGIN);
63996
- const endIdx = existing.indexOf(END);
64197
+ const beginIdx = existing.indexOf(CODEX_HOOKS_BEGIN);
64198
+ const endIdx = existing.indexOf(CODEX_HOOKS_END);
63997
64199
  if (beginIdx >= 0 && endIdx > beginIdx) {
63998
- existing = existing.slice(0, beginIdx).trimEnd() + existing.slice(endIdx + END.length).trimStart();
64200
+ existing = existing.slice(0, beginIdx).trimEnd() + existing.slice(endIdx + CODEX_HOOKS_END.length).trimStart();
63999
64201
  }
64000
64202
  }
64001
- const cmd2 = hookCurlCommand(port).replace(/"/g, '\\"');
64002
- const block = `${BEGIN}
64003
- [[hooks.PreToolUse]]
64004
- matcher = ".*"
64005
-
64006
- [[hooks.PreToolUse.hooks]]
64007
- type = "command"
64008
- command = "${cmd2}"
64009
- timeout = 10
64010
-
64011
- [[hooks.PostToolUse]]
64012
- matcher = ".*"
64013
-
64014
- [[hooks.PostToolUse.hooks]]
64015
- type = "command"
64016
- command = "${cmd2}"
64017
- timeout = 10
64018
- ${END}
64019
- `;
64203
+ const block = buildCodexHooksToml(port);
64020
64204
  const final = existing.length > 0 && !existing.endsWith("\n") ? `${existing}
64021
64205
 
64022
64206
  ${block}` : existing + (existing.endsWith("\n\n") ? "" : "\n") + block;
64023
64207
  writeFileSync23(file2, final, "utf8");
64024
64208
  console.log(`installed Codex hooks \u2192 ${file2}`);
64025
- console.log(` endpoint: http://127.0.0.1:${port}/api/hook`);
64209
+ console.log(` endpoint: http://127.0.0.1:${port}/api/hook (+ /api/turn on PostToolUse)`);
64026
64210
  console.log("");
64027
64211
  console.log(` Codex reads files via the shell, so the virtual graph surface`);
64028
64212
  console.log(` works by \`cat .errata/g/<verb>/<seed>\` (e.g. cat .errata/g/burst/foo).`);
64213
+ console.log("");
64214
+ console.log(` Codex has no Stop/SessionEnd hook at this installed version, so`);
64215
+ console.log(` [!\u2026]/(fix:[\u2026]) tags are harvested via the PostToolUse-triggered`);
64216
+ console.log(` /api/turn call above instead \u2014 every turn with a tool call is`);
64217
+ console.log(` covered; a tool-less final turn is picked up on this workspace's`);
64218
+ console.log(` next daemon boot replay. Re-run this after a Codex upgrade in`);
64219
+ console.log(` case a newer version adds a real turn-boundary hook.`);
64029
64220
  console.log(` If [features] hooks=false in your config.toml, set it true. Hook`);
64030
64221
  console.log(` schemas evolve \u2014 if hooks stop firing after an update, re-run this.`);
64031
64222
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.2-dev.1066",
3
+ "version": "2.0.2-dev.1109",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {