@inerrata-corporation/errata 2.0.22-dev.4669 → 2.0.22-dev.4676

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.
package/errata.mjs CHANGED
@@ -18932,6 +18932,41 @@ var init_wm_calibration = __esm({
18932
18932
  }
18933
18933
  });
18934
18934
 
18935
+ // ../../packages/local-graph/src/exposure-attrs.ts
18936
+ function pickExposure(attrs) {
18937
+ const out2 = {};
18938
+ for (const k of EXPOSURE_ATTRS) if (attrs[k] !== void 0) out2[k] = attrs[k];
18939
+ return out2;
18940
+ }
18941
+ function exposureDocSql(key) {
18942
+ return `coalesce((SELECT x.attrs_json FROM node_exposure x WHERE x.id = nodes.id AND json_type(x.attrs_json, '$.${key}') IS NOT NULL), attrs_json)`;
18943
+ }
18944
+ var EXPOSURE_ATTRS, EXPOSURE_SCOPE, EXPOSURE_KEYS, isExposureAttr;
18945
+ var init_exposure_attrs = __esm({
18946
+ "../../packages/local-graph/src/exposure-attrs.ts"() {
18947
+ "use strict";
18948
+ EXPOSURE_ATTRS = [
18949
+ "shownCount",
18950
+ "evictedCount",
18951
+ "firstShownAt",
18952
+ "firstEvictedAt",
18953
+ "firstShownAtSeq",
18954
+ "lastShownAtSeq",
18955
+ "lastEvictedAtSeq",
18956
+ "shownSinceTouch",
18957
+ "shownSinceTouchAt",
18958
+ "exposureLog",
18959
+ "exposureLogStart",
18960
+ "exposureLogSince",
18961
+ "exposureFirst",
18962
+ "exposureTrimmed"
18963
+ ];
18964
+ EXPOSURE_SCOPE = "exposure";
18965
+ EXPOSURE_KEYS = new Set(EXPOSURE_ATTRS);
18966
+ isExposureAttr = (key) => EXPOSURE_KEYS.has(key);
18967
+ }
18968
+ });
18969
+
18935
18970
  // ../../packages/local-graph/src/history-cache-floor.ts
18936
18971
  function installHistoryCacheFloor(db) {
18937
18972
  const key = HISTORY_CACHE_FLOOR_KEY;
@@ -19252,31 +19287,6 @@ var init_text_index = __esm({
19252
19287
  }
19253
19288
  });
19254
19289
 
19255
- // ../../packages/local-graph/src/exposure-attrs.ts
19256
- var EXPOSURE_ATTRS, EXPOSURE_SCOPE;
19257
- var init_exposure_attrs = __esm({
19258
- "../../packages/local-graph/src/exposure-attrs.ts"() {
19259
- "use strict";
19260
- EXPOSURE_ATTRS = [
19261
- "shownCount",
19262
- "evictedCount",
19263
- "firstShownAt",
19264
- "firstEvictedAt",
19265
- "firstShownAtSeq",
19266
- "lastShownAtSeq",
19267
- "lastEvictedAtSeq",
19268
- "shownSinceTouch",
19269
- "shownSinceTouchAt",
19270
- "exposureLog",
19271
- "exposureLogStart",
19272
- "exposureLogSince",
19273
- "exposureFirst",
19274
- "exposureTrimmed"
19275
- ];
19276
- EXPOSURE_SCOPE = "exposure";
19277
- }
19278
- });
19279
-
19280
19290
  // ../../packages/local-graph/src/change-feed.ts
19281
19291
  function installChangeFeed(db) {
19282
19292
  if (changeFeedInstalled(db)) return;
@@ -19335,6 +19345,16 @@ function readChangedNodeIds(db, sinceSeq, labels) {
19335
19345
  const rows = db.prepare(`SELECT DISTINCT id FROM change_log WHERE change_seq > ? AND kind = 'node' AND label IN (${labels.map(() => "?").join(",")})`).all(sinceSeq, ...labels);
19336
19346
  return rows.map((r) => r.id);
19337
19347
  }
19348
+ function readChangedAmong(db, sinceSeq, ids) {
19349
+ if (!changeLogCovers(db, sinceSeq)) return null;
19350
+ const out2 = [];
19351
+ for (let i2 = 0; i2 < ids.length; i2 += 500) {
19352
+ const chunk = ids.slice(i2, i2 + 500);
19353
+ const rows = db.prepare(`SELECT DISTINCT id FROM change_log WHERE change_seq > ? AND kind = 'node' AND id IN (${chunk.map(() => "?").join(",")})`).all(sinceSeq, ...chunk);
19354
+ for (const r of rows) out2.push(r.id);
19355
+ }
19356
+ return out2;
19357
+ }
19338
19358
  function readLineageChangedFiles(db, sinceSeq) {
19339
19359
  if (!changeLogCovers(db, sinceSeq)) return null;
19340
19360
  const rows = db.prepare(LINEAGE_CHANGED_FILES_SQL).all({ s: sinceSeq });
@@ -20536,8 +20556,11 @@ function compileNodeWhere(q) {
20536
20556
  }
20537
20557
  return { where: conds.join(" AND "), params };
20538
20558
  }
20539
- function attrValueSql(p) {
20540
- return `CASE json_type(attrs_json, ${p}) WHEN 'integer' THEN CAST(json_extract(attrs_json, ${p}) AS REAL) ELSE json_extract(attrs_json, ${p}) END`;
20559
+ function attrValueSql(p, doc = "attrs_json") {
20560
+ return `CASE json_type(${doc}, ${p}) WHEN 'integer' THEN CAST(json_extract(${doc}, ${p}) AS REAL) ELSE json_extract(${doc}, ${p}) END`;
20561
+ }
20562
+ function attrDocSql(key) {
20563
+ return isExposureAttr(key) ? exposureDocSql(key) : "attrs_json";
20541
20564
  }
20542
20565
  function nodeSelectFrom(q) {
20543
20566
  if (q.indexedBy) return `nodes INDEXED BY ${selectIndex(q.indexedBy)}`;
@@ -20550,7 +20573,7 @@ function nodeSelectFrom(q) {
20550
20573
  function compileNodeSelect(q) {
20551
20574
  const { where, params } = compileNodeWhere(q);
20552
20575
  const attrs = q.attrs ?? [];
20553
- const attrCols = attrs.map((k, i2) => `, ${attrValueSql(path(k))} AS a${i2}, json_type(attrs_json, ${path(k)}) AS t${i2}`).join("");
20576
+ const attrCols = attrs.map((k, i2) => `, ${attrValueSql(path(k), attrDocSql(k))} AS a${i2}, json_type(${attrDocSql(k)}, ${path(k)}) AS t${i2}`).join("");
20554
20577
  let order;
20555
20578
  if (q.orderBy && "column" in q.orderBy) {
20556
20579
  order = `${column(q.orderBy.column)} ${q.orderBy.desc ? "DESC" : "ASC"}, rowid`;
@@ -20616,6 +20639,7 @@ var SELECT_INDEXES, EMBEDDING_DIM_SQL, KEY, COLUMNS;
20616
20639
  var init_node_select = __esm({
20617
20640
  "../../packages/local-graph/src/node-select.ts"() {
20618
20641
  "use strict";
20642
+ init_exposure_attrs();
20619
20643
  SELECT_INDEXES = [
20620
20644
  "nodes_live_local_community",
20621
20645
  "nodes_live_fold_candidate",
@@ -20668,7 +20692,7 @@ function graphStoreNeedsPreparation(path3) {
20668
20692
  if (!indexes.has(match2[1])) return true;
20669
20693
  }
20670
20694
  if (!changeFeedInstalled(db)) return true;
20671
- return [SESSION_REPAIR_KEY, HISTORY_CACHE_FLOOR_KEY, EDIT_PLACEHOLDER_FLOOR_KEY, CLOUD_ATTR_COMPACTION_KEY, HISTORY_ATTR_SLIM_KEY, TEXT_INDEX_KEY, LINEAGE_INTEGRITY_KEY].some((key) => !db.prepare("SELECT value FROM store_meta WHERE key = ?").get(key));
20695
+ return [SESSION_REPAIR_KEY, HISTORY_CACHE_FLOOR_KEY, EDIT_PLACEHOLDER_FLOOR_KEY, CLOUD_ATTR_COMPACTION_KEY, HISTORY_ATTR_SLIM_KEY, TEXT_INDEX_KEY, LINEAGE_INTEGRITY_KEY, EXPOSURE_SIDE_KEY].some((key) => !db.prepare("SELECT value FROM store_meta WHERE key = ?").get(key));
20672
20696
  } catch {
20673
20697
  return true;
20674
20698
  } finally {
@@ -20735,13 +20759,14 @@ function rowToEdge(r) {
20735
20759
  function openGraphStore(opts) {
20736
20760
  return new SqliteGraphStore(opts);
20737
20761
  }
20738
- var PAGE_RANK_SCALE_KEY, TEXT_SEARCH_COLUMNS, LOCAL_RULE_OVERRIDES, SCHEMA_VERSION, SESSION_REPAIR_KEY, SESSION_EVIDENCE_PREDICATE, SCHEMA_SQL, TEMPORAL_INDEX_SQL, FINGERPRINT_INDEXED_LABELS, NODE_LABELS_CHUNK, PRIOR_TAG_MARKER, SqliteGraphStore;
20762
+ var PAGE_RANK_SCALE_KEY, TEXT_SEARCH_COLUMNS, LOCAL_RULE_OVERRIDES, SCHEMA_VERSION, SESSION_REPAIR_KEY, EXPOSURE_SIDE_KEY, SESSION_EVIDENCE_PREDICATE, SCHEMA_SQL, TEMPORAL_INDEX_SQL, FINGERPRINT_INDEXED_LABELS, NODE_LABELS_CHUNK, PRIOR_TAG_MARKER, SqliteGraphStore;
20739
20763
  var init_store = __esm({
20740
20764
  "../../packages/local-graph/src/store.ts"() {
20741
20765
  "use strict";
20742
20766
  init_src2();
20743
20767
  init_src();
20744
20768
  init_wm_calibration();
20769
+ init_exposure_attrs();
20745
20770
  init_history_cache_floor();
20746
20771
  init_session_evidence();
20747
20772
  init_edit_placeholder_retirement();
@@ -20786,6 +20811,7 @@ var init_store = __esm({
20786
20811
  };
20787
20812
  SCHEMA_VERSION = 6;
20788
20813
  SESSION_REPAIR_KEY = "session-evidence-v1";
20814
+ EXPOSURE_SIDE_KEY = "exposure-side-rows-1";
20789
20815
  SESSION_EVIDENCE_PREDICATE = SESSION_EVIDENCE_FIELDS.map((field) => `instr(attrs_json, '${JSON.stringify(field)}') > 0`).join(" OR ");
20790
20816
  SCHEMA_SQL = `
20791
20817
  CREATE TABLE IF NOT EXISTS schema_version (
@@ -20849,8 +20875,13 @@ CREATE TABLE IF NOT EXISTS nodes (
20849
20875
  );
20850
20876
 
20851
20877
  CREATE INDEX IF NOT EXISTS nodes_label ON nodes(label);
20852
- CREATE INDEX IF NOT EXISTS nodes_memory_tier ON nodes(memory_tier);
20853
- CREATE INDEX IF NOT EXISTS nodes_page_rank ON nodes(page_rank DESC);
20878
+ -- Dropped: no query plans through them (every prepared statement across the
20879
+ -- local-graph, indexer, context-writer, generalizer and daemon suites was
20880
+ -- planned with and without them, 2026-10-21), and each write that touches
20881
+ -- memory_tier / page_rank (every node upsert, every rescore) paid to maintain
20882
+ -- them. nodes_live_page_rank serves the live page-rank orderings.
20883
+ DROP INDEX IF EXISTS nodes_memory_tier;
20884
+ DROP INDEX IF EXISTS nodes_page_rank;
20854
20885
 
20855
20886
  CREATE TABLE IF NOT EXISTS edges (
20856
20887
  id TEXT PRIMARY KEY,
@@ -20872,7 +20903,10 @@ CREATE TABLE IF NOT EXISTS edges (
20872
20903
  FOREIGN KEY (to_id) REFERENCES nodes(id) ON DELETE CASCADE
20873
20904
  );
20874
20905
 
20875
- CREATE INDEX IF NOT EXISTS edges_from ON edges(from_id, type);
20906
+ -- Dropped: a prefix of edges_triple (from_id, type, to_id), which every
20907
+ -- from-side lookup and foreign-key check already plans through; it cost 47 MB
20908
+ -- and an index write on every edge write.
20909
+ DROP INDEX IF EXISTS edges_from;
20876
20910
  CREATE INDEX IF NOT EXISTS edges_to ON edges(to_id, type);
20877
20911
  CREATE INDEX IF NOT EXISTS edges_type ON edges(type);
20878
20912
  CREATE UNIQUE INDEX IF NOT EXISTS edges_triple ON edges(from_id, type, to_id);
@@ -20894,6 +20928,12 @@ CREATE INDEX IF NOT EXISTS nodes_live_local_community ON nodes(label, valid_to,
20894
20928
  -- Problems parked for the fold judge (read every settle boundary): ~1 in 5 open
20895
20929
  -- Problems; without it every live Problem was read to find them (56 MB).
20896
20930
  CREATE INDEX IF NOT EXISTS nodes_live_fold_candidate ON nodes(label, valid_to) WHERE valid_to IS NULL AND json_type(attrs_json, '$.foldCandidate') IS NOT NULL;
20931
+ -- Render impressions, one row per node id (exposure-attrs.ts): a render bumps
20932
+ -- ~40 of these, which in the node row rewrote each whole row and its indexes.
20933
+ CREATE TABLE IF NOT EXISTS node_exposure (
20934
+ id TEXT PRIMARY KEY,
20935
+ attrs_json TEXT NOT NULL DEFAULT '{}'
20936
+ ) WITHOUT ROWID;
20897
20937
  -- The semantic-maintenance sweeps' own filters (closed, asked, hinted): each read
20898
20938
  -- every Problem row (~30 MB on a 1.1 GB store) for the few hundred that match,
20899
20939
  -- several times a run, and a run follows most writes (2026-10-20).
@@ -21000,6 +21040,7 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
21000
21040
  constructor(opts) {
21001
21041
  this.db = openDatabase(opts.path);
21002
21042
  this.db.exec("PRAGMA cache_size = -32768");
21043
+ this.db.exec("PRAGMA mmap_size = 2147483648");
21003
21044
  this.db.exec(SCHEMA_SQL);
21004
21045
  const versionRow = this.db.prepare("SELECT version FROM schema_version LIMIT 1").get();
21005
21046
  if (!versionRow) {
@@ -21063,7 +21104,10 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
21063
21104
  last_reinforced_at_seq = excluded.last_reinforced_at_seq,
21064
21105
  attrs_json = excluded.attrs_json
21065
21106
  `),
21066
- getNode: this.db.prepare("SELECT * FROM nodes WHERE id = ?"),
21107
+ getNode: this.db.prepare("SELECT nodes.*, (SELECT x.attrs_json FROM node_exposure x WHERE x.id = nodes.id) AS exposure_json FROM nodes WHERE id = ?"),
21108
+ exposureOf: this.db.prepare("SELECT attrs_json FROM node_exposure WHERE id = ?"),
21109
+ writeExposure: this.db.prepare("INSERT INTO node_exposure(id, attrs_json) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET attrs_json = excluded.attrs_json"),
21110
+ lastUpdatedOf: this.db.prepare("SELECT last_updated_at AS t, attrs_json FROM nodes WHERE id = ?"),
21067
21111
  labelOf: this.db.prepare("SELECT label FROM nodes WHERE id = ?"),
21068
21112
  nodeContent: this.db.prepare(
21069
21113
  "SELECT label, description, extraction_confidence, extraction_source, stability, valid_to, attrs_json FROM nodes WHERE id = ?"
@@ -21227,6 +21271,7 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
21227
21271
  "INSERT INTO store_meta (key, value) VALUES ('ingest_seq', :v) ON CONFLICT(key) DO UPDATE SET value = excluded.value"
21228
21272
  )
21229
21273
  };
21274
+ this.moveExposureToSideRows();
21230
21275
  const seqRow = this.db.prepare("SELECT value FROM store_meta WHERE key = 'ingest_seq'").get();
21231
21276
  this.ingestSeq = seqRow ? Number(seqRow.value) : 0;
21232
21277
  }
@@ -21287,21 +21332,35 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
21287
21332
  `SELECT rowid AS rid, logical_id, version, valid_to, label, ${WM_CALIBRATION_KEYS.map((k, i2) => `${wmCounterSql(k)} AS c${i2}`).join(", ")}
21288
21333
  FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}`
21289
21334
  ).all();
21335
+ const exposed = this.db.prepare(
21336
+ `SELECT n.rowid AS rid, n.logical_id, n.version, n.valid_to, n.label, x.attrs_json AS exposure
21337
+ FROM node_exposure x JOIN nodes n ON n.id = x.id`
21338
+ ).all();
21290
21339
  const others = this.db.prepare(
21291
21340
  `SELECT rowid AS rid, logical_id, version, valid_to, label FROM nodes WHERE rowid IN (
21292
21341
  SELECT n.rowid FROM nodes AS n INDEXED BY nodes_logical
21293
- WHERE n.logical_id IN (SELECT logical_id FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE})
21294
- EXCEPT SELECT rowid FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE})`
21342
+ WHERE n.logical_id IN (SELECT logical_id FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}
21343
+ UNION SELECT n2.logical_id FROM node_exposure x2 JOIN nodes n2 ON n2.id = x2.id)
21344
+ EXCEPT SELECT rowid FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}
21345
+ EXCEPT SELECT n3.rowid FROM node_exposure x3 JOIN nodes n3 ON n3.id = x3.id)`
21295
21346
  ).all();
21296
- const order = new Map(labels.map((l, i2) => [l, i2]));
21297
- return [...counted, ...others].filter((r) => order.has(r.label)).sort((a, b) => order.get(a.label) - order.get(b.label) || a.rid - b.rid).map((r) => {
21347
+ const byRid = /* @__PURE__ */ new Map();
21348
+ for (const r of [...counted, ...others]) {
21298
21349
  const attrs = {};
21299
21350
  WM_CALIBRATION_KEYS.forEach((k, i2) => {
21300
21351
  const v = r[`c${i2}`];
21301
21352
  if (typeof v === "number") attrs[k] = v;
21302
21353
  });
21303
- return { logicalId: r.logical_id, version: Number(r.version), validTo: r.valid_to ?? null, attrs };
21304
- });
21354
+ byRid.set(r.rid, { row: r, attrs });
21355
+ }
21356
+ for (const r of exposed) {
21357
+ const side = JSON.parse(r.exposure);
21358
+ const entry = byRid.get(r.rid) ?? { row: r, attrs: {} };
21359
+ for (const k of WM_CALIBRATION_KEYS) if (typeof side[k] === "number" && Number.isFinite(side[k])) entry.attrs[k] = side[k];
21360
+ byRid.set(r.rid, entry);
21361
+ }
21362
+ const order = new Map(labels.map((l, i2) => [l, i2]));
21363
+ return [...byRid.values()].filter(({ row }) => order.has(row.label)).sort((a, b) => order.get(a.row.label) - order.get(b.row.label) || a.row.rid - b.row.rid).map(({ row, attrs }) => ({ logicalId: row.logical_id, version: Number(row.version), validTo: row.valid_to ?? null, attrs }));
21305
21364
  }
21306
21365
  liveCommentsAttachedTo(name2) {
21307
21366
  return this.db.prepare(
@@ -21439,7 +21498,7 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
21439
21498
  logical_id: node2.logicalId ?? node2.id,
21440
21499
  created_at_seq: node2.createdAtSeq ?? null,
21441
21500
  last_reinforced_at_seq: node2.lastReinforcedAtSeq ?? null,
21442
- attrs_json: JSON.stringify(node2.attrs ?? {})
21501
+ attrs_json: this.nodeAttrsJson(node2.id, node2.attrs ?? {})
21443
21502
  });
21444
21503
  }
21445
21504
  /** mergeNode, unless the live row already holds this content. Content is what
@@ -21451,7 +21510,7 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
21451
21510
  * whether it wrote. */
21452
21511
  mergeNodeIfChanged(node2) {
21453
21512
  const row = this.stmts.nodeContent.get(node2.id);
21454
- if (row && row.valid_to === null && row.label === node2.label && row.description === node2.description && row.extraction_confidence === node2.extractionConfidence && row.stability === node2.stability && row.attrs_json === JSON.stringify(node2.attrs ?? {})) return false;
21513
+ if (row && row.valid_to === null && row.label === node2.label && row.description === node2.description && row.extraction_confidence === node2.extractionConfidence && row.stability === node2.stability && !EXPOSURE_ATTRS.some((k) => node2.attrs?.[k] !== void 0) && row.attrs_json === JSON.stringify(node2.attrs ?? {})) return false;
21455
21514
  this.mergeNode(node2);
21456
21515
  return true;
21457
21516
  }
@@ -21478,7 +21537,37 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
21478
21537
  logical_id: patch.logicalId ?? null,
21479
21538
  created_at_seq: patch.createdAtSeq ?? null,
21480
21539
  last_reinforced_at_seq: patch.lastReinforcedAtSeq ?? null,
21481
- attrs_json: patch.attrs ? JSON.stringify(patch.attrs) : null
21540
+ attrs_json: patch.attrs ? this.nodeAttrsJson(id, patch.attrs) : null
21541
+ });
21542
+ }
21543
+ /**
21544
+ * The node-row attrs of a write, after handing any exposure keys to the side
21545
+ * row (exposure-attrs.ts). `getNode` returns exposure merged into attrs, so a
21546
+ * read-modify-write carries it; kept in the row it would re-bloat the row and
21547
+ * drift from the side row. Unchanged exposure costs no write.
21548
+ */
21549
+ nodeAttrsJson(id, attrs) {
21550
+ if (!EXPOSURE_ATTRS.some((k) => attrs[k] !== void 0)) return JSON.stringify(attrs);
21551
+ const current = this.stmts.exposureOf.get(id);
21552
+ const next = JSON.stringify({ ...current ? JSON.parse(current.attrs_json) : {}, ...pickExposure(attrs) });
21553
+ if (current?.attrs_json !== next) this.stmts.writeExposure.run(id, next);
21554
+ const rest2 = { ...attrs };
21555
+ for (const k of EXPOSURE_ATTRS) delete rest2[k];
21556
+ return JSON.stringify(rest2);
21557
+ }
21558
+ /** One-time: exposure keys written to node rows before the side table existed
21559
+ * move to it. Only rows carrying a counter can carry exposure, and those are
21560
+ * what nodes_wm_counted holds, so this reads the index, not the table. */
21561
+ moveExposureToSideRows() {
21562
+ if (this.db.prepare("SELECT value FROM store_meta WHERE key = ?").get(EXPOSURE_SIDE_KEY)) return;
21563
+ const rows = this.db.prepare(`SELECT id, attrs_json FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}`).all();
21564
+ const move = this.db.prepare("UPDATE nodes SET attrs_json = ? WHERE id = ?");
21565
+ this.db.transaction(() => {
21566
+ for (const r of rows) {
21567
+ const stripped = this.nodeAttrsJson(r.id, JSON.parse(r.attrs_json));
21568
+ if (stripped !== r.attrs_json) move.run(stripped, r.id);
21569
+ }
21570
+ this.db.prepare("INSERT OR REPLACE INTO store_meta(key, value) VALUES (?, 'done')").run(EXPOSURE_SIDE_KEY);
21482
21571
  });
21483
21572
  }
21484
21573
  /** Fast single-column writes for the nightly pass (avoid the 18-column COALESCE
@@ -21641,7 +21730,27 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
21641
21730
  }
21642
21731
  getNode(id) {
21643
21732
  const row = this.stmts.getNode.get(id);
21644
- return row ? rowToNode(row) : null;
21733
+ if (!row) return null;
21734
+ const node2 = rowToNode(row);
21735
+ if (row.exposure_json) Object.assign(node2.attrs, JSON.parse(row.exposure_json));
21736
+ return node2;
21737
+ }
21738
+ /** A node's render exposure: the side row's keys over the node row's own
21739
+ * (exposure-attrs.ts), plus the node's `lastUpdatedAt` (the "shown since
21740
+ * touch" counter is relative to it) — without reading the node's attrs. */
21741
+ exposureOf(id) {
21742
+ const row = this.stmts.lastUpdatedOf.get(id);
21743
+ if (!row) return null;
21744
+ const side = this.stmts.exposureOf.get(id);
21745
+ return {
21746
+ attrs: { ...pickExposure(JSON.parse(row.attrs_json)), ...side ? JSON.parse(side.attrs_json) : {} },
21747
+ lastUpdatedAt: row.t
21748
+ };
21749
+ }
21750
+ /** Replace a node's exposure (only EXPOSURE_ATTRS keys belong here). Touches
21751
+ * neither the node row nor its indexes, nor the change feed. */
21752
+ writeExposure(id, attrs) {
21753
+ this.stmts.writeExposure.run(id, JSON.stringify(attrs));
21645
21754
  }
21646
21755
  nodeLabels(ids) {
21647
21756
  const width = NODE_LABELS_CHUNK;
@@ -21692,7 +21801,7 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
21692
21801
  findNodeStampsByLabel(label, attrKey) {
21693
21802
  if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(attrKey)) throw new Error(`findNodeStampsByLabel: bad attr key ${attrKey}`);
21694
21803
  const rows = this.db.prepare(
21695
- `SELECT id, created_at, created_at_seq, json_type(attrs_json, '$.${attrKey}') IS NOT NULL AS has,
21804
+ `SELECT id, created_at, created_at_seq, json_type(${attrDocSql(attrKey)}, '$.${attrKey}') IS NOT NULL AS has,
21696
21805
  COALESCE(json_extract(attrs_json, '$.source') = 'cloud', 0) AS cloud
21697
21806
  FROM nodes WHERE label = ? AND valid_to IS NULL`
21698
21807
  ).all(label);
@@ -21709,7 +21818,7 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
21709
21818
  const keys = [...new Set(attrKeys)];
21710
21819
  const rows = this.db.prepare(
21711
21820
  `SELECT id, created_at, created_at_seq,
21712
- ${keys.map((k, i2) => `json_type(attrs_json, '$.${k}') IS NOT NULL AS h${i2}`).join(", ")}${keys.length ? "," : ""}
21821
+ ${keys.map((k, i2) => `json_type(${attrDocSql(k)}, '$.${k}') IS NOT NULL AS h${i2}`).join(", ")}${keys.length ? "," : ""}
21713
21822
  COALESCE(json_extract(attrs_json, '$.source') = 'cloud', 0) AS cloud
21714
21823
  FROM nodes WHERE label = ? AND valid_to IS NULL`
21715
21824
  ).all(label);
@@ -21969,6 +22078,9 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
21969
22078
  changedNodeIds(sinceSeq, labels) {
21970
22079
  return readChangedNodeIds(this.db, sinceSeq, labels);
21971
22080
  }
22081
+ changedAmong(sinceSeq, ids) {
22082
+ return readChangedAmong(this.db, sinceSeq, ids);
22083
+ }
21972
22084
  pruneChangeLog() {
21973
22085
  return pruneChangeLog(this.db);
21974
22086
  }
@@ -24931,9 +25043,11 @@ function recordRenderLedger(store, ledger, seq, ts) {
24931
25043
  const bump = (ids, field) => {
24932
25044
  let n = 0;
24933
25045
  for (const id of new Set(ids.map((i2) => i2.id))) {
24934
- const node2 = store.getNode(id);
24935
- if (!node2) continue;
24936
- const attrs = { ...node2.attrs };
25046
+ const side = store.exposureOf?.(id);
25047
+ const node2 = side ? null : store.getNode(id);
25048
+ if (!side && !node2) continue;
25049
+ const lastUpdatedAt = side ? side.lastUpdatedAt : node2.lastUpdatedAt;
25050
+ const attrs = side ? { ...side.attrs } : { ...node2.attrs };
24937
25051
  if (field === "shownCount" && attrs["firstShownAt"] === void 0) {
24938
25052
  attrs["firstShownAt"] = attrs["firstShownAtSeq"] !== void 0 || attrs["lastShownAtSeq"] !== void 0 ? 0 : ts;
24939
25053
  }
@@ -24944,15 +25058,16 @@ function recordRenderLedger(store, ledger, seq, ts) {
24944
25058
  if (field === "shownCount") {
24945
25059
  attrs["lastShownAtSeq"] = seq;
24946
25060
  if (attrs["firstShownAtSeq"] === void 0) attrs["firstShownAtSeq"] = seq;
24947
- const counted = attrs["shownSinceTouchAt"] === node2.lastUpdatedAt ? Number(attrs["shownSinceTouch"] ?? 0) : 0;
25061
+ const counted = attrs["shownSinceTouchAt"] === lastUpdatedAt ? Number(attrs["shownSinceTouch"] ?? 0) : 0;
24948
25062
  attrs["shownSinceTouch"] = counted + 1;
24949
- attrs["shownSinceTouchAt"] = node2.lastUpdatedAt;
25063
+ attrs["shownSinceTouchAt"] = lastUpdatedAt;
24950
25064
  logImpression(attrs, ts, retrieved.has(id) ? "r" : "c");
24951
25065
  } else {
24952
25066
  attrs["lastEvictedAtSeq"] = seq;
24953
25067
  logImpression(attrs, ts, "e");
24954
25068
  }
24955
- store.updateNode(id, { attrs, lastUpdatedAt: node2.lastUpdatedAt });
25069
+ if (side) store.writeExposure(id, pickExposure(attrs));
25070
+ else store.updateNode(id, { attrs, lastUpdatedAt });
24956
25071
  n++;
24957
25072
  }
24958
25073
  return n;
@@ -25061,6 +25176,7 @@ var init_design_problem = __esm({
25061
25176
  init_src();
25062
25177
  init_src();
25063
25178
  init_problem_package_link();
25179
+ init_exposure_attrs();
25064
25180
  init_src4();
25065
25181
  init_src2();
25066
25182
  init_problem_package_link();
@@ -27808,6 +27924,95 @@ var init_src5 = __esm({
27808
27924
  }
27809
27925
  });
27810
27926
 
27927
+ // ../../packages/context-writer/src/snapshot-inputs.ts
27928
+ function guardSnapshotReads(store) {
27929
+ const fail = (what) => {
27930
+ throw new Error(`buildSnapshot read ${what}, outside SNAPSHOT_NODE_LABELS/SNAPSHOT_EDGE_TYPES (snapshot-inputs.ts)`);
27931
+ };
27932
+ const label = (l, via) => {
27933
+ if (typeof l === "string" && !LABELS.has(l)) fail(`label ${l} via ${via}`);
27934
+ };
27935
+ const types = (t, via) => {
27936
+ if (!Array.isArray(t) || t.length === 0) fail(`every edge type via ${via}`);
27937
+ for (const x of t) if (!EDGES.has(String(x))) fail(`edge type ${String(x)} via ${via}`);
27938
+ };
27939
+ const nodes = (r, via) => {
27940
+ for (const n of Array.isArray(r) ? r : r ? [r] : []) label(n.label, via);
27941
+ return r;
27942
+ };
27943
+ const allowed = {
27944
+ getNode: (id) => nodes(store.getNode(id), "getNode"),
27945
+ selectNodes: (q) => {
27946
+ for (const l of q.labels) label(l, "selectNodes");
27947
+ return store.selectNodes(q);
27948
+ },
27949
+ countNodes: (q) => {
27950
+ for (const l of q.labels) label(l, "countNodes");
27951
+ return store.countNodes(q);
27952
+ },
27953
+ findLiveNodesByAttr: (l, ...rest2) => {
27954
+ label(l, "findLiveNodesByAttr");
27955
+ return store.findLiveNodesByAttr(l, ...rest2);
27956
+ },
27957
+ outEdges: (id, t) => {
27958
+ types(t, "outEdges");
27959
+ return store.outEdges(id, t);
27960
+ },
27961
+ inEdges: (id, t) => {
27962
+ types(t, "inEdges");
27963
+ return store.inEdges(id, t);
27964
+ },
27965
+ inDegreeByType: (t) => {
27966
+ types(t, "inDegreeByType");
27967
+ return store.inDegreeByType(t);
27968
+ },
27969
+ nodesAsOfWhereTrue: (asOf, attr) => {
27970
+ if (attr !== "revisit") fail(`nodesAsOfWhereTrue(${String(attr)})`);
27971
+ return store.nodesAsOfWhereTrue(asOf, attr);
27972
+ }
27973
+ };
27974
+ return new Proxy(store, {
27975
+ get(target, prop) {
27976
+ if (typeof prop === "string" && prop in allowed) return allowed[prop];
27977
+ const v = Reflect.get(target, prop, target);
27978
+ if (typeof v === "function") return () => fail(`store.${String(prop)}()`);
27979
+ return v;
27980
+ }
27981
+ });
27982
+ }
27983
+ var SNAPSHOT_NODE_LABELS, SNAPSHOT_EDGE_TYPES, SNAPSHOT_READ_GUARD, LABELS, EDGES;
27984
+ var init_snapshot_inputs = __esm({
27985
+ "../../packages/context-writer/src/snapshot-inputs.ts"() {
27986
+ "use strict";
27987
+ SNAPSHOT_NODE_LABELS = [
27988
+ "Problem",
27989
+ "Vulnerability",
27990
+ "Solution",
27991
+ "Pattern",
27992
+ "Technique",
27993
+ "AntiPattern",
27994
+ "Language",
27995
+ "Package",
27996
+ "Tool"
27997
+ ];
27998
+ SNAPSHOT_EDGE_TYPES = [
27999
+ "ANCHORED_AT",
28000
+ "CAUSED_BY",
28001
+ "CONCERNS",
28002
+ "DEPENDS_ON",
28003
+ "EVIDENCED_BY",
28004
+ "MANIFESTED_IN",
28005
+ "OCCURS_IN",
28006
+ "PERTAIN_TO",
28007
+ "REGRESSION_OF",
28008
+ "SOLVED_BY"
28009
+ ];
28010
+ SNAPSHOT_READ_GUARD = typeof process !== "undefined" && Boolean(process.env["VITEST"]);
28011
+ LABELS = new Set(SNAPSHOT_NODE_LABELS);
28012
+ EDGES = new Set(SNAPSHOT_EDGE_TYPES);
28013
+ }
28014
+ });
28015
+
27811
28016
  // ../../packages/context-writer/src/render.ts
27812
28017
  function isPassiveInjectable(node2, atCommit) {
27813
28018
  if (!historyReadPolicy(node2, { mode: "passive", atCommit }).allow) return false;
@@ -27833,6 +28038,8 @@ function passiveAllowed(row, atCommit) {
27833
28038
  return historyReadPolicy({ extractionSource: row.extractionSource, attrs: row.attrs }, { mode: "passive", atCommit }).allow;
27834
28039
  }
27835
28040
  function buildSnapshot(opts) {
28041
+ const codeStore = opts.store;
28042
+ if (SNAPSHOT_READ_GUARD) opts = { ...opts, store: guardSnapshotReads(opts.store) };
27836
28043
  const hydrate = (id) => opts.store.getNode(id);
27837
28044
  const problems = opts.store.selectNodes({ labels: ["Problem", "Vulnerability"], attrs: PROBLEM_BAND_ATTRS }).filter((p) => p.attrs["mergedInto"] === void 0 && passiveAllowed(p, opts.atCommit));
27838
28045
  const stillOpen = problems.filter((p) => p.attrs["resolvedAt"] == null);
@@ -27868,7 +28075,7 @@ function buildSnapshot(opts) {
27868
28075
  const recentResolved = resolvedPool.slice(0, RECENT_RESOLVED);
27869
28076
  const motifPool = opts.store.selectNodes({ labels: ["Pattern", "Technique", "AntiPattern"], attrs: ["missedRuns", ...HISTORY_POLICY_ATTRS] }).filter((n) => passiveAllowed(n, opts.atCommit)).filter((n) => !(Number(n.attrs["missedRuns"] ?? 0) > 0)).sort((a, b) => b.pageRank - a.pageRank).slice(0, MOTIF_POOL).map((r) => hydrate(r.id)).map((n) => n.embedding.length > 0 ? { ...n, embedding: [] } : n);
27870
28077
  const motifs = rankMotifs(motifPool, void 0);
27871
- const workingFile = opts.workingFile ? sliceForFile(opts.store, opts.workingFile, opts.atCommit) : void 0;
28078
+ const workingFile = opts.workingFile ? sliceForFile(codeStore, opts.workingFile, opts.atCommit) : void 0;
27872
28079
  const byEpisode = /* @__PURE__ */ new Map();
27873
28080
  for (const sol of opts.store.findLiveNodesByAttr("Solution", "enrichmentPending", 1)) {
27874
28081
  if (!historyReadPolicy(sol, { mode: "passive", atCommit: opts.atCommit }).allow) continue;
@@ -28549,6 +28756,7 @@ var init_render = __esm({
28549
28756
  init_src5();
28550
28757
  init_src2();
28551
28758
  init_src();
28759
+ init_snapshot_inputs();
28552
28760
  MOTIF_BAND = 10;
28553
28761
  MOTIF_POOL = 1e3;
28554
28762
  MOTIF_SEEDS = 8;
@@ -28878,6 +29086,7 @@ var init_src6 = __esm({
28878
29086
  init_context_file();
28879
29087
  init_redirectors();
28880
29088
  init_render();
29089
+ init_snapshot_inputs();
28881
29090
  init_bleed_memory();
28882
29091
  init_bleed_rules();
28883
29092
  init_select_durable_memory();
@@ -38882,10 +39091,20 @@ function isReExportBarrel(relPath) {
38882
39091
  function graphExportMap(store, workspaceId2, relPath) {
38883
39092
  const out2 = /* @__PURE__ */ new Map();
38884
39093
  const fileId = fileNodeId(workspaceId2, relPath);
38885
- if (!store.getNode(fileId)) return out2;
38886
- for (const e of store.outEdges(fileId, ["DEFINES"])) {
38887
- const n = store.getNode(e.to);
38888
- if (!n || !EXPORTABLE_LABELS.has(n.label)) continue;
39094
+ if (store.nodeLabels([fileId]).size === 0) return out2;
39095
+ const defines = store.outEdges(fileId, ["DEFINES"]);
39096
+ const rows = /* @__PURE__ */ new Map();
39097
+ for (let i2 = 0; i2 < defines.length; i2 += 500) {
39098
+ for (const r of store.selectNodes({
39099
+ labels: [...EXPORTABLE_LABELS],
39100
+ attrs: ["workspaceId", "name"],
39101
+ where: [{ idIn: defines.slice(i2, i2 + 500).map((e) => e.to) }],
39102
+ includeClosed: true
39103
+ })) rows.set(r.id, r);
39104
+ }
39105
+ for (const e of defines) {
39106
+ const n = rows.get(e.to);
39107
+ if (!n) continue;
38889
39108
  if (n.attrs["workspaceId"] !== workspaceId2) continue;
38890
39109
  const name2 = n.attrs["name"] ?? n.description;
38891
39110
  if (!name2 || name2.startsWith("<") || name2.includes(":")) continue;
@@ -80474,6 +80693,13 @@ init_session_registry();
80474
80693
  import { readFileSync as readFileSync18, renameSync as renameSync3, statSync as statSync8, writeFileSync as writeFileSync14 } from "node:fs";
80475
80694
  import { fileURLToPath as fileURLToPath2 } from "node:url";
80476
80695
  import { dirname as dirname11, join as join22 } from "node:path";
80696
+ function revisitFlagsUnchanged(store, held, flaggedNow) {
80697
+ if (held.anySeq === void 0 || held.revisitIds === void 0 || !store.changedAmong) return false;
80698
+ const watched = [.../* @__PURE__ */ new Set([...held.revisitIds, ...flaggedNow])];
80699
+ if (watched.length === 0) return true;
80700
+ const changed = store.changedAmong(held.anySeq, watched);
80701
+ return changed !== null && changed.length === 0;
80702
+ }
80477
80703
  function snapshotReusable(held, want) {
80478
80704
  return held !== null && held.key === want.key && held.atCommit === want.atCommit;
80479
80705
  }
@@ -83326,12 +83552,40 @@ function withHeavyPassSlot(label, fn) {
83326
83552
  return run3;
83327
83553
  }
83328
83554
 
83555
+ // src/warm-worker.ts
83556
+ var WARM_HEAP_CEILING_BYTES = 256 * 1024 * 1024;
83557
+ var WARM_IDLE_MS = 2 * 60 * 1e3;
83558
+ async function mayStayWarm(worker, ceiling = WARM_HEAP_CEILING_BYTES) {
83559
+ const stats = await worker.getHeapStatistics?.().catch(() => void 0);
83560
+ return stats !== void 0 && stats.total_heap_size <= ceiling;
83561
+ }
83562
+ var IdleRecycle = class {
83563
+ constructor(recycle, idleMs = WARM_IDLE_MS) {
83564
+ this.recycle = recycle;
83565
+ this.idleMs = idleMs;
83566
+ }
83567
+ timer = null;
83568
+ arm() {
83569
+ this.clear();
83570
+ this.timer = setTimeout(() => {
83571
+ this.timer = null;
83572
+ this.recycle();
83573
+ }, this.idleMs);
83574
+ this.timer.unref?.();
83575
+ }
83576
+ clear() {
83577
+ if (this.timer) clearTimeout(this.timer);
83578
+ this.timer = null;
83579
+ }
83580
+ };
83581
+
83329
83582
  // src/pass-worker-client.ts
83330
83583
  var PassWorker = class {
83331
83584
  constructor(init2) {
83332
83585
  this.init = init2;
83333
83586
  }
83334
83587
  worker = null;
83588
+ idle = new IdleRecycle(() => void this.recycleIdle());
83335
83589
  ready = null;
83336
83590
  pending = /* @__PURE__ */ new Map();
83337
83591
  nextId = 1;
@@ -83378,6 +83632,7 @@ var PassWorker = class {
83378
83632
  if (this.stopped) return Promise.reject(new Error("pass worker stopped"));
83379
83633
  return withHeavyPassSlot(`${kind}:${this.init.workspaceId}`, async () => {
83380
83634
  if (this.stopped) throw new Error("pass worker stopped");
83635
+ this.idle.clear();
83381
83636
  this.ensureWorker();
83382
83637
  const id = this.nextId++;
83383
83638
  try {
@@ -83387,7 +83642,8 @@ var PassWorker = class {
83387
83642
  this.worker.postMessage({ id, kind, ...payload !== void 0 ? { payload } : {} });
83388
83643
  });
83389
83644
  } finally {
83390
- await this.recycleIdle();
83645
+ if (this.worker && this.pending.size === 0 && !this.stopped && await mayStayWarm(this.worker)) this.idle.arm();
83646
+ else await this.recycleIdle();
83391
83647
  }
83392
83648
  });
83393
83649
  }
@@ -83451,6 +83707,7 @@ var PassWorker = class {
83451
83707
  }
83452
83708
  async stop() {
83453
83709
  this.stopped = true;
83710
+ this.idle.clear();
83454
83711
  const w = this.worker;
83455
83712
  this.worker = null;
83456
83713
  this.ready = null;
@@ -87137,7 +87394,7 @@ function createWatchBreaker(deps) {
87137
87394
  }
87138
87395
 
87139
87396
  // src/engine.ts
87140
- var DAEMON_VERSION = true ? "2.0.22-dev.4669" : "2.0.0-alpha.0";
87397
+ var DAEMON_VERSION = true ? "2.0.22-dev.4676" : "2.0.0-alpha.0";
87141
87398
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
87142
87399
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
87143
87400
  var GIT_OP_MUTE_MS = 4e3;
@@ -87563,6 +87820,7 @@ function createWorkspaceEngine(opts) {
87563
87820
  let ctxRefreshInFlight = false;
87564
87821
  let ctxRefreshQueued = false;
87565
87822
  let lastWorkerSnapshot = null;
87823
+ const SNAPSHOT_SCOPES = [...SNAPSHOT_NODE_LABELS.map(nodeScope), ...SNAPSHOT_EDGE_TYPES.map(edgeScope)];
87566
87824
  let lastSelfCiteSkips = 0;
87567
87825
  const refreshContextNow = () => {
87568
87826
  void refreshContextAsync().catch((err2) => {
@@ -87648,21 +87906,30 @@ function createWorkspaceEngine(opts) {
87648
87906
  skills: readSkillManifest(paths.skillsManifest),
87649
87907
  ...lastSelfCiteSkips > 0 ? { selfCiteSkips: lastSelfCiteSkips } : {}
87650
87908
  };
87651
- const changes = store.changeSeq ? store.changeSeq() - store.changeSeq([EXPOSURE_SCOPE]) : store.changeStamp();
87909
+ const changes = store.changeSeq ? store.changeSeq(SNAPSHOT_SCOPES) : store.changeStamp();
87910
+ const anySeq = store.changeSeq?.();
87652
87911
  const { id, name: name2, stack, languages, domains, goals } = snapOpts.profile;
87653
87912
  const { workingFile: _workingFile, ...keyedOpts } = snapOpts;
87654
87913
  const snapshotKey = `${snapshotCodeIdentity(DAEMON_VERSION)}|${changes}|${JSON.stringify({ ...keyedOpts, profile: { id, name: name2, stack, languages, domains, goals } })}`;
87655
87914
  lastWorkerSnapshot ??= loadHeldSnapshot(heldSnapshotPath(paths.configDir));
87656
87915
  const held = lastWorkerSnapshot;
87657
- const withWorkingFile = (snap, builtFor, want, atCommit) => {
87658
- if (builtFor === want) return snap;
87916
+ const withWorkingFile = (snap, want, atCommit) => {
87659
87917
  const slice = want ? sliceForFile(store, want, atCommit) : void 0;
87660
87918
  if (slice) snap.workingFile = slice;
87661
87919
  else delete snap.workingFile;
87662
87920
  return snap;
87663
87921
  };
87664
- const prebuilt = snapshotReusable(held, { key: snapshotKey, atCommit: snapOpts.atCommit }) ? withWorkingFile(structuredClone(held.snapshot), held.workingFile, snapOpts.workingFile, snapOpts.atCommit) : passWorker ? await passWorker.snapshot(snapOpts).then((snapshot2) => {
87665
- lastWorkerSnapshot = { key: snapshotKey, at: Date.now(), atCommit: snapOpts.atCommit, workingFile: snapOpts.workingFile, snapshot: structuredClone(snapshot2) };
87922
+ const revisitIds = store.nodesAsOfWhereTrue(Date.now(), "revisit").map((n) => n.id);
87923
+ const prebuilt = snapshotReusable(held, { key: snapshotKey, atCommit: snapOpts.atCommit }) && revisitFlagsUnchanged(store, held, revisitIds) ? withWorkingFile(structuredClone(held.snapshot), snapOpts.workingFile, snapOpts.atCommit) : passWorker ? await passWorker.snapshot(snapOpts).then((snapshot2) => {
87924
+ lastWorkerSnapshot = {
87925
+ key: snapshotKey,
87926
+ at: Date.now(),
87927
+ atCommit: snapOpts.atCommit,
87928
+ workingFile: snapOpts.workingFile,
87929
+ ...anySeq !== void 0 ? { anySeq } : {},
87930
+ revisitIds,
87931
+ snapshot: structuredClone(snapshot2)
87932
+ };
87666
87933
  saveHeldSnapshot(heldSnapshotPath(paths.configDir), lastWorkerSnapshot);
87667
87934
  return snapshot2;
87668
87935
  }).catch((err2) => {
@@ -91686,6 +91953,7 @@ init_session_registry();
91686
91953
  import { Worker as Worker2 } from "node:worker_threads";
91687
91954
  var ConsolidateWorker = class {
91688
91955
  worker = null;
91956
+ idle = new IdleRecycle(() => void this.recycleIdle());
91689
91957
  ready = null;
91690
91958
  pending = /* @__PURE__ */ new Map();
91691
91959
  nextId = 1;
@@ -91696,6 +91964,7 @@ var ConsolidateWorker = class {
91696
91964
  if (this.stopped) return Promise.reject(new Error("consolidate worker stopped"));
91697
91965
  return withHeavyPassSlot(`consolidate:${req.kind ?? "pass"}`, async () => {
91698
91966
  if (this.stopped) throw new Error("consolidate worker stopped");
91967
+ this.idle.clear();
91699
91968
  this.ensureWorker();
91700
91969
  const id = this.nextId++;
91701
91970
  try {
@@ -91705,7 +91974,8 @@ var ConsolidateWorker = class {
91705
91974
  this.worker.postMessage({ ...req, id });
91706
91975
  });
91707
91976
  } finally {
91708
- await this.recycleIdle();
91977
+ if (this.worker && this.pending.size === 0 && !this.stopped && await mayStayWarm(this.worker)) this.idle.arm();
91978
+ else await this.recycleIdle();
91709
91979
  }
91710
91980
  });
91711
91981
  }
@@ -91768,6 +92038,7 @@ var ConsolidateWorker = class {
91768
92038
  }
91769
92039
  async stop() {
91770
92040
  this.stopped = true;
92041
+ this.idle.clear();
91771
92042
  if (this.worker) {
91772
92043
  await this.worker.terminate().catch(() => {
91773
92044
  });
@@ -93614,7 +93885,7 @@ var GitBlobReader = class {
93614
93885
  init_src11();
93615
93886
  init_src();
93616
93887
  import { extname as extname4 } from "node:path";
93617
- var LABELS = {
93888
+ var LABELS2 = {
93618
93889
  function: "Function",
93619
93890
  class: "Class",
93620
93891
  method: "Method",
@@ -93648,7 +93919,7 @@ async function historicalAnchors(input) {
93648
93919
  const source = await input.readBlob(spec);
93649
93920
  if (source === null || source.includes("\0") || Buffer.byteLength(source) > MAX_SOURCE_BYTES) return null;
93650
93921
  await provider.preload?.();
93651
- const symbols = provider.extractSymbols(source, path3).filter((s) => LABELS[s.kind] && s.startByte >= 0 && s.endByte > s.startByte);
93922
+ const symbols = provider.extractSymbols(source, path3).filter((s) => LABELS2[s.kind] && s.startByte >= 0 && s.endByte > s.startByte);
93652
93923
  const offsets = [0];
93653
93924
  for (let i2 = 0; i2 < source.length; i2++) if (source[i2] === "\n") offsets.push(i2 + 1);
93654
93925
  offsets.push(source.length + 1);
@@ -93683,7 +93954,7 @@ async function historicalAnchors(input) {
93683
93954
  const symbols = file2.symbols.filter((s) => s.startByte < stop2 && s.endByte > start2).sort((a, b) => a.endByte - a.startByte - (b.endByte - b.startByte));
93684
93955
  const symbol2 = symbols[0];
93685
93956
  if (symbol2) {
93686
- const kind = LABELS[symbol2.kind];
93957
+ const kind = LABELS2[symbol2.kind];
93687
93958
  const symbolId = projectSymbolId(input.salt, input.projectId, input.bundle.pathTokens[token], symbol2.qname ?? symbol2.name, kind);
93688
93959
  const prev = list.at(-1);
93689
93960
  if (prev?.symbolId === symbolId && prev.historical?.side === side && prev.historical.blockDigest === block.digest && prev.historical.endByte + 1 === offset) {