@inerrata-corporation/errata 2.0.2-dev.1203 → 2.0.2-dev.1218

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 +207 -10
  2. package/package.json +1 -1
package/errata.mjs CHANGED
@@ -53249,9 +53249,10 @@ init_src10();
53249
53249
  init_review2();
53250
53250
 
53251
53251
  // src/turn.ts
53252
- import { closeSync, existsSync as existsSync13, fstatSync, openSync, readdirSync as readdirSync6, readSync, statSync as statSync4 } from "node:fs";
53252
+ import { closeSync, existsSync as existsSync13, fstatSync, openSync, readdirSync as readdirSync6, readSync, realpathSync, statSync as statSync4 } from "node:fs";
53253
53253
  import { basename as basename3, dirname as dirname8, join as join16 } from "node:path";
53254
53254
  import { homedir as homedir4 } from "node:os";
53255
+ import { createHash as createHash13 } from "node:crypto";
53255
53256
  function readFrom(path2, fromByte, maxBytes) {
53256
53257
  let fd;
53257
53258
  try {
@@ -53417,6 +53418,9 @@ function detectHostHarness(raw2) {
53417
53418
  if (obj.type === "response_item" || obj.type === "event_msg" || obj.type === "session_meta" || obj.type === "turn_context" || obj.type === "world_state") {
53418
53419
  return "codex";
53419
53420
  }
53421
+ if (obj.type === "gemini" || "$set" in obj || "projectHash" in obj && "sessionId" in obj || obj.type === "user" && !("message" in obj) && "content" in obj) {
53422
+ return "gemini";
53423
+ }
53420
53424
  if (obj.type === "user" || obj.type === "assistant" || obj.type === "summary") {
53421
53425
  return "claude_code";
53422
53426
  }
@@ -53425,7 +53429,61 @@ function detectHostHarness(raw2) {
53425
53429
  }
53426
53430
  function parseAssistantTurns(raw2, includeThinking, skipFirstLineParseFailure = false) {
53427
53431
  if (!raw2) return { turns: [], parseFailures: 0 };
53428
- return detectHostHarness(raw2) === "codex" ? parseCodexAssistantTurns(raw2, skipFirstLineParseFailure) : parseClaudeAssistantTurns(raw2, includeThinking, skipFirstLineParseFailure);
53432
+ const harness = detectHostHarness(raw2);
53433
+ if (harness === "codex") return parseCodexAssistantTurns(raw2, skipFirstLineParseFailure);
53434
+ if (harness === "gemini") return parseGeminiAssistantTurns(raw2, skipFirstLineParseFailure);
53435
+ return parseClaudeAssistantTurns(raw2, includeThinking, skipFirstLineParseFailure);
53436
+ }
53437
+ function parseGeminiAssistantTurns(raw2, skipFirstLineParseFailure = false) {
53438
+ const lines = raw2.split(/\r?\n/);
53439
+ const byId = /* @__PURE__ */ new Map();
53440
+ let order = 0;
53441
+ let parseFailures = 0;
53442
+ const textOf = (content) => {
53443
+ if (typeof content === "string") return content;
53444
+ if (Array.isArray(content)) {
53445
+ const parts2 = [];
53446
+ for (const b of content) {
53447
+ if (b && typeof b["text"] === "string") parts2.push(b["text"]);
53448
+ }
53449
+ return parts2.length > 0 ? parts2.join("\n\n") : null;
53450
+ }
53451
+ return null;
53452
+ };
53453
+ const fold = (m, fallbackId) => {
53454
+ if (m["type"] !== "gemini") return;
53455
+ const text = textOf(m["content"]);
53456
+ if (text === null || text.length === 0) return;
53457
+ const id = typeof m["id"] === "string" ? m["id"] : fallbackId;
53458
+ const prev = byId.get(id);
53459
+ byId.set(id, { text, order: prev ? prev.order : order++ });
53460
+ };
53461
+ for (let i2 = 0; i2 < lines.length; i2++) {
53462
+ const line = lines[i2];
53463
+ if (!line) continue;
53464
+ let obj;
53465
+ try {
53466
+ obj = JSON.parse(line);
53467
+ } catch {
53468
+ if (!(i2 === 0 && skipFirstLineParseFailure)) parseFailures++;
53469
+ continue;
53470
+ }
53471
+ if (!obj || typeof obj !== "object") continue;
53472
+ const set2 = obj["$set"];
53473
+ if (set2 && typeof set2 === "object") {
53474
+ const msgs = set2["messages"];
53475
+ if (Array.isArray(msgs)) {
53476
+ for (let j = 0; j < msgs.length; j++) {
53477
+ const m = msgs[j];
53478
+ if (m && typeof m === "object") fold(m, `gemini-${i2}-${j}`);
53479
+ }
53480
+ }
53481
+ continue;
53482
+ }
53483
+ if (typeof obj["type"] === "string") fold(obj, `gemini-${i2}`);
53484
+ }
53485
+ const turns = [...byId.entries()].sort((a, b) => a[1].order - b[1].order).map(([uuid3, v]) => ({ uuid: uuid3, text: v.text, hostHarness: "gemini" }));
53486
+ return { turns, parseFailures };
53429
53487
  }
53430
53488
  function parseCodexAssistantTurns(raw2, skipFirstLineParseFailure = false) {
53431
53489
  const turns = [];
@@ -53666,6 +53724,87 @@ function codexRolloutsForCwd(cwd, opts = {}) {
53666
53724
  out2.sort((a, b) => b.mtimeMs - a.mtimeMs);
53667
53725
  return out2.slice(0, limit);
53668
53726
  }
53727
+ function geminiSessionRoots(env2 = process.env, home = homedir4()) {
53728
+ const roots = /* @__PURE__ */ new Set();
53729
+ const explicit = env2["ERRATA_GEMINI_SESSIONS_DIRS"];
53730
+ if (explicit) {
53731
+ for (const d of explicit.split(":")) if (d.trim()) roots.add(d.trim());
53732
+ }
53733
+ if (env2["GEMINI_CLI_HOME"]) roots.add(join16(env2["GEMINI_CLI_HOME"], ".gemini", "tmp"));
53734
+ roots.add(join16(home, ".gemini", "tmp"));
53735
+ return [...roots];
53736
+ }
53737
+ function geminiProjectHash(cwd) {
53738
+ return createHash13("sha256").update(cwd).digest("hex");
53739
+ }
53740
+ function geminiHashesFor(cwd) {
53741
+ const variants = /* @__PURE__ */ new Set([cwd, cwd.replace(/\/+$/, "")]);
53742
+ try {
53743
+ variants.add(realpathSync(cwd));
53744
+ } catch {
53745
+ }
53746
+ return new Set([...variants].map(geminiProjectHash));
53747
+ }
53748
+ function readGeminiTranscriptProjectHash(path2) {
53749
+ let fd;
53750
+ try {
53751
+ fd = openSync(path2, "r");
53752
+ const CAP = 65536;
53753
+ const buf = Buffer.allocUnsafe(CAP);
53754
+ const n = readSync(fd, buf, 0, CAP, 0);
53755
+ const nl = buf.indexOf(10);
53756
+ const firstLine = buf.toString("utf8", 0, nl >= 0 && nl < n ? nl : n);
53757
+ const o = JSON.parse(firstLine);
53758
+ return typeof o?.projectHash === "string" ? o.projectHash : null;
53759
+ } catch {
53760
+ return null;
53761
+ } finally {
53762
+ if (fd !== void 0) try {
53763
+ closeSync(fd);
53764
+ } catch {
53765
+ }
53766
+ }
53767
+ }
53768
+ function geminiTranscriptsForCwd(cwd, opts = {}) {
53769
+ const sinceMs = opts.sinceMs ?? 0;
53770
+ const limit = opts.limit ?? 25;
53771
+ const hashes = geminiHashesFor(cwd);
53772
+ const out2 = [];
53773
+ for (const root of geminiSessionRoots(opts.env, opts.home)) {
53774
+ if (!existsSync13(root)) continue;
53775
+ let slugs;
53776
+ try {
53777
+ slugs = readdirSync6(root);
53778
+ } catch {
53779
+ continue;
53780
+ }
53781
+ for (const slug2 of slugs) {
53782
+ const chats = join16(root, slug2, "chats");
53783
+ let names;
53784
+ try {
53785
+ names = readdirSync6(chats);
53786
+ } catch {
53787
+ continue;
53788
+ }
53789
+ for (const name2 of names) {
53790
+ if (!name2.startsWith("session-") || !name2.endsWith(".jsonl")) continue;
53791
+ const full = join16(chats, name2);
53792
+ let st;
53793
+ try {
53794
+ st = statSync4(full);
53795
+ } catch {
53796
+ continue;
53797
+ }
53798
+ if (!st.isFile() || st.mtimeMs < sinceMs) continue;
53799
+ const h = readGeminiTranscriptProjectHash(full);
53800
+ if (!h || !hashes.has(h)) continue;
53801
+ out2.push({ path: full, sessionId: `gemini:${basename3(name2, ".jsonl")}`, mtimeMs: st.mtimeMs });
53802
+ }
53803
+ }
53804
+ }
53805
+ out2.sort((a, b) => b.mtimeMs - a.mtimeMs);
53806
+ return out2.slice(0, limit);
53807
+ }
53669
53808
 
53670
53809
  // src/prior-tags.ts
53671
53810
  init_src();
@@ -56339,7 +56478,7 @@ init_paths();
56339
56478
  init_src2();
56340
56479
  init_paths();
56341
56480
  import { existsSync as existsSync21, readFileSync as readFileSync20, writeFileSync as writeFileSync17 } from "node:fs";
56342
- import { createHash as createHash13 } from "node:crypto";
56481
+ import { createHash as createHash14 } from "node:crypto";
56343
56482
  import { join as join25 } from "node:path";
56344
56483
 
56345
56484
  // src/git-remote.ts
@@ -56396,11 +56535,11 @@ function detectRepoLocator(root, remote) {
56396
56535
 
56397
56536
  // src/profile.ts
56398
56537
  function workspaceId(root) {
56399
- return "wp_" + createHash13("sha256").update(root).digest("hex").slice(0, 12);
56538
+ return "wp_" + createHash14("sha256").update(root).digest("hex").slice(0, 12);
56400
56539
  }
56401
56540
  function sessionOriginKey(sessionId) {
56402
56541
  if (!sessionId) return void 0;
56403
- return "ws_" + createHash13("sha256").update(sessionId).digest("hex").slice(0, 12);
56542
+ return "ws_" + createHash14("sha256").update(sessionId).digest("hex").slice(0, 12);
56404
56543
  }
56405
56544
  function refreshRepoLocator(root, profile) {
56406
56545
  const detected = detectRepoLocator(root, profile.repoRemote);
@@ -57065,7 +57204,7 @@ function createWatchBreaker(deps) {
57065
57204
  }
57066
57205
 
57067
57206
  // src/engine.ts
57068
- var DAEMON_VERSION = true ? "2.0.2-dev.1203" : "2.0.0-alpha.0";
57207
+ var DAEMON_VERSION = true ? "2.0.2-dev.1218" : "2.0.0-alpha.0";
57069
57208
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
57070
57209
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
57071
57210
  var GIT_OP_MUTE_MS = 4e3;
@@ -58708,6 +58847,14 @@ function createWorkspaceEngine(opts) {
58708
58847
  await yieldToLoop();
58709
58848
  await harvestSession(ref.sessionId, ref.path);
58710
58849
  }
58850
+ const geminiRefs = geminiTranscriptsForCwd(opts.workspaceRoot, {
58851
+ sinceMs: Date.now() - TURN_REPLAY_LOOKBACK_MS,
58852
+ limit: 25
58853
+ });
58854
+ for (const ref of geminiRefs) {
58855
+ await yieldToLoop();
58856
+ await harvestSession(ref.sessionId, ref.path);
58857
+ }
58711
58858
  } catch {
58712
58859
  }
58713
58860
  })();
@@ -58716,10 +58863,11 @@ function createWorkspaceEngine(opts) {
58716
58863
  setImmediate(() => {
58717
58864
  void (async () => {
58718
58865
  try {
58719
- const refs = codexRolloutsForCwd(opts.workspaceRoot, {
58720
- sinceMs: Date.now() - CODEX_SWEEP_LOOKBACK_MS,
58721
- limit: 10
58722
- });
58866
+ const since = Date.now() - CODEX_SWEEP_LOOKBACK_MS;
58867
+ const refs = [
58868
+ ...codexRolloutsForCwd(opts.workspaceRoot, { sinceMs: since, limit: 10 }),
58869
+ ...geminiTranscriptsForCwd(opts.workspaceRoot, { sinceMs: since, limit: 10 })
58870
+ ];
58723
58871
  for (const ref of refs) {
58724
58872
  await yieldToLoop();
58725
58873
  await harvestSession(ref.sessionId, ref.path);
@@ -60650,6 +60798,26 @@ function createHealthSentinel(deps = {}) {
60650
60798
  };
60651
60799
  }
60652
60800
 
60801
+ // src/codex-wake.ts
60802
+ var CODEX_WAKE_LOOKBACK_MS = 10 * 6e4;
60803
+ var CODEX_WAKE_BOOT_LOOKBACK_MS = 24 * 60 * 6e4;
60804
+ function codexWakeCandidates(input) {
60805
+ if (input.disabled) return [];
60806
+ const find = input.find ?? ((cwd, since) => codexRolloutsForCwd(cwd, { sinceMs: since, limit: 1 }).length + geminiTranscriptsForCwd(cwd, { sinceMs: since, limit: 1 }).length);
60807
+ const out2 = [];
60808
+ for (const entry of input.entries) {
60809
+ if (input.liveRoots.has(input.normPath(entry.path))) continue;
60810
+ let n = 0;
60811
+ try {
60812
+ n = find(entry.path, input.sinceMs);
60813
+ } catch {
60814
+ continue;
60815
+ }
60816
+ if (n > 0) out2.push({ name: entry.name, path: entry.path });
60817
+ }
60818
+ return out2;
60819
+ }
60820
+
60653
60821
  // src/multi.ts
60654
60822
  var projectSymbolSalts = /* @__PURE__ */ new Map();
60655
60823
  async function resolveProjectSymbolSalt(client, projectId) {
@@ -61190,6 +61358,33 @@ async function startMultiDaemon(opts = {}) {
61190
61358
  };
61191
61359
  const idleSweep = setInterval(sweepIdleWorkspaces, WORKSPACE_SWEEP_INTERVAL_MS);
61192
61360
  idleSweep.unref?.();
61361
+ let codexWakeFirstPass = true;
61362
+ const sweepCodexWake = () => {
61363
+ const lookback = codexWakeFirstPass ? CODEX_WAKE_BOOT_LOOKBACK_MS : CODEX_WAKE_LOOKBACK_MS;
61364
+ codexWakeFirstPass = false;
61365
+ let entries2;
61366
+ try {
61367
+ entries2 = listWorkspaceEntries();
61368
+ } catch {
61369
+ return;
61370
+ }
61371
+ const candidates = codexWakeCandidates({
61372
+ entries: entries2,
61373
+ liveRoots: new Set(records.map((r) => normPath(r.root))),
61374
+ normPath,
61375
+ sinceMs: Date.now() - lookback,
61376
+ disabled: process.env["ERRATA_CODEX_WAKE_DISABLE"] === "1"
61377
+ });
61378
+ for (const c of candidates) {
61379
+ if (attachWorkspace(c.path).attached) {
61380
+ console.log(`[errata] woke workspace ${c.name} \u2014 recent Codex/Gemini transcript (headless capture)`);
61381
+ }
61382
+ }
61383
+ };
61384
+ const codexWake = setInterval(sweepCodexWake, WORKSPACE_SWEEP_INTERVAL_MS);
61385
+ codexWake.unref?.();
61386
+ const codexWakeBoot = setTimeout(sweepCodexWake, 5e3);
61387
+ codexWakeBoot.unref?.();
61193
61388
  const reindexAll = async (rOpts) => {
61194
61389
  const out2 = /* @__PURE__ */ new Map();
61195
61390
  const toIndex = records.filter((r) => rOpts?.force || r.engine.store.nodeCount() === 0);
@@ -61903,6 +62098,8 @@ async function startMultiDaemon(opts = {}) {
61903
62098
  },
61904
62099
  async stop() {
61905
62100
  clearInterval(idleSweep);
62101
+ clearInterval(codexWake);
62102
+ clearTimeout(codexWakeBoot);
61906
62103
  try {
61907
62104
  const cur = readFileSync27(lockPath, "utf8");
61908
62105
  if (JSON.parse(cur).pid === process.pid) unlinkSync3(lockPath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.2-dev.1203",
3
+ "version": "2.0.2-dev.1218",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {