@inerrata-corporation/errata 2.0.0-dev.28 → 2.0.0-dev.30

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 (3) hide show
  1. package/errata.mjs +135 -56
  2. package/package.json +1 -1
  3. package/pass-worker.mjs +203 -0
package/errata.mjs CHANGED
@@ -19939,7 +19939,7 @@ function budgetSnapshot(s, maxChars) {
19939
19939
  return dropped;
19940
19940
  }
19941
19941
  function assembleAgentContext(opts) {
19942
- const snapshot = buildSnapshot({
19942
+ const snapshot = opts.snapshot ?? buildSnapshot({
19943
19943
  store: opts.store,
19944
19944
  profile: opts.profile,
19945
19945
  reviewCount: opts.reviewCount,
@@ -20569,6 +20569,7 @@ var init_env_paths = __esm({
20569
20569
  // src/paths.ts
20570
20570
  var paths_exports = {};
20571
20571
  __export(paths_exports, {
20572
+ daemonLogPath: () => daemonLogPath,
20572
20573
  ensureDir: () => ensureDir,
20573
20574
  ensureParent: () => ensureParent,
20574
20575
  globalConfigPath: () => globalConfigPath,
@@ -20587,6 +20588,10 @@ function globalDir() {
20587
20588
  function globalConfigPath() {
20588
20589
  return process.env["ERRATA_CONFIG_PATH"] ?? join3(globalDir(), "config.json");
20589
20590
  }
20591
+ function daemonLogPath() {
20592
+ mkdirSync3(globalDir(), { recursive: true });
20593
+ return join3(globalDir(), "daemon.log");
20594
+ }
20590
20595
  function globalDaemonLock() {
20591
20596
  return process.env["ERRATA_DAEMON_LOCK"] ?? join3(globalDir(), "daemon.lock");
20592
20597
  }
@@ -25876,7 +25881,9 @@ async function incrementalReindex(store, rootPath, workspaceId2, changedAbsPaths
25876
25881
  });
25877
25882
  }
25878
25883
  const after = /* @__PURE__ */ new Map();
25884
+ let parsedFiles = 0;
25879
25885
  for (const rel of changedRelPaths) {
25886
+ if (parsedFiles++ > 0) await new Promise((r2) => setImmediate(r2));
25880
25887
  const abs = join6(rootPath, ...rel.split("/"));
25881
25888
  const ext = extname(abs).toLowerCase();
25882
25889
  const provider = providers.find((p) => p.fileExtensions.includes(ext));
@@ -37111,7 +37118,7 @@ var init_report_render = __esm({
37111
37118
 
37112
37119
  // src/cli.ts
37113
37120
  init_src5();
37114
- import { existsSync as existsSync19, readFileSync as readFileSync18 } from "node:fs";
37121
+ import { closeSync as closeSync2, existsSync as existsSync19, openSync as openSync2, readFileSync as readFileSync18, renameSync as renameSync3, statSync as statSync5 } from "node:fs";
37115
37122
  import { join as join22 } from "node:path";
37116
37123
  import { spawn as spawn3 } from "node:child_process";
37117
37124
 
@@ -37695,7 +37702,7 @@ init_webui();
37695
37702
  init_config();
37696
37703
 
37697
37704
  // src/engine.ts
37698
- import { execFileSync as execFileSync4 } from "node:child_process";
37705
+ import { execFileSync as execFileSync3 } from "node:child_process";
37699
37706
  import { existsSync as existsSync14, statSync as statSync4, appendFileSync as appendFileSync2, readdirSync as readdirSync7, renameSync as renameSync2, readFileSync as readFileSync13, writeFileSync as writeFileSync11 } from "node:fs";
37700
37707
  import { join as join19, relative as relative6, sep as sep4 } from "node:path";
37701
37708
 
@@ -40272,14 +40279,19 @@ var PassWorker = class {
40272
40279
  reconcile() {
40273
40280
  return this.call("reconcile");
40274
40281
  }
40275
- call(kind) {
40282
+ /** Build the AGENTS.md ContextSnapshot on the worker's own store connection —
40283
+ * the O(graph) scans stay off the main loop (HZ-boot-render-offload). */
40284
+ snapshot(opts) {
40285
+ return this.call("snapshot", opts);
40286
+ }
40287
+ call(kind, payload) {
40276
40288
  if (this.stopped) return Promise.reject(new Error("pass worker stopped"));
40277
40289
  this.ensureWorker();
40278
40290
  const id = this.nextId++;
40279
40291
  return this.ready.then(
40280
40292
  () => new Promise((resolve5, reject) => {
40281
40293
  this.pending.set(id, { resolve: resolve5, reject });
40282
- this.worker.postMessage({ id, kind });
40294
+ this.worker.postMessage({ id, kind, ...payload !== void 0 ? { payload } : {} });
40283
40295
  })
40284
40296
  );
40285
40297
  }
@@ -40725,18 +40737,25 @@ function startGitSensor(gitDir, onEvent, opts = {}) {
40725
40737
  }
40726
40738
 
40727
40739
  // src/git-delta.ts
40728
- import { execFileSync as execFileSync3 } from "node:child_process";
40740
+ import { execFile } from "node:child_process";
40729
40741
  function git(repoDir, args2) {
40730
- return execFileSync3("git", ["-C", repoDir, ...args2], {
40731
- encoding: "utf8",
40732
- maxBuffer: 64 * 1024 * 1024,
40733
- // Windows: don't flash a console window for each git subcommand the
40734
- // git-sensor runs on every commit/checkout.
40735
- windowsHide: true
40742
+ return new Promise((resolve5, reject) => {
40743
+ execFile(
40744
+ "git",
40745
+ ["-C", repoDir, ...args2],
40746
+ {
40747
+ encoding: "utf8",
40748
+ maxBuffer: 64 * 1024 * 1024,
40749
+ // Windows: don't flash a console window for each git subcommand the
40750
+ // git-sensor runs on every commit/checkout.
40751
+ windowsHide: true
40752
+ },
40753
+ (err2, stdout) => err2 ? reject(err2) : resolve5(stdout)
40754
+ );
40736
40755
  });
40737
40756
  }
40738
- function gitDiff(repoDir, oldRef, newRef) {
40739
- const out2 = git(repoDir, ["diff", "--name-status", "-M", oldRef, newRef]);
40757
+ async function gitDiff(repoDir, oldRef, newRef) {
40758
+ const out2 = await git(repoDir, ["diff", "--name-status", "-M", oldRef, newRef]);
40740
40759
  const changedPaths = [];
40741
40760
  const renames = [];
40742
40761
  const deleted = [];
@@ -40760,8 +40779,8 @@ function gitDiff(repoDir, oldRef, newRef) {
40760
40779
  return { changedPaths, renames, deleted };
40761
40780
  }
40762
40781
  var FMT = ["%H", "%an", "%ae", "%at", "%P", "%s"].join("%x1f");
40763
- function commitMeta(repoDir, sha2) {
40764
- const line = git(repoDir, ["show", "--no-patch", `--format=${FMT}`, sha2]).split(/\r?\n/, 1)[0] ?? "";
40782
+ async function commitMeta(repoDir, sha2) {
40783
+ const line = (await git(repoDir, ["show", "--no-patch", `--format=${FMT}`, sha2])).split(/\r?\n/, 1)[0] ?? "";
40765
40784
  const [h, an, ae, at, parents, subject] = line.split("");
40766
40785
  return {
40767
40786
  sha: h ?? sha2,
@@ -41315,8 +41334,36 @@ function maybeFlushDigests() {
41315
41334
  }
41316
41335
  }
41317
41336
 
41337
+ // src/loop-lag.ts
41338
+ var HEARTBEAT_MS = 500;
41339
+ var currentPass = "idle";
41340
+ var started = false;
41341
+ function markPass(name2) {
41342
+ const prev = currentPass;
41343
+ currentPass = name2;
41344
+ return () => {
41345
+ currentPass = prev;
41346
+ };
41347
+ }
41348
+ function startLoopLagMonitor(thresholdMs = 1e3) {
41349
+ if (started) return;
41350
+ started = true;
41351
+ let last = Date.now();
41352
+ const timer = setInterval(() => {
41353
+ const now = Date.now();
41354
+ const lag = now - last - HEARTBEAT_MS;
41355
+ if (lag > thresholdMs) {
41356
+ console.warn(
41357
+ `[loop-lag] ${(/* @__PURE__ */ new Date()).toISOString()} event loop held ~${(lag / 1e3).toFixed(1)}s during "${currentPass}"`
41358
+ );
41359
+ }
41360
+ last = now;
41361
+ }, HEARTBEAT_MS);
41362
+ timer.unref();
41363
+ }
41364
+
41318
41365
  // src/engine.ts
41319
- var DAEMON_VERSION = true ? "2.0.0-dev.28" : "2.0.0-alpha.0";
41366
+ var DAEMON_VERSION = true ? "2.0.0-dev.30" : "2.0.0-alpha.0";
41320
41367
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
41321
41368
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
41322
41369
  var GIT_OP_MUTE_MS = 4e3;
@@ -41350,7 +41397,7 @@ function saveTurnCursors(path2, cursors) {
41350
41397
  function gitSourceWatchTargets(root) {
41351
41398
  const SOURCE = /\.(ts|tsx|js|jsx|mjs|cjs|py)$/i;
41352
41399
  try {
41353
- const lsOut = execFileSync4("git", ["-C", root, "ls-files", "-z"], {
41400
+ const lsOut = execFileSync3("git", ["-C", root, "ls-files", "-z"], {
41354
41401
  encoding: "utf8",
41355
41402
  maxBuffer: 128 * 1024 * 1024,
41356
41403
  windowsHide: true,
@@ -41360,7 +41407,7 @@ function gitSourceWatchTargets(root) {
41360
41407
  if (sourceFiles.length === 0) throw new Error("no tracked source");
41361
41408
  let ignoredDirs = [];
41362
41409
  try {
41363
- const igOut = execFileSync4(
41410
+ const igOut = execFileSync3(
41364
41411
  "git",
41365
41412
  ["-C", root, "ls-files", "--others", "--ignored", "--exclude-standard", "--directory", "-z"],
41366
41413
  { encoding: "utf8", maxBuffer: 256 * 1024 * 1024, windowsHide: true, stdio: ["ignore", "pipe", "ignore"] }
@@ -41449,12 +41496,13 @@ function createWorkspaceEngine(opts) {
41449
41496
  const t = Date.now();
41450
41497
  const relChanged = changed.map((p) => relative6(opts.workspaceRoot, p).split(sep4).join("/"));
41451
41498
  const causal = causalBuffer.correlate(relChanged, t) ?? void 0;
41499
+ const doneReindex = markPass(`fs-reindex:${profile.name} (${changed.length} files)`);
41452
41500
  void incrementalReindex(store, opts.workspaceRoot, profile.id, changed, t, {
41453
41501
  onIdentityEvaluate: (e) => {
41454
41502
  appendIdentityAudit(identityAuditPath, e, JSON.stringify({ t, ...e }) + "\n");
41455
41503
  },
41456
41504
  declaredRenames: causal?.declaredRenames
41457
- }).then((r) => {
41505
+ }).finally(doneReindex).then((r) => {
41458
41506
  if (r.filesReindexed > 0) {
41459
41507
  console.log(
41460
41508
  `[errata] incremental reindex: ${r.filesReindexed} file(s), +${r.nodesEmitted} nodes / +${r.edgesEmitted} edges`
@@ -41501,8 +41549,8 @@ function createWorkspaceEngine(opts) {
41501
41549
  let diff;
41502
41550
  let meta3;
41503
41551
  try {
41504
- diff = gitDiff(opts.workspaceRoot, ev.oldSha, ev.newSha);
41505
- meta3 = commitMeta(opts.workspaceRoot, ev.newSha);
41552
+ diff = await gitDiff(opts.workspaceRoot, ev.oldSha, ev.newSha);
41553
+ meta3 = await commitMeta(opts.workspaceRoot, ev.newSha);
41506
41554
  } catch (err2) {
41507
41555
  console.warn("[errata] git read failed:", err2);
41508
41556
  return;
@@ -41512,6 +41560,7 @@ function createWorkspaceEngine(opts) {
41512
41560
  let episodeId2;
41513
41561
  if (srcPaths.length > 0) {
41514
41562
  const abs = srcPaths.map((p) => join19(opts.workspaceRoot, p));
41563
+ const doneReindex = markPass(`git-reindex:${profile.name} (${abs.length} files)`);
41515
41564
  try {
41516
41565
  const r = await incrementalReindex(store, opts.workspaceRoot, profile.id, abs, t, { declaredRenames });
41517
41566
  if (r.filesReindexed > 0) {
@@ -41524,6 +41573,8 @@ function createWorkspaceEngine(opts) {
41524
41573
  }
41525
41574
  } catch (err2) {
41526
41575
  console.warn("[errata] git reindex failed:", err2);
41576
+ } finally {
41577
+ doneReindex();
41527
41578
  }
41528
41579
  }
41529
41580
  recordGitCommit(store, {
@@ -41559,19 +41610,49 @@ function createWorkspaceEngine(opts) {
41559
41610
  let lastRemoteAt = 0;
41560
41611
  let remoteInFlight = false;
41561
41612
  const REMOTE_REFRESH_MS = 5 * 6e4;
41613
+ let ctxRefreshInFlight = false;
41614
+ let ctxRefreshQueued = false;
41562
41615
  const refreshContextNow = () => {
41616
+ void refreshContextAsync().catch((err2) => {
41617
+ console.warn("[errata] context refresh failed:", err2 instanceof Error ? err2.message : err2);
41618
+ });
41619
+ };
41620
+ const refreshContextAsync = async () => {
41563
41621
  if (opts.skipContextWriter) return;
41622
+ if (ctxRefreshInFlight) {
41623
+ ctxRefreshQueued = true;
41624
+ return;
41625
+ }
41626
+ ctxRefreshInFlight = true;
41627
+ try {
41628
+ await refreshContextOnce();
41629
+ } finally {
41630
+ ctxRefreshInFlight = false;
41631
+ if (ctxRefreshQueued) {
41632
+ ctxRefreshQueued = false;
41633
+ refreshContextNow();
41634
+ }
41635
+ }
41636
+ };
41637
+ const refreshContextOnce = async () => {
41564
41638
  const elicit = isEdgeElicitationEnabled();
41565
- const { body: body2, snapshot } = assembleAgentContext({
41566
- store,
41639
+ const snapOpts = {
41567
41640
  profile,
41568
41641
  reviewCount: pendingCount(paths),
41569
41642
  reviewUiUrl: opts.reviewUrl(),
41570
41643
  workingFile: workingFiles.mostRecent() ?? void 0,
41571
- skills: readSkillManifest(paths.skillsManifest),
41644
+ skills: readSkillManifest(paths.skillsManifest)
41645
+ };
41646
+ const prebuilt = passWorker ? await passWorker.snapshot(snapOpts).catch(() => null) : null;
41647
+ const doneRender = prebuilt ? null : markPass(`context-render-inline:${profile.name}`);
41648
+ const { body: body2, snapshot } = assembleAgentContext({
41649
+ store,
41650
+ ...snapOpts,
41572
41651
  remote: remotePriors,
41573
- ...elicit ? { edgeElicitation: { instruction: PRIOR_TAG_INSTRUCTION } } : {}
41652
+ ...elicit ? { edgeElicitation: { instruction: PRIOR_TAG_INSTRUCTION } } : {},
41653
+ ...prebuilt ? { snapshot: prebuilt } : {}
41574
41654
  });
41655
+ doneRender?.();
41575
41656
  const target = join19(opts.workspaceRoot, "AGENTS.md");
41576
41657
  writeManagedBlock(target, { body: body2 });
41577
41658
  if (elicit) {
@@ -41751,6 +41832,14 @@ function createWorkspaceEngine(opts) {
41751
41832
  const sessionThreads = /* @__PURE__ */ new Map();
41752
41833
  const harvestTexts = async (sessionId, items) => {
41753
41834
  if (items.length === 0) return;
41835
+ const doneHarvest = markPass(`turn-harvest:${profile.name} (${items.length} turns)`);
41836
+ try {
41837
+ await harvestTextsInner(sessionId, items);
41838
+ } finally {
41839
+ doneHarvest();
41840
+ }
41841
+ };
41842
+ const harvestTextsInner = async (sessionId, items) => {
41754
41843
  let minted = 0;
41755
41844
  let resolved = 0;
41756
41845
  let abstracted = 0;
@@ -41774,7 +41863,7 @@ function createWorkspaceEngine(opts) {
41774
41863
  return hit?.id;
41775
41864
  };
41776
41865
  for (const turn of items) {
41777
- if (processedTurns++ > 0 && processedTurns % 20 === 0) await yieldToLoop();
41866
+ if (processedTurns++ > 0) await yieldToLoop();
41778
41867
  const turnFile = turn.workingFile ? toRel(turn.workingFile) : void 0;
41779
41868
  if (opts.sharedStore) {
41780
41869
  try {
@@ -41823,7 +41912,7 @@ function createWorkspaceEngine(opts) {
41823
41912
  if (!retractAs) {
41824
41913
  const pid = r.id ?? designProblemId(r.problem);
41825
41914
  try {
41826
- const d = gitDiff(opts.workspaceRoot, "HEAD~1", "HEAD");
41915
+ const d = await gitDiff(opts.workspaceRoot, "HEAD~1", "HEAD");
41827
41916
  anchorProblemToDiff(store, pid, d.changedPaths, profile.id, t);
41828
41917
  const solId = solutionForProblem(store, pid);
41829
41918
  if (solId) anchorSolutionToDiff(store, solId, d.changedPaths, profile.id, t);
@@ -42037,6 +42126,7 @@ function createWorkspaceEngine(opts) {
42037
42126
  const harvestSession = async (sessionId, transcriptPath) => {
42038
42127
  await harvestTurns(sessionId, transcriptPath);
42039
42128
  for (const sub of subagentTranscripts(transcriptPath, sessionId)) {
42129
+ await yieldToLoop();
42040
42130
  await harvestTurns(sub.sessionId, sub.path);
42041
42131
  }
42042
42132
  };
@@ -42050,7 +42140,10 @@ function createWorkspaceEngine(opts) {
42050
42140
  sinceMs: Date.now() - TURN_REPLAY_LOOKBACK_MS,
42051
42141
  limit: 25
42052
42142
  });
42053
- for (const ref of refs) await harvestSession(ref.sessionId, ref.path);
42143
+ for (const ref of refs) {
42144
+ await yieldToLoop();
42145
+ await harvestSession(ref.sessionId, ref.path);
42146
+ }
42054
42147
  } catch {
42055
42148
  }
42056
42149
  })();
@@ -43050,32 +43143,6 @@ var ConsolidateWorker = class {
43050
43143
  }
43051
43144
  };
43052
43145
 
43053
- // src/loop-lag.ts
43054
- var HEARTBEAT_MS = 500;
43055
- var currentPass = "idle";
43056
- var started = false;
43057
- function markPass(name2) {
43058
- const prev = currentPass;
43059
- currentPass = name2;
43060
- return () => {
43061
- currentPass = prev;
43062
- };
43063
- }
43064
- function startLoopLagMonitor(thresholdMs = 1e3) {
43065
- if (started) return;
43066
- started = true;
43067
- let last = Date.now();
43068
- const timer = setInterval(() => {
43069
- const now = Date.now();
43070
- const lag = now - last - HEARTBEAT_MS;
43071
- if (lag > thresholdMs) {
43072
- console.warn(`[loop-lag] event loop held ~${(lag / 1e3).toFixed(1)}s during "${currentPass}"`);
43073
- }
43074
- last = now;
43075
- }, HEARTBEAT_MS);
43076
- timer.unref();
43077
- }
43078
-
43079
43146
  // src/multi.ts
43080
43147
  init_paths();
43081
43148
 
@@ -44290,6 +44357,7 @@ async function cmdStart() {
44290
44357
  const up = await waitForDaemon(globalDaemonLock(), 12e3);
44291
44358
  if (up) {
44292
44359
  console.log(` daemon: running \xB7 pid ${up.pid} \xB7 ${up.webUiUrl ?? "(running)"}`);
44360
+ console.log(` logs: ${daemonLogPath()}`);
44293
44361
  console.log(` watch it: errata dash \xB7 check it: errata status`);
44294
44362
  } else {
44295
44363
  console.log(` \u26A0 daemon did not come up within 12s.`);
@@ -45265,12 +45333,23 @@ function selfArgv(sub) {
45265
45333
  }
45266
45334
  function spawnDaemonDetached() {
45267
45335
  const { cmd: cmd2, args: args2 } = selfArgv("dash");
45336
+ let out2 = "ignore";
45337
+ try {
45338
+ const logPath = daemonLogPath();
45339
+ try {
45340
+ if (statSync5(logPath).size > 5 * 1024 * 1024) renameSync3(logPath, `${logPath}.1`);
45341
+ } catch {
45342
+ }
45343
+ out2 = openSync2(logPath, "a");
45344
+ } catch {
45345
+ }
45268
45346
  spawn3(cmd2, args2, {
45269
45347
  detached: true,
45270
- stdio: "ignore",
45348
+ stdio: ["ignore", out2, out2],
45271
45349
  windowsHide: true,
45272
45350
  cwd: ROOT
45273
45351
  }).unref();
45352
+ if (out2 !== "ignore") closeSync2(out2);
45274
45353
  }
45275
45354
  function ensureSingletonRunning() {
45276
45355
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.0-dev.28",
3
+ "version": "2.0.0-dev.30",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/pass-worker.mjs CHANGED
@@ -15555,7 +15555,9 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
15555
15555
  });
15556
15556
  }
15557
15557
  const after = /* @__PURE__ */ new Map();
15558
+ let parsedFiles = 0;
15558
15559
  for (const rel of changedRelPaths) {
15560
+ if (parsedFiles++ > 0) await new Promise((r2) => setImmediate(r2));
15559
15561
  const abs = join2(rootPath, ...rel.split("/"));
15560
15562
  const ext = extname(abs).toLowerCase();
15561
15563
  const provider = providers.find((p) => p.fileExtensions.includes(ext));
@@ -24451,6 +24453,14 @@ function addDependency(store2, opts) {
24451
24453
  store2.mergeEdge(edge);
24452
24454
  return edge;
24453
24455
  }
24456
+ function listNeedsRevisit(store2, asOf) {
24457
+ return store2.nodesAsOf(asOf).filter((n) => n.attrs["revisit"] === true).map((n) => ({
24458
+ id: n.id,
24459
+ label: n.label,
24460
+ description: n.description,
24461
+ reason: String(n.attrs["revisitReason"] ?? "")
24462
+ }));
24463
+ }
24454
24464
 
24455
24465
  // ../../packages/local-graph/src/credit.ts
24456
24466
  init_src2();
@@ -24798,6 +24808,48 @@ function mergeEdge(store2, from, to, type, ts) {
24798
24808
  attrs: { provisional: true }
24799
24809
  });
24800
24810
  }
24811
+ var CITABLE_PRIOR_LABELS = /* @__PURE__ */ new Set([
24812
+ "Solution",
24813
+ "RootCause",
24814
+ "Pattern",
24815
+ "Technique",
24816
+ "AntiPattern"
24817
+ ]);
24818
+ function priorsForFile(store2, relPath) {
24819
+ const want = relPath.trim().replace(/^\.?\//, "");
24820
+ let file2 = null;
24821
+ for (const n of store2.findNodesByLabel("File")) {
24822
+ if (n.description === want || n.attrs["relPath"] === want) {
24823
+ file2 = n;
24824
+ break;
24825
+ }
24826
+ }
24827
+ if (!file2) return null;
24828
+ const openProblems = [];
24829
+ const seenProblem = /* @__PURE__ */ new Set();
24830
+ const related = /* @__PURE__ */ new Map();
24831
+ for (const e of store2.inEdges(file2.id, ["ANCHORED_AT"])) {
24832
+ const n = store2.getNode(e.from);
24833
+ if (!n) continue;
24834
+ if (n.label === "Problem") {
24835
+ if (!n.attrs["resolvedAt"] && !seenProblem.has(n.id)) {
24836
+ seenProblem.add(n.id);
24837
+ openProblems.push(n);
24838
+ }
24839
+ } else if (CITABLE_PRIOR_LABELS.has(n.label)) {
24840
+ related.set(n.id, n);
24841
+ }
24842
+ }
24843
+ for (const p of openProblems) {
24844
+ for (const e of store2.outEdges(p.id, ["SOLVED_BY", "CAUSED_BY", "INSTANCE_OF"])) {
24845
+ const n = store2.getNode(e.to);
24846
+ if (n && CITABLE_PRIOR_LABELS.has(n.label)) related.set(n.id, n);
24847
+ }
24848
+ }
24849
+ if (openProblems.length === 0 && related.size === 0) return null;
24850
+ openProblems.sort((a, b) => (b.lastUpdatedAt ?? 0) - (a.lastUpdatedAt ?? 0));
24851
+ return { file: file2, openProblems, related: [...related.values()] };
24852
+ }
24801
24853
  function resolveDesignProblems(store2, t) {
24802
24854
  let resolved = 0;
24803
24855
  for (const p of store2.findNodesByLabel("Problem")) {
@@ -25517,6 +25569,146 @@ function codeAnchorProjection(store2, semanticIds, opts = {}) {
25517
25569
  return out2;
25518
25570
  }
25519
25571
 
25572
+ // ../../packages/context-writer/src/agents-md.ts
25573
+ init_src();
25574
+
25575
+ // ../../packages/context-writer/src/render.ts
25576
+ init_src2();
25577
+ function buildSnapshot(opts) {
25578
+ const problems = opts.store.findNodesByLabel("Problem").filter((p) => p.attrs["mergedInto"] === void 0);
25579
+ const allProblems = problems.filter((p) => p.attrs["resolvedAt"] == null);
25580
+ allProblems.sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt);
25581
+ const recent = allProblems.slice(0, 8).map((p) => ({
25582
+ node: p,
25583
+ anchors: opts.store.outEdges(p.id, ["MANIFESTED_IN", "EVIDENCED_BY"])
25584
+ }));
25585
+ const recentResolved = problems.filter((p) => p.attrs["resolvedAt"] != null && p.attrs["resolvedAs"] == null).sort((a, b) => Number(b.attrs["resolvedAt"] ?? 0) - Number(a.attrs["resolvedAt"] ?? 0)).slice(0, 4).map((p) => {
25586
+ const solEdge = opts.store.outEdges(p.id, ["SOLVED_BY"])[0];
25587
+ const solution = solEdge ? opts.store.getNode(solEdge.to) ?? void 0 : void 0;
25588
+ return { node: p, ...solution ? { solution } : {} };
25589
+ });
25590
+ const motifs = [
25591
+ ...opts.store.findNodesByLabel("Pattern"),
25592
+ ...opts.store.findNodesByLabel("Technique"),
25593
+ ...opts.store.findNodesByLabel("AntiPattern")
25594
+ ].sort((a, b) => b.pageRank - a.pageRank).slice(0, 10);
25595
+ const workingFile = opts.workingFile ? sliceForFile(opts.store, opts.workingFile) : void 0;
25596
+ const byEpisode = /* @__PURE__ */ new Map();
25597
+ for (const sol of opts.store.findNodesByLabel("Solution")) {
25598
+ if (sol.attrs["enrichmentPending"] !== true) continue;
25599
+ const ep = sol.attrs["episodeId"] ?? sol.id;
25600
+ const list = byEpisode.get(ep) ?? [];
25601
+ list.push({
25602
+ problemId: sol.attrs["problemId"] ?? "",
25603
+ fixed: sol.attrs["symbolName"] ?? "",
25604
+ problem: sol.attrs["problemDescription"] ?? ""
25605
+ });
25606
+ byEpisode.set(ep, list);
25607
+ }
25608
+ const pendingEnrichment = [...byEpisode.entries()].map(([episodeId, problems2]) => ({
25609
+ episodeId,
25610
+ problems: problems2
25611
+ }));
25612
+ const needsRevisit = listNeedsRevisit(
25613
+ opts.store,
25614
+ (opts.now ?? /* @__PURE__ */ new Date()).getTime()
25615
+ ).slice(0, 10);
25616
+ const norm = (x) => x.toLowerCase().replace(/[^a-z0-9]+/g, "");
25617
+ const pkgBase = (x) => {
25618
+ const at = x.lastIndexOf("@");
25619
+ return at > 0 ? x.slice(0, at) : x;
25620
+ };
25621
+ const matchNode = (cands, key) => {
25622
+ const k = norm(key);
25623
+ if (k.length < 2) return null;
25624
+ return cands.find((n) => norm(n.description ?? "") === k) ?? cands.find((n) => {
25625
+ const d = norm(n.description ?? "");
25626
+ return d.length >= 2 && (d.includes(k) || k.includes(d));
25627
+ }) ?? null;
25628
+ };
25629
+ const langNodes = opts.store.findNodesByLabel("Language");
25630
+ const pkgNodes = opts.store.findNodesByLabel("Package");
25631
+ const profileContext = {
25632
+ languages: opts.profile.languages.map((name2) => ({ name: name2, node: matchNode(langNodes, name2) })),
25633
+ packages: opts.profile.stack.map((name2) => ({ name: name2, node: matchNode(pkgNodes, pkgBase(name2)) }))
25634
+ };
25635
+ return {
25636
+ profile: opts.profile,
25637
+ profileContext,
25638
+ recentProblems: recent,
25639
+ recentResolved,
25640
+ motifs,
25641
+ reviewCount: opts.reviewCount,
25642
+ reviewUiUrl: opts.reviewUiUrl,
25643
+ ...workingFile ? { workingFile } : {},
25644
+ pendingEnrichment,
25645
+ skills: opts.skills ?? [],
25646
+ needsRevisit,
25647
+ generatedAt: (opts.now ?? /* @__PURE__ */ new Date()).toISOString()
25648
+ };
25649
+ }
25650
+ function sliceForFile(store2, relPath) {
25651
+ const candidates = store2.findNodesByLabel("File");
25652
+ const file2 = candidates.find((n) => n.description === relPath);
25653
+ if (!file2) return void 0;
25654
+ const symbols = [];
25655
+ for (const e of store2.outEdges(file2.id, ["DEFINES"])) {
25656
+ const n = store2.getNode(e.to);
25657
+ if (n) symbols.push({ label: n.label, name: n.description });
25658
+ if (symbols.length >= 25) break;
25659
+ }
25660
+ const importsFrom = [];
25661
+ for (const e of store2.outEdges(file2.id, ["IMPORTS"])) {
25662
+ const n = store2.getNode(e.to);
25663
+ if (n && n.label === "File") importsFrom.push(n.description);
25664
+ if (importsFrom.length >= 15) break;
25665
+ }
25666
+ const importedBy = [];
25667
+ for (const e of store2.inEdges(file2.id, ["IMPORTS"])) {
25668
+ const n = store2.getNode(e.from);
25669
+ if (n && n.label === "File") importedBy.push(n.description);
25670
+ if (importedBy.length >= 15) break;
25671
+ }
25672
+ const externalRefs = [];
25673
+ for (const e of store2.outEdges(file2.id, ["DEFINES"])) {
25674
+ const sym = store2.getNode(e.to);
25675
+ if (!sym) continue;
25676
+ for (const ref of store2.outEdges(sym.id, ["CALLS", "ACCESSES"])) {
25677
+ const tgt = store2.getNode(ref.to);
25678
+ if (!tgt) continue;
25679
+ const tgtFile = fileOf(store2, tgt.id);
25680
+ if (tgtFile && tgtFile !== file2.id) {
25681
+ externalRefs.push({ kind: ref.type, target: tgt.description });
25682
+ if (externalRefs.length >= 20) break;
25683
+ }
25684
+ }
25685
+ if (externalRefs.length >= 20) break;
25686
+ }
25687
+ const fp = priorsForFile(store2, relPath);
25688
+ const priors = fp ? {
25689
+ openProblems: fp.openProblems.slice(0, 3).map((p) => ({ id: p.id, description: p.description })),
25690
+ related: fp.related.slice(0, 4).map((n) => ({ id: n.id, label: n.label, description: n.description }))
25691
+ } : void 0;
25692
+ return {
25693
+ relPath,
25694
+ symbols,
25695
+ importsFrom,
25696
+ importedBy,
25697
+ externalRefs,
25698
+ ...priors ? { priors } : {}
25699
+ };
25700
+ }
25701
+ function fileOf(store2, nodeId) {
25702
+ for (const e of store2.inEdges(nodeId, ["DEFINES"])) {
25703
+ const parent = store2.getNode(e.from);
25704
+ if (parent && parent.label === "File") return parent.id;
25705
+ }
25706
+ return null;
25707
+ }
25708
+
25709
+ // ../../packages/context-writer/src/select-durable-memory.ts
25710
+ init_src2();
25711
+
25520
25712
  // ../../node_modules/.pnpm/env-paths@3.0.0/node_modules/env-paths/index.js
25521
25713
  import os from "node:os";
25522
25714
  import process3 from "node:process";
@@ -25650,6 +25842,17 @@ function runPass(msg) {
25650
25842
  return runNightly();
25651
25843
  case "reconcile":
25652
25844
  return reconcileStaleFiles(store, init2.workspaceRoot, init2.workspaceId);
25845
+ case "snapshot": {
25846
+ const p = msg.payload;
25847
+ return buildSnapshot({
25848
+ store,
25849
+ profile: p.profile,
25850
+ reviewCount: p.reviewCount,
25851
+ reviewUiUrl: p.reviewUiUrl,
25852
+ ...p.workingFile !== void 0 ? { workingFile: p.workingFile } : {},
25853
+ ...p.skills !== void 0 ? { skills: p.skills } : {}
25854
+ });
25855
+ }
25653
25856
  default:
25654
25857
  throw new Error(`pass-worker: unknown pass "${msg.kind}"`);
25655
25858
  }