@inerrata-corporation/errata 2.0.6-dev.2752 → 2.0.6-dev.2759

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 +122 -11
  2. package/package.json +1 -1
package/errata.mjs CHANGED
@@ -71189,7 +71189,7 @@ var init_report_render = __esm({
71189
71189
  init_src6();
71190
71190
  import { closeSync as closeSync2, existsSync as existsSync34, openSync as openSync2, readFileSync as readFileSync35, renameSync as renameSync4, statSync as statSync8 } from "node:fs";
71191
71191
  import { join as join40, resolve as pathResolve2 } from "node:path";
71192
- import { spawn as spawn4 } from "node:child_process";
71192
+ import { spawn as spawn5 } from "node:child_process";
71193
71193
 
71194
71194
  // src/daemon.ts
71195
71195
  import { existsSync as existsSync26, writeFileSync as writeFileSync22 } from "node:fs";
@@ -79907,7 +79907,7 @@ function createWatchBreaker(deps) {
79907
79907
  }
79908
79908
 
79909
79909
  // src/engine.ts
79910
- var DAEMON_VERSION = true ? "2.0.6-dev.2752" : "2.0.0-alpha.0";
79910
+ var DAEMON_VERSION = true ? "2.0.6-dev.2759" : "2.0.0-alpha.0";
79911
79911
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
79912
79912
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
79913
79913
  var GIT_OP_MUTE_MS = 4e3;
@@ -85577,6 +85577,7 @@ var SessionAffinity = class {
85577
85577
  };
85578
85578
 
85579
85579
  // src/multi.ts
85580
+ init_checkout_root();
85580
85581
  var PROCESS_STARTED_AT = Date.now();
85581
85582
  var SELF_ENTRY = process.argv[1] ? pathResolve(process.argv[1]) : "";
85582
85583
  var LOCAL_BUILD_ENTRY = SELF_ENTRY && !isInstalledEntry(SELF_ENTRY) ? SELF_ENTRY : void 0;
@@ -86124,6 +86125,19 @@ async function startMultiDaemon(opts = {}) {
86124
86125
  if (owner) break;
86125
86126
  owner = ownerOf(records, p);
86126
86127
  }
86128
+ if (!owner) {
86129
+ for (const p of [primary, ...others]) {
86130
+ if (!p) continue;
86131
+ const gitRoot = nearestGitRoot(p);
86132
+ const main2 = gitRoot ? mainCheckoutOfWorktree(gitRoot) : null;
86133
+ if (!main2) continue;
86134
+ owner = ownerOf(records, main2);
86135
+ if (!owner && listWorkspaceEntries().some((e) => pickOwnerRoot([e.path], main2))) {
86136
+ if (attachWorkspace(main2).attached) owner = ownerOf(records, main2);
86137
+ }
86138
+ if (owner) break;
86139
+ }
86140
+ }
86127
86141
  if (!owner && others.length > 0) {
86128
86142
  const registryRoots = listWorkspaceEntries().map((e) => e.path);
86129
86143
  const registered = others.map((p) => pickOwnerRoot(registryRoots, p)).find((r) => r != null);
@@ -87552,9 +87566,9 @@ function deriveAuthBase(verificationUri) {
87552
87566
  }
87553
87567
  function openInBrowser(url2) {
87554
87568
  try {
87555
- void import("node:child_process").then(({ spawn: spawn5 }) => {
87569
+ void import("node:child_process").then(({ spawn: spawn6 }) => {
87556
87570
  const [cmd2, args2] = process.platform === "win32" ? ["cmd", ["/c", "start", "", url2]] : process.platform === "darwin" ? ["open", [url2]] : ["xdg-open", [url2]];
87557
- spawn5(cmd2, args2, { detached: true, stdio: "ignore" }).unref();
87571
+ spawn6(cmd2, args2, { detached: true, stdio: "ignore" }).unref();
87558
87572
  });
87559
87573
  } catch {
87560
87574
  }
@@ -87634,6 +87648,7 @@ function installedHarnessVersion(bin) {
87634
87648
  }
87635
87649
  async function diagnoseInstallation(cfg, dependencies) {
87636
87650
  const checks = [];
87651
+ if (dependencies.codexHooks) checks.push(...await dependencies.codexHooks());
87637
87652
  const profileName = cfg.activeInstallationProfile;
87638
87653
  const profile = profileName ? cfg.installationProfiles[profileName] : void 0;
87639
87654
  checks.push(
@@ -87727,6 +87742,97 @@ async function diagnoseInstallation(cfg, dependencies) {
87727
87742
  };
87728
87743
  }
87729
87744
 
87745
+ // src/codex-doctor.ts
87746
+ import { spawn as spawn4 } from "node:child_process";
87747
+ import { createInterface } from "node:readline";
87748
+ function readCodexHookState(cwd, options = {}) {
87749
+ return new Promise((resolve7, reject) => {
87750
+ const child = spawn4(options.bin ?? "codex", options.args ?? ["app-server", "--stdio"], {
87751
+ cwd,
87752
+ windowsHide: true,
87753
+ stdio: ["pipe", "pipe", "pipe"]
87754
+ });
87755
+ const lines = createInterface({ input: child.stdout });
87756
+ let settled = false;
87757
+ let size = 0;
87758
+ let hooks;
87759
+ let features;
87760
+ const finish = (error48) => {
87761
+ if (settled) return;
87762
+ settled = true;
87763
+ clearTimeout(timer);
87764
+ lines.close();
87765
+ child.kill();
87766
+ if (error48) reject(error48);
87767
+ else resolve7({ ...hooks, features });
87768
+ };
87769
+ const timer = setTimeout(() => finish(new Error("Codex hook inspection timed out")), options.timeoutMs ?? 1e4);
87770
+ const send = (id, method, params) => {
87771
+ child.stdin.write(JSON.stringify({ id, method, params }) + "\n");
87772
+ };
87773
+ child.on("error", () => finish(new Error("Codex could not be started for hook inspection")));
87774
+ child.on("exit", () => finish(new Error("Codex exited before returning its hook state")));
87775
+ child.stdin.on("error", () => finish(new Error("Codex hook inspection connection closed")));
87776
+ child.stderr.resume();
87777
+ child.stdout.on("data", (chunk) => {
87778
+ size += chunk.length;
87779
+ if (size > 2e6) finish(new Error("Codex hook inspection exceeded its response budget"));
87780
+ });
87781
+ lines.on("line", (line) => {
87782
+ if (settled) return;
87783
+ let message2;
87784
+ try {
87785
+ message2 = JSON.parse(line);
87786
+ } catch {
87787
+ return;
87788
+ }
87789
+ if (![1, 2, 3].includes(message2.id)) return;
87790
+ if (message2.error) return finish(new Error("Codex does not support hook inspection; update Codex and review /hooks"));
87791
+ if (message2.id === 1) {
87792
+ child.stdin.write(JSON.stringify({ method: "initialized" }) + "\n");
87793
+ send(2, "hooks/list", { cwds: [cwd] });
87794
+ send(3, "config/read", { cwd, includeLayers: false });
87795
+ } else if (message2.id === 2) {
87796
+ const entry = message2.result?.data?.[0];
87797
+ if (!Array.isArray(entry?.hooks)) return finish(new Error("Codex returned no hook inspection result"));
87798
+ hooks = { hooks: entry.hooks, errors: entry.errors };
87799
+ } else {
87800
+ if (!message2.result?.config) return finish(new Error("Codex returned no effective configuration"));
87801
+ features = message2.result.config.features ?? {};
87802
+ }
87803
+ if (hooks && features) finish();
87804
+ });
87805
+ send(1, "initialize", {
87806
+ clientInfo: { name: "errata_doctor", version: "1.0.0" },
87807
+ capabilities: { experimentalApi: true }
87808
+ });
87809
+ });
87810
+ }
87811
+ function assessCodexHooks(state) {
87812
+ const check2 = (status, detail) => [{ id: "codex_hooks", status, detail }];
87813
+ if (state.errors?.length) return check2("fail", "Codex could not load hook configuration; inspect /hooks for errors");
87814
+ if ((state.features?.hooks ?? state.features?.codex_hooks) === false) {
87815
+ return check2("fail", "Codex hooks are explicitly disabled; left unchanged");
87816
+ }
87817
+ const hooks = state.hooks.filter((h) => h.command?.includes("errata-relay.mjs"));
87818
+ const required2 = ["sessionStart", "userPromptSubmit", "preToolUse", "postToolUse", "stop"];
87819
+ const missing = required2.filter((event) => !hooks.some((h) => h.eventName === event));
87820
+ const checks = [];
87821
+ if (missing.length) checks.push(...check2("fail", `Errata hooks missing or outdated in this checkout (${missing.join(", ")}); run errata install-hooks codex here`));
87822
+ if (hooks.some((h) => !h.enabled)) checks.push(...check2("fail", "Errata hooks are disabled in Codex; review /hooks; left unchanged"));
87823
+ if (hooks.some((h) => !["trusted", "managed"].includes(h.trustStatus))) {
87824
+ checks.push(...check2("fail", "Errata hooks need review/trust in Codex /hooks before they can run; project trust is separate"));
87825
+ }
87826
+ return checks.length ? checks : check2("pass", "Codex discovers enabled, trusted Errata hooks; verify live context and capture in a session");
87827
+ }
87828
+ async function diagnoseCodexHooks(cwd) {
87829
+ try {
87830
+ return assessCodexHooks(await readCodexHookState(cwd));
87831
+ } catch (error48) {
87832
+ return [{ id: "codex_hooks", status: "fail", detail: error48 instanceof Error ? error48.message : "Cannot verify Codex hooks" }];
87833
+ }
87834
+ }
87835
+
87730
87836
  // src/whoami.ts
87731
87837
  async function resolveWhoami(client, actingHandle) {
87732
87838
  return actingHandle ? client.contextAs(actingHandle) : client.context();
@@ -87819,8 +87925,8 @@ function buildCodexHooksToml(port, options) {
87819
87925
  };
87820
87926
  if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error("Invalid hook port");
87821
87927
  const command = quote(options.nodePath) + " " + quote(options.relayPath) + " " + port;
87822
- const handlers = ["SessionStart", "UserPromptSubmit", "PreToolUse", "PostToolUse"].map(
87823
- (event) => "[[hooks." + event + "]]\n" + (event === "UserPromptSubmit" ? "" : 'matcher = ".*"\n') + "\n[[hooks." + event + '.hooks]]\ntype = "command"\ncommand = ' + JSON.stringify(command) + "\ntimeout = 10\nadditionalContextLimit = 5000\n"
87928
+ const handlers = ["SessionStart", "UserPromptSubmit", "PreToolUse", "PostToolUse", "Stop"].map(
87929
+ (event) => "[[hooks." + event + "]]\n" + (["UserPromptSubmit", "Stop"].includes(event) ? "" : 'matcher = ".*"\n') + "\n[[hooks." + event + '.hooks]]\ntype = "command"\ncommand = ' + JSON.stringify(command) + "\ntimeout = 10\nadditionalContextLimit = 5000\n"
87824
87930
  );
87825
87931
  return CODEX_HOOKS_BEGIN + "\n" + handlers.join("\n") + CODEX_HOOKS_END + "\n";
87826
87932
  }
@@ -87853,7 +87959,8 @@ try {
87853
87959
  } catch { /* An unavailable daemon must never block the agent. */ }
87854
87960
  };
87855
87961
  if (event === 'PreToolUse' || event === 'PostToolUse') await post('hook', true);
87856
- if (event === 'PostToolUse') await post('turn');
87962
+ // Stop sees the final assistant message, including turns with no tools.
87963
+ if (event === 'PostToolUse' || event === 'Stop') await post('turn');
87857
87964
  if (['SessionStart', 'UserPromptSubmit', 'PostToolUse'].includes(event)) {
87858
87965
  // Run after capture, so changes already rendered by the daemon arrive now.
87859
87966
  // Background extraction finishing later arrives at the next tool boundary.
@@ -88752,6 +88859,7 @@ async function cmdWhoami(args2) {
88752
88859
  async function cmdDoctor(args2) {
88753
88860
  const cfg = loadConfig();
88754
88861
  const report = await diagnoseInstallation(cfg, {
88862
+ ...hasCodexConfig(process.cwd()) ? { codexHooks: () => diagnoseCodexHooks(process.cwd()) } : {},
88755
88863
  inspect: () => inspectCloudEndpoint(cfg.cloudUrl),
88756
88864
  authenticate: async () => {
88757
88865
  const me = await authedCloudClient(cfg).me();
@@ -90197,7 +90305,7 @@ function spawnDaemonDetached(options = {}) {
90197
90305
  out2 = openSync2(logPath, "a");
90198
90306
  } catch {
90199
90307
  }
90200
- spawn4(cmd2, args2, {
90308
+ spawn5(cmd2, args2, {
90201
90309
  detached: true,
90202
90310
  stdio: ["ignore", out2, out2],
90203
90311
  windowsHide: true,
@@ -90419,12 +90527,15 @@ async function installCursorMcpConfig() {
90419
90527
  console.log(` \u26A0\uFE0F reload Cursor (Cmd/Ctrl-Shift-P \u2192 "Reload Window") to pick it up.`);
90420
90528
  }
90421
90529
  async function installCodexHooks(port) {
90422
- const file2 = writeCodexHooks(ROOT, port);
90530
+ const gitRoot = nearestGitRoot(process.cwd());
90531
+ const hookRoot = gitRoot && mainCheckoutOfWorktree(gitRoot) ? gitRoot : ROOT;
90532
+ const file2 = writeCodexHooks(hookRoot, port);
90423
90533
  console.log(`installed Codex hooks \u2192 ${file2}`);
90424
90534
  console.log(" Context loads on SessionStart/UserPromptSubmit and refreshes after tools when changed.");
90425
- console.log(" Tool capture and /api/turn harvesting remain enabled.");
90535
+ console.log(" Tool and final-reply capture are configured; hooks must be trusted before they run.");
90426
90536
  console.log(" Restart/resume Codex to reload config, then review and trust the hooks with /hooks.");
90427
- console.log(" If your Codex version gates hooks, enable [features] codex_hooks = true.");
90537
+ console.log(" Run `errata doctor` here to check hook discovery and trust; installation alone is not activation.");
90538
+ console.log(" Respect an explicit [features] hooks = false setting (older builds use codex_hooks).");
90428
90539
  console.log(" Older Codex versions may not support every event; update if hooks do not fire.");
90429
90540
  }
90430
90541
  async function refreshLinkedCodexHooks(args2) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.6-dev.2752",
3
+ "version": "2.0.6-dev.2759",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {