@inerrata-corporation/errata 2.0.6-dev.2081 → 2.0.6-dev.2082

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 +102 -9
  2. package/package.json +1 -1
package/errata.mjs CHANGED
@@ -77056,7 +77056,7 @@ function createWatchBreaker(deps) {
77056
77056
  }
77057
77057
 
77058
77058
  // src/engine.ts
77059
- var DAEMON_VERSION = true ? "2.0.6-dev.2081" : "2.0.0-alpha.0";
77059
+ var DAEMON_VERSION = true ? "2.0.6-dev.2082" : "2.0.0-alpha.0";
77060
77060
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
77061
77061
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
77062
77062
  var GIT_OP_MUTE_MS = 4e3;
@@ -83159,6 +83159,76 @@ function codexWakeCandidates(input) {
83159
83159
  return out2;
83160
83160
  }
83161
83161
 
83162
+ // src/hook-route.ts
83163
+ function msysToWindowsPath(p, platform = process.platform) {
83164
+ if (platform !== "win32") return p;
83165
+ const m = /^\/([a-zA-Z])(\/.*)?$/.exec(p);
83166
+ return m ? `${m[1].toUpperCase()}:${m[2] ?? "/"}` : p;
83167
+ }
83168
+ var SHELL_BREAK = /[\s"'`;&|()<>=]+/;
83169
+ function absolutePathsInCommand(command, platform = process.platform) {
83170
+ const out2 = [];
83171
+ for (const raw2 of command.split(SHELL_BREAK)) {
83172
+ const token = raw2.replace(/^[:,]+/, "").replace(/[,:]+$/, "");
83173
+ if (!token) continue;
83174
+ if (/^[a-zA-Z]:[\\/]/.test(token)) {
83175
+ out2.push(token);
83176
+ } else if (token.startsWith("/") && !token.startsWith("//")) {
83177
+ if (platform === "win32") {
83178
+ if (/^\/[a-zA-Z](\/|$)/.test(token)) out2.push(msysToWindowsPath(token, platform));
83179
+ } else if (token.length > 1) {
83180
+ out2.push(token);
83181
+ }
83182
+ }
83183
+ }
83184
+ return out2;
83185
+ }
83186
+ function hookPathCandidates(body2, platform = process.platform) {
83187
+ const input = body2["tool_input"] ?? {};
83188
+ const str = (v) => typeof v === "string" && v.length > 0 ? v : void 0;
83189
+ const out2 = [];
83190
+ for (const key of ["file_path", "notebook_path", "path"]) {
83191
+ const v = str(input[key]);
83192
+ if (v) out2.push(msysToWindowsPath(v, platform));
83193
+ }
83194
+ const command = str(input["command"]);
83195
+ if (command) out2.push(...absolutePathsInCommand(command, platform));
83196
+ const cwd = str(body2["cwd"]);
83197
+ if (cwd) out2.push(msysToWindowsPath(cwd, platform));
83198
+ return [...new Set(out2)];
83199
+ }
83200
+ var SESSION_AFFINITY_TTL_MS = 30 * 6e4;
83201
+ var SessionAffinity = class {
83202
+ constructor(ttlMs = SESSION_AFFINITY_TTL_MS, maxSessions = 500) {
83203
+ this.ttlMs = ttlMs;
83204
+ this.maxSessions = maxSessions;
83205
+ }
83206
+ bySession = /* @__PURE__ */ new Map();
83207
+ note(sessionId, root, now = Date.now()) {
83208
+ if (typeof sessionId !== "string" || sessionId.length === 0) return;
83209
+ this.bySession.delete(sessionId);
83210
+ this.bySession.set(sessionId, { root, at: now });
83211
+ while (this.bySession.size > this.maxSessions) {
83212
+ const oldest = this.bySession.keys().next().value;
83213
+ if (oldest === void 0) break;
83214
+ this.bySession.delete(oldest);
83215
+ }
83216
+ }
83217
+ rootFor(sessionId, now = Date.now()) {
83218
+ if (typeof sessionId !== "string" || sessionId.length === 0) return null;
83219
+ const hit = this.bySession.get(sessionId);
83220
+ if (!hit) return null;
83221
+ if (now - hit.at > this.ttlMs) {
83222
+ this.bySession.delete(sessionId);
83223
+ return null;
83224
+ }
83225
+ return hit.root;
83226
+ }
83227
+ forget(sessionId) {
83228
+ if (typeof sessionId === "string") this.bySession.delete(sessionId);
83229
+ }
83230
+ };
83231
+
83162
83232
  // src/multi.ts
83163
83233
  var projectSymbolSalts = /* @__PURE__ */ new Map();
83164
83234
  async function resolveProjectSymbolSalt(client, projectId) {
@@ -83609,7 +83679,8 @@ async function startMultiDaemon(opts = {}) {
83609
83679
  }))
83610
83680
  })
83611
83681
  );
83612
- const forward = async (c, pathInBody) => {
83682
+ const sessionAffinity = new SessionAffinity();
83683
+ const forward = async (c, pathInBody, opts2 = {}) => {
83613
83684
  const raw2 = await c.req.text();
83614
83685
  let body2 = {};
83615
83686
  try {
@@ -83618,7 +83689,21 @@ async function startMultiDaemon(opts = {}) {
83618
83689
  return c.json({ error: "bad-json" }, 400);
83619
83690
  }
83620
83691
  lastHook = { at: Date.now(), event: typeof body2["hook_event_name"] === "string" ? body2["hook_event_name"] : c.req.path };
83621
- let owner = ownerOf(records, pathInBody(body2));
83692
+ const primary = pathInBody(body2);
83693
+ const others = (opts2.extraPaths?.(body2) ?? []).filter((p) => p !== primary);
83694
+ let owner = ownerOf(records, primary);
83695
+ for (const p of others) {
83696
+ if (owner) break;
83697
+ owner = ownerOf(records, p);
83698
+ }
83699
+ if (!owner && others.length > 0) {
83700
+ const registryRoots = listWorkspaceEntries().map((e) => e.path);
83701
+ const registered = others.map((p) => pickOwnerRoot(registryRoots, p)).find((r) => r != null);
83702
+ if (registered && attachWorkspace(registered).attached) {
83703
+ owner = records.find((r) => r.root === registered) ?? null;
83704
+ if (owner) console.log(`[errata] woke workspace ${owner.entry.name}`);
83705
+ }
83706
+ }
83622
83707
  if (!owner) {
83623
83708
  const missPath = pathInBody(body2);
83624
83709
  const registeredRoot = missPath ? pickOwnerRoot(listWorkspaceEntries().map((e) => e.path), missPath) : null;
@@ -83628,10 +83713,18 @@ async function startMultiDaemon(opts = {}) {
83628
83713
  if (owner) console.log(`[errata] woke workspace ${owner.entry.name}`);
83629
83714
  }
83630
83715
  }
83716
+ if (!owner && opts2.affinity === "follow") {
83717
+ const root = sessionAffinity.rootFor(body2["session_id"]);
83718
+ if (root) {
83719
+ owner = records.find((r) => r.root === root) ?? null;
83720
+ if (!owner && attachWorkspace(root).attached) owner = records.find((r) => r.root === root) ?? null;
83721
+ }
83722
+ }
83631
83723
  if (!owner) {
83632
- rootAdopter.noteMiss(pathInBody(body2));
83724
+ rootAdopter.noteMiss(primary);
83633
83725
  return c.json({ error: "no-matching-project" }, 404);
83634
83726
  }
83727
+ if (opts2.affinity === "note") sessionAffinity.note(body2["session_id"], owner.root);
83635
83728
  owner.lastHookAt = Date.now();
83636
83729
  const sub = c.req.path;
83637
83730
  const done = markPass(`${sub}:${owner.entry.name}`);
@@ -83654,7 +83747,7 @@ async function startMultiDaemon(opts = {}) {
83654
83747
  const fp = input["file_path"];
83655
83748
  const cwd = b["cwd"];
83656
83749
  return (typeof fp === "string" ? fp : void 0) ?? (typeof cwd === "string" ? cwd : void 0);
83657
- })
83750
+ }, { extraPaths: (b) => hookPathCandidates(b), affinity: "note" })
83658
83751
  );
83659
83752
  app.post(
83660
83753
  "/api/diagnostics",
@@ -83664,10 +83757,10 @@ async function startMultiDaemon(opts = {}) {
83664
83757
  })
83665
83758
  );
83666
83759
  const cwdOf = (b) => typeof b["cwd"] === "string" ? b["cwd"] : void 0;
83667
- app.post("/api/turn", (c) => forward(c, cwdOf));
83668
- app.post("/api/message", (c) => forward(c, cwdOf));
83669
- app.post("/api/session-end", (c) => forward(c, cwdOf));
83670
- app.post("/api/context-inject", (c) => forward(c, cwdOf));
83760
+ app.post("/api/turn", (c) => forward(c, cwdOf, { affinity: "follow" }));
83761
+ app.post("/api/message", (c) => forward(c, cwdOf, { affinity: "follow" }));
83762
+ app.post("/api/session-end", (c) => forward(c, cwdOf, { affinity: "follow" }));
83763
+ app.post("/api/context-inject", (c) => forward(c, cwdOf, { affinity: "follow" }));
83671
83764
  for (const r of records) {
83672
83765
  mountedIds.add(r.id);
83673
83766
  app.route(`/ws/${r.id}`, r.webApp);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.6-dev.2081",
3
+ "version": "2.0.6-dev.2082",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {