@inerrata-corporation/errata 2.0.22-dev.4603 → 2.0.22-dev.4624
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/consolidate-worker.mjs
CHANGED
|
@@ -17179,6 +17179,12 @@ function changeLogCovers(db, sinceSeq) {
|
|
|
17179
17179
|
const prunedTo = Number(db.prepare("SELECT value FROM store_meta WHERE key = ?").get(PRUNED_TO_KEY)?.["value"] ?? NaN);
|
|
17180
17180
|
return Number.isFinite(prunedTo) && sinceSeq >= prunedTo;
|
|
17181
17181
|
}
|
|
17182
|
+
function readChangedNodeIds(db, sinceSeq, labels) {
|
|
17183
|
+
if (!changeLogCovers(db, sinceSeq)) return null;
|
|
17184
|
+
if (!labels.length) return [];
|
|
17185
|
+
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);
|
|
17186
|
+
return rows.map((r) => r.id);
|
|
17187
|
+
}
|
|
17182
17188
|
function readLineageChangedFiles(db, sinceSeq) {
|
|
17183
17189
|
if (!changeLogCovers(db, sinceSeq)) return null;
|
|
17184
17190
|
const rows = db.prepare(LINEAGE_CHANGED_FILES_SQL).all({ s: sinceSeq });
|
|
@@ -17563,6 +17569,7 @@ var DEMAND_HOT = Math.log1p(10);
|
|
|
17563
17569
|
var DEMAND_WARM = Math.log1p(5);
|
|
17564
17570
|
|
|
17565
17571
|
// ../../packages/local-graph/src/node-select.ts
|
|
17572
|
+
var SELECT_INDEXES = ["nodes_live_local_community", "nodes_live_fold_candidate"];
|
|
17566
17573
|
var EMBEDDING_DIM_SQL = "(CASE WHEN length(embedding_blob) >= 4 THEN length(embedding_blob) / 4 ELSE COALESCE(json_array_length(embedding_json), 0) END)";
|
|
17567
17574
|
var KEY = /^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$/;
|
|
17568
17575
|
var COLUMNS = /* @__PURE__ */ new Set([
|
|
@@ -17578,6 +17585,10 @@ function path(key) {
|
|
|
17578
17585
|
if (!KEY.test(key)) throw new Error(`selectNodes: bad attr key ${key}`);
|
|
17579
17586
|
return `'$.${key}'`;
|
|
17580
17587
|
}
|
|
17588
|
+
function selectIndex(i) {
|
|
17589
|
+
if (!SELECT_INDEXES.includes(i)) throw new Error(`selectNodes: bad index ${i}`);
|
|
17590
|
+
return i;
|
|
17591
|
+
}
|
|
17581
17592
|
function column(c) {
|
|
17582
17593
|
if (!COLUMNS.has(c)) throw new Error(`selectNodes: bad column ${c}`);
|
|
17583
17594
|
return c;
|
|
@@ -17593,6 +17604,12 @@ function compileNodeWhere(q) {
|
|
|
17593
17604
|
} else if ("idPrefix" in w) {
|
|
17594
17605
|
conds.push("substr(id, 1, ?) = ?");
|
|
17595
17606
|
params.push(w.idPrefix.length, w.idPrefix);
|
|
17607
|
+
} else if ("idIn" in w) {
|
|
17608
|
+
if (w.idIn.length === 0) conds.push("0");
|
|
17609
|
+
else {
|
|
17610
|
+
conds.push(`id IN (${w.idIn.map(() => "?").join(", ")})`);
|
|
17611
|
+
params.push(...w.idIn);
|
|
17612
|
+
}
|
|
17596
17613
|
} else if ("descriptionIn" in w) {
|
|
17597
17614
|
if (w.descriptionIn.length === 0) conds.push("0");
|
|
17598
17615
|
else {
|
|
@@ -17649,7 +17666,7 @@ function compileNodeSelect(q) {
|
|
|
17649
17666
|
}
|
|
17650
17667
|
const orderParams = !q.orderBy && q.labels.length > 1 ? [...q.labels] : [];
|
|
17651
17668
|
const limit = q.limit !== void 0 ? ` LIMIT ${Math.max(0, Math.floor(q.limit))}` : "";
|
|
17652
|
-
const sql = `SELECT id, label, description, created_at, last_updated_at, page_rank, cumulative_hits, extraction_confidence, extraction_source, community, created_at_seq, last_reinforced_at_seq, valid_to, logical_id, version, ${EMBEDDING_DIM_SQL} AS embedding_dim${q.withEmbedding ? ", embedding_blob, embedding_json" : ""}${attrCols} FROM nodes WHERE ${where} ORDER BY ${order}${limit}`;
|
|
17669
|
+
const sql = `SELECT id, label, description, created_at, last_updated_at, page_rank, cumulative_hits, extraction_confidence, extraction_source, community, created_at_seq, last_reinforced_at_seq, valid_to, logical_id, version, ${EMBEDDING_DIM_SQL} AS embedding_dim${q.withEmbedding ? ", embedding_blob, embedding_json" : ""}${attrCols} FROM nodes${q.indexedBy ? ` INDEXED BY ${selectIndex(q.indexedBy)}` : ""} WHERE ${where} ORDER BY ${order}${limit}`;
|
|
17653
17670
|
return { sql, params: [...params, ...orderParams] };
|
|
17654
17671
|
}
|
|
17655
17672
|
function decodeAttr(value, type) {
|
|
@@ -17847,6 +17864,19 @@ var TEMPORAL_INDEX_SQL = `
|
|
|
17847
17864
|
CREATE INDEX IF NOT EXISTS nodes_live ON nodes(label, valid_to);
|
|
17848
17865
|
CREATE INDEX IF NOT EXISTS nodes_live_count ON nodes(valid_to);
|
|
17849
17866
|
CREATE INDEX IF NOT EXISTS nodes_live_recent ON nodes(label, last_updated_at DESC) WHERE valid_to IS NULL;
|
|
17867
|
+
-- "Anything created since the watermark?" guards (the fold, regression and
|
|
17868
|
+
-- canonical-bind sweeps run one per session boundary). Without it the planner
|
|
17869
|
+
-- walks nodes_live and reads every live row of the label to test created_at:
|
|
17870
|
+
-- 56 MB per guard on a 1.1 GB store, to learn there was nothing to do. valid_to
|
|
17871
|
+
-- is a key column so the planner prefers this over nodes_live (one more term).
|
|
17872
|
+
CREATE INDEX IF NOT EXISTS nodes_live_created ON nodes(label, valid_to, created_at) WHERE valid_to IS NULL;
|
|
17873
|
+
-- Community members (the skills pull reads them twice per session boundary):
|
|
17874
|
+
-- ~15% of semantic nodes carry a localCommunity, and without this every live
|
|
17875
|
+
-- semantic row was read to find them (168 MB \u2192 18 MB per read).
|
|
17876
|
+
CREATE INDEX IF NOT EXISTS nodes_live_local_community ON nodes(label, valid_to, json_extract(attrs_json, '$.localCommunity')) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.localCommunity') IS NOT NULL;
|
|
17877
|
+
-- Problems parked for the fold judge (read every settle boundary): ~1 in 5 open
|
|
17878
|
+
-- Problems; without it every live Problem was read to find them (56 MB).
|
|
17879
|
+
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;
|
|
17850
17880
|
CREATE INDEX IF NOT EXISTS nodes_session_evidence ON nodes(id) WHERE ${SESSION_EVIDENCE_PREDICATE};
|
|
17851
17881
|
CREATE INDEX IF NOT EXISTS edges_session_evidence ON edges(id) WHERE ${SESSION_EVIDENCE_PREDICATE};
|
|
17852
17882
|
-- Covering + partial index for labelFingerprint over the instance labels: the
|
|
@@ -17861,6 +17891,10 @@ CREATE INDEX IF NOT EXISTS nodes_instance_fingerprint_v2 ON nodes(label, last_up
|
|
|
17861
17891
|
CREATE INDEX IF NOT EXISTS nodes_live_page_rank ON nodes(valid_to, page_rank);
|
|
17862
17892
|
CREATE INDEX IF NOT EXISTS nodes_logical ON nodes(logical_id, version);
|
|
17863
17893
|
CREATE INDEX IF NOT EXISTS edges_live ON edges(valid_to);
|
|
17894
|
+
-- Newest live edges first, so a "recent N matching edges" read stops early instead
|
|
17895
|
+
-- of matching and sorting every live edge (the hourly liveness check: 163 MB \u2192
|
|
17896
|
+
-- 69 MB). Named by recentEdgeAttrCoverage; the planner keeps edges_live otherwise.
|
|
17897
|
+
CREATE INDEX IF NOT EXISTS edges_live_created ON edges(created_at) WHERE valid_to IS NULL;
|
|
17864
17898
|
-- Expression index on the per-node relPath (stored in attrs_json) so the
|
|
17865
17899
|
-- incremental reindex can fetch just the changed files' symbols instead of
|
|
17866
17900
|
-- scanning every versioned node. Partial: only live rows are ever queried.
|
|
@@ -18557,6 +18591,27 @@ var SqliteGraphStore = class {
|
|
|
18557
18591
|
cloud: r.cloud === 1
|
|
18558
18592
|
}));
|
|
18559
18593
|
}
|
|
18594
|
+
findNodeStampsByLabelAttrs(label, attrKeys) {
|
|
18595
|
+
for (const k of attrKeys) if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(k)) throw new Error(`findNodeStampsByLabelAttrs: bad attr key ${k}`);
|
|
18596
|
+
const keys = [...new Set(attrKeys)];
|
|
18597
|
+
const rows = this.db.prepare(
|
|
18598
|
+
`SELECT id, created_at, created_at_seq,
|
|
18599
|
+
${keys.map((k, i) => `json_type(attrs_json, '$.${k}') IS NOT NULL AS h${i}`).join(", ")}${keys.length ? "," : ""}
|
|
18600
|
+
COALESCE(json_extract(attrs_json, '$.source') = 'cloud', 0) AS cloud
|
|
18601
|
+
FROM nodes WHERE label = ? AND valid_to IS NULL`
|
|
18602
|
+
).all(label);
|
|
18603
|
+
const out = new Map(keys.map((k) => [k, []]));
|
|
18604
|
+
for (const r of rows) {
|
|
18605
|
+
keys.forEach((k, i) => out.get(k).push({
|
|
18606
|
+
id: r.id,
|
|
18607
|
+
createdAt: r.created_at,
|
|
18608
|
+
...r.created_at_seq != null ? { createdAtSeq: r.created_at_seq } : {},
|
|
18609
|
+
has: r[`h${i}`] === 1,
|
|
18610
|
+
cloud: r.cloud === 1
|
|
18611
|
+
}));
|
|
18612
|
+
}
|
|
18613
|
+
return out;
|
|
18614
|
+
}
|
|
18560
18615
|
findNodeAttrsByLabel(label) {
|
|
18561
18616
|
const rows = this.stmts.attrsByLabel.all(label);
|
|
18562
18617
|
return rows.map((r) => ({
|
|
@@ -18798,6 +18853,9 @@ var SqliteGraphStore = class {
|
|
|
18798
18853
|
lineageChangedFiles(sinceSeq) {
|
|
18799
18854
|
return readLineageChangedFiles(this.db, sinceSeq);
|
|
18800
18855
|
}
|
|
18856
|
+
changedNodeIds(sinceSeq, labels) {
|
|
18857
|
+
return readChangedNodeIds(this.db, sinceSeq, labels);
|
|
18858
|
+
}
|
|
18801
18859
|
pruneChangeLog() {
|
|
18802
18860
|
return pruneChangeLog(this.db);
|
|
18803
18861
|
}
|
|
@@ -18992,7 +19050,7 @@ var SqliteGraphStore = class {
|
|
|
18992
19050
|
const r = this.db.prepare(
|
|
18993
19051
|
`SELECT COUNT(*) AS total,
|
|
18994
19052
|
COALESCE(SUM(CASE WHEN json_extract(attrs_json, '$.' || ?) = 1 THEN 1 ELSE 0 END), 0) AS count
|
|
18995
|
-
FROM (SELECT attrs_json FROM edges
|
|
19053
|
+
FROM (SELECT attrs_json FROM edges INDEXED BY edges_live_created
|
|
18996
19054
|
WHERE valid_to IS NULL AND attrs_json LIKE ?
|
|
18997
19055
|
ORDER BY created_at DESC LIMIT ?)`
|
|
18998
19056
|
).get(attr, `%${marker}%`, limit);
|
|
@@ -19140,8 +19198,9 @@ var SqliteGraphStore = class {
|
|
|
19140
19198
|
* so it must be a plain identifier. */
|
|
19141
19199
|
nodesAsOfWhereTrue(t, attr) {
|
|
19142
19200
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(attr)) throw new Error(`nodesAsOfWhereTrue: bad attr ${JSON.stringify(attr)}`);
|
|
19201
|
+
const indexed = attr === "revisit" ? " INDEXED BY nodes_revisit_true" : "";
|
|
19143
19202
|
const rows = this.db.prepare(
|
|
19144
|
-
`SELECT * FROM nodes WHERE valid_from <= :t AND (valid_to IS NULL OR valid_to > :t)
|
|
19203
|
+
`SELECT * FROM nodes${indexed} WHERE valid_from <= :t AND (valid_to IS NULL OR valid_to > :t)
|
|
19145
19204
|
AND json_type(attrs_json, '$.${attr}') = 'true'
|
|
19146
19205
|
ORDER BY rowid`
|
|
19147
19206
|
).all({ t });
|
package/errata.mjs
CHANGED
|
@@ -19243,6 +19243,12 @@ function changeLogCovers(db, sinceSeq) {
|
|
|
19243
19243
|
const prunedTo = Number(db.prepare("SELECT value FROM store_meta WHERE key = ?").get(PRUNED_TO_KEY)?.["value"] ?? NaN);
|
|
19244
19244
|
return Number.isFinite(prunedTo) && sinceSeq >= prunedTo;
|
|
19245
19245
|
}
|
|
19246
|
+
function readChangedNodeIds(db, sinceSeq, labels) {
|
|
19247
|
+
if (!changeLogCovers(db, sinceSeq)) return null;
|
|
19248
|
+
if (!labels.length) return [];
|
|
19249
|
+
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);
|
|
19250
|
+
return rows.map((r) => r.id);
|
|
19251
|
+
}
|
|
19246
19252
|
function readLineageChangedFiles(db, sinceSeq) {
|
|
19247
19253
|
if (!changeLogCovers(db, sinceSeq)) return null;
|
|
19248
19254
|
const rows = db.prepare(LINEAGE_CHANGED_FILES_SQL).all({ s: sinceSeq });
|
|
@@ -20376,6 +20382,10 @@ function path(key) {
|
|
|
20376
20382
|
if (!KEY.test(key)) throw new Error(`selectNodes: bad attr key ${key}`);
|
|
20377
20383
|
return `'$.${key}'`;
|
|
20378
20384
|
}
|
|
20385
|
+
function selectIndex(i2) {
|
|
20386
|
+
if (!SELECT_INDEXES.includes(i2)) throw new Error(`selectNodes: bad index ${i2}`);
|
|
20387
|
+
return i2;
|
|
20388
|
+
}
|
|
20379
20389
|
function column(c) {
|
|
20380
20390
|
if (!COLUMNS.has(c)) throw new Error(`selectNodes: bad column ${c}`);
|
|
20381
20391
|
return c;
|
|
@@ -20391,6 +20401,12 @@ function compileNodeWhere(q) {
|
|
|
20391
20401
|
} else if ("idPrefix" in w) {
|
|
20392
20402
|
conds.push("substr(id, 1, ?) = ?");
|
|
20393
20403
|
params.push(w.idPrefix.length, w.idPrefix);
|
|
20404
|
+
} else if ("idIn" in w) {
|
|
20405
|
+
if (w.idIn.length === 0) conds.push("0");
|
|
20406
|
+
else {
|
|
20407
|
+
conds.push(`id IN (${w.idIn.map(() => "?").join(", ")})`);
|
|
20408
|
+
params.push(...w.idIn);
|
|
20409
|
+
}
|
|
20394
20410
|
} else if ("descriptionIn" in w) {
|
|
20395
20411
|
if (w.descriptionIn.length === 0) conds.push("0");
|
|
20396
20412
|
else {
|
|
@@ -20447,7 +20463,7 @@ function compileNodeSelect(q) {
|
|
|
20447
20463
|
}
|
|
20448
20464
|
const orderParams = !q.orderBy && q.labels.length > 1 ? [...q.labels] : [];
|
|
20449
20465
|
const limit = q.limit !== void 0 ? ` LIMIT ${Math.max(0, Math.floor(q.limit))}` : "";
|
|
20450
|
-
const sql = `SELECT id, label, description, created_at, last_updated_at, page_rank, cumulative_hits, extraction_confidence, extraction_source, community, created_at_seq, last_reinforced_at_seq, valid_to, logical_id, version, ${EMBEDDING_DIM_SQL} AS embedding_dim${q.withEmbedding ? ", embedding_blob, embedding_json" : ""}${attrCols} FROM nodes WHERE ${where} ORDER BY ${order}${limit}`;
|
|
20466
|
+
const sql = `SELECT id, label, description, created_at, last_updated_at, page_rank, cumulative_hits, extraction_confidence, extraction_source, community, created_at_seq, last_reinforced_at_seq, valid_to, logical_id, version, ${EMBEDDING_DIM_SQL} AS embedding_dim${q.withEmbedding ? ", embedding_blob, embedding_json" : ""}${attrCols} FROM nodes${q.indexedBy ? ` INDEXED BY ${selectIndex(q.indexedBy)}` : ""} WHERE ${where} ORDER BY ${order}${limit}`;
|
|
20451
20467
|
return { sql, params: [...params, ...orderParams] };
|
|
20452
20468
|
}
|
|
20453
20469
|
function decodeAttr(value, type) {
|
|
@@ -20495,10 +20511,11 @@ function decodeSelectedRow(r, attrs, decodeVector) {
|
|
|
20495
20511
|
attrs: out2
|
|
20496
20512
|
};
|
|
20497
20513
|
}
|
|
20498
|
-
var EMBEDDING_DIM_SQL, KEY, COLUMNS;
|
|
20514
|
+
var SELECT_INDEXES, EMBEDDING_DIM_SQL, KEY, COLUMNS;
|
|
20499
20515
|
var init_node_select = __esm({
|
|
20500
20516
|
"../../packages/local-graph/src/node-select.ts"() {
|
|
20501
20517
|
"use strict";
|
|
20518
|
+
SELECT_INDEXES = ["nodes_live_local_community", "nodes_live_fold_candidate"];
|
|
20502
20519
|
EMBEDDING_DIM_SQL = "(CASE WHEN length(embedding_blob) >= 4 THEN length(embedding_blob) / 4 ELSE COALESCE(json_array_length(embedding_json), 0) END)";
|
|
20503
20520
|
KEY = /^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$/;
|
|
20504
20521
|
COLUMNS = /* @__PURE__ */ new Set([
|
|
@@ -20756,6 +20773,19 @@ CREATE UNIQUE INDEX IF NOT EXISTS edges_triple ON edges(from_id, type, to_id);
|
|
|
20756
20773
|
CREATE INDEX IF NOT EXISTS nodes_live ON nodes(label, valid_to);
|
|
20757
20774
|
CREATE INDEX IF NOT EXISTS nodes_live_count ON nodes(valid_to);
|
|
20758
20775
|
CREATE INDEX IF NOT EXISTS nodes_live_recent ON nodes(label, last_updated_at DESC) WHERE valid_to IS NULL;
|
|
20776
|
+
-- "Anything created since the watermark?" guards (the fold, regression and
|
|
20777
|
+
-- canonical-bind sweeps run one per session boundary). Without it the planner
|
|
20778
|
+
-- walks nodes_live and reads every live row of the label to test created_at:
|
|
20779
|
+
-- 56 MB per guard on a 1.1 GB store, to learn there was nothing to do. valid_to
|
|
20780
|
+
-- is a key column so the planner prefers this over nodes_live (one more term).
|
|
20781
|
+
CREATE INDEX IF NOT EXISTS nodes_live_created ON nodes(label, valid_to, created_at) WHERE valid_to IS NULL;
|
|
20782
|
+
-- Community members (the skills pull reads them twice per session boundary):
|
|
20783
|
+
-- ~15% of semantic nodes carry a localCommunity, and without this every live
|
|
20784
|
+
-- semantic row was read to find them (168 MB \u2192 18 MB per read).
|
|
20785
|
+
CREATE INDEX IF NOT EXISTS nodes_live_local_community ON nodes(label, valid_to, json_extract(attrs_json, '$.localCommunity')) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.localCommunity') IS NOT NULL;
|
|
20786
|
+
-- Problems parked for the fold judge (read every settle boundary): ~1 in 5 open
|
|
20787
|
+
-- Problems; without it every live Problem was read to find them (56 MB).
|
|
20788
|
+
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;
|
|
20759
20789
|
CREATE INDEX IF NOT EXISTS nodes_session_evidence ON nodes(id) WHERE ${SESSION_EVIDENCE_PREDICATE};
|
|
20760
20790
|
CREATE INDEX IF NOT EXISTS edges_session_evidence ON edges(id) WHERE ${SESSION_EVIDENCE_PREDICATE};
|
|
20761
20791
|
-- Covering + partial index for labelFingerprint over the instance labels: the
|
|
@@ -20770,6 +20800,10 @@ CREATE INDEX IF NOT EXISTS nodes_instance_fingerprint_v2 ON nodes(label, last_up
|
|
|
20770
20800
|
CREATE INDEX IF NOT EXISTS nodes_live_page_rank ON nodes(valid_to, page_rank);
|
|
20771
20801
|
CREATE INDEX IF NOT EXISTS nodes_logical ON nodes(logical_id, version);
|
|
20772
20802
|
CREATE INDEX IF NOT EXISTS edges_live ON edges(valid_to);
|
|
20803
|
+
-- Newest live edges first, so a "recent N matching edges" read stops early instead
|
|
20804
|
+
-- of matching and sorting every live edge (the hourly liveness check: 163 MB \u2192
|
|
20805
|
+
-- 69 MB). Named by recentEdgeAttrCoverage; the planner keeps edges_live otherwise.
|
|
20806
|
+
CREATE INDEX IF NOT EXISTS edges_live_created ON edges(created_at) WHERE valid_to IS NULL;
|
|
20773
20807
|
-- Expression index on the per-node relPath (stored in attrs_json) so the
|
|
20774
20808
|
-- incremental reindex can fetch just the changed files' symbols instead of
|
|
20775
20809
|
-- scanning every versioned node. Partial: only live rows are ever queried.
|
|
@@ -21409,6 +21443,27 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
|
|
|
21409
21443
|
cloud: r.cloud === 1
|
|
21410
21444
|
}));
|
|
21411
21445
|
}
|
|
21446
|
+
findNodeStampsByLabelAttrs(label, attrKeys) {
|
|
21447
|
+
for (const k of attrKeys) if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(k)) throw new Error(`findNodeStampsByLabelAttrs: bad attr key ${k}`);
|
|
21448
|
+
const keys = [...new Set(attrKeys)];
|
|
21449
|
+
const rows = this.db.prepare(
|
|
21450
|
+
`SELECT id, created_at, created_at_seq,
|
|
21451
|
+
${keys.map((k, i2) => `json_type(attrs_json, '$.${k}') IS NOT NULL AS h${i2}`).join(", ")}${keys.length ? "," : ""}
|
|
21452
|
+
COALESCE(json_extract(attrs_json, '$.source') = 'cloud', 0) AS cloud
|
|
21453
|
+
FROM nodes WHERE label = ? AND valid_to IS NULL`
|
|
21454
|
+
).all(label);
|
|
21455
|
+
const out2 = new Map(keys.map((k) => [k, []]));
|
|
21456
|
+
for (const r of rows) {
|
|
21457
|
+
keys.forEach((k, i2) => out2.get(k).push({
|
|
21458
|
+
id: r.id,
|
|
21459
|
+
createdAt: r.created_at,
|
|
21460
|
+
...r.created_at_seq != null ? { createdAtSeq: r.created_at_seq } : {},
|
|
21461
|
+
has: r[`h${i2}`] === 1,
|
|
21462
|
+
cloud: r.cloud === 1
|
|
21463
|
+
}));
|
|
21464
|
+
}
|
|
21465
|
+
return out2;
|
|
21466
|
+
}
|
|
21412
21467
|
findNodeAttrsByLabel(label) {
|
|
21413
21468
|
const rows = this.stmts.attrsByLabel.all(label);
|
|
21414
21469
|
return rows.map((r) => ({
|
|
@@ -21650,6 +21705,9 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
|
|
|
21650
21705
|
lineageChangedFiles(sinceSeq) {
|
|
21651
21706
|
return readLineageChangedFiles(this.db, sinceSeq);
|
|
21652
21707
|
}
|
|
21708
|
+
changedNodeIds(sinceSeq, labels) {
|
|
21709
|
+
return readChangedNodeIds(this.db, sinceSeq, labels);
|
|
21710
|
+
}
|
|
21653
21711
|
pruneChangeLog() {
|
|
21654
21712
|
return pruneChangeLog(this.db);
|
|
21655
21713
|
}
|
|
@@ -21844,7 +21902,7 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
|
|
|
21844
21902
|
const r = this.db.prepare(
|
|
21845
21903
|
`SELECT COUNT(*) AS total,
|
|
21846
21904
|
COALESCE(SUM(CASE WHEN json_extract(attrs_json, '$.' || ?) = 1 THEN 1 ELSE 0 END), 0) AS count
|
|
21847
|
-
FROM (SELECT attrs_json FROM edges
|
|
21905
|
+
FROM (SELECT attrs_json FROM edges INDEXED BY edges_live_created
|
|
21848
21906
|
WHERE valid_to IS NULL AND attrs_json LIKE ?
|
|
21849
21907
|
ORDER BY created_at DESC LIMIT ?)`
|
|
21850
21908
|
).get(attr, `%${marker}%`, limit);
|
|
@@ -21992,8 +22050,9 @@ CREATE INDEX IF NOT EXISTS nodes_revisit_true ON nodes(valid_from) WHERE json_ty
|
|
|
21992
22050
|
* so it must be a plain identifier. */
|
|
21993
22051
|
nodesAsOfWhereTrue(t, attr) {
|
|
21994
22052
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(attr)) throw new Error(`nodesAsOfWhereTrue: bad attr ${JSON.stringify(attr)}`);
|
|
22053
|
+
const indexed = attr === "revisit" ? " INDEXED BY nodes_revisit_true" : "";
|
|
21995
22054
|
const rows = this.db.prepare(
|
|
21996
|
-
`SELECT * FROM nodes WHERE valid_from <= :t AND (valid_to IS NULL OR valid_to > :t)
|
|
22055
|
+
`SELECT * FROM nodes${indexed} WHERE valid_from <= :t AND (valid_to IS NULL OR valid_to > :t)
|
|
21997
22056
|
AND json_type(attrs_json, '$.${attr}') = 'true'
|
|
21998
22057
|
ORDER BY rowid`
|
|
21999
22058
|
).all({ t });
|
|
@@ -22373,19 +22432,16 @@ function conditionOf(node2) {
|
|
|
22373
22432
|
}
|
|
22374
22433
|
function clearAnsweredRevisits(store, ts) {
|
|
22375
22434
|
const report = { cleared: 0, standing: 0 };
|
|
22376
|
-
|
|
22377
|
-
|
|
22378
|
-
|
|
22379
|
-
|
|
22380
|
-
|
|
22381
|
-
|
|
22382
|
-
|
|
22383
|
-
report.standing++;
|
|
22384
|
-
continue;
|
|
22385
|
-
}
|
|
22386
|
-
clearRevisit(store, node2.id, ts);
|
|
22387
|
-
report.cleared++;
|
|
22435
|
+
const semantic = new Set(SEMANTIC_NODE_LABELS);
|
|
22436
|
+
for (const node2 of store.nodesAsOfWhereTrue(ts, "revisit")) {
|
|
22437
|
+
if (!semantic.has(node2.label) || node2.validTo != null) continue;
|
|
22438
|
+
const condition = conditionOf(node2);
|
|
22439
|
+
if (condition === void 0 || !isRevisitAnswered(store, node2, condition)) {
|
|
22440
|
+
report.standing++;
|
|
22441
|
+
continue;
|
|
22388
22442
|
}
|
|
22443
|
+
clearRevisit(store, node2.id, ts);
|
|
22444
|
+
report.cleared++;
|
|
22389
22445
|
}
|
|
22390
22446
|
return report;
|
|
22391
22447
|
}
|
|
@@ -26253,7 +26309,10 @@ function communityMembers(store) {
|
|
|
26253
26309
|
return store.selectNodes({
|
|
26254
26310
|
labels: JOINT_COMMUNITY_LABELS,
|
|
26255
26311
|
attrs: ["localCommunity", "cloudNodeId", "source"],
|
|
26256
|
-
where: [{ attr: "localCommunity", notNullish: true }]
|
|
26312
|
+
where: [{ attr: "localCommunity", notNullish: true }],
|
|
26313
|
+
// ~15% of semantic nodes are members; the ordered select otherwise walks
|
|
26314
|
+
// nodes_live and reads every live semantic row (168 MB on a 1.1 GB store).
|
|
26315
|
+
indexedBy: "nodes_live_local_community"
|
|
26257
26316
|
});
|
|
26258
26317
|
}
|
|
26259
26318
|
function communitySeeds(store, opts = {}) {
|
|
@@ -26451,7 +26510,10 @@ async function resolveFoldCandidates(store, opts) {
|
|
|
26451
26510
|
{ attr: "resolvedAt", nullish: true },
|
|
26452
26511
|
{ attr: "mergedInto", absent: true },
|
|
26453
26512
|
{ attr: "foldCandidate", present: true }
|
|
26454
|
-
]
|
|
26513
|
+
],
|
|
26514
|
+
// Only parked Problems are in it; the rowid-ordered select otherwise walks
|
|
26515
|
+
// nodes_live and reads every live Problem (56 MB per settle boundary).
|
|
26516
|
+
indexedBy: "nodes_live_fold_candidate"
|
|
26455
26517
|
});
|
|
26456
26518
|
const markOf = (n) => n.attrs["foldCandidate"];
|
|
26457
26519
|
report.pending = pool.length;
|
|
@@ -26783,7 +26845,7 @@ var init_principle_sync = __esm({
|
|
|
26783
26845
|
});
|
|
26784
26846
|
|
|
26785
26847
|
// ../../packages/local-graph/src/mechanism-liveness.ts
|
|
26786
|
-
function evaluateFed(store, d) {
|
|
26848
|
+
function evaluateFed(store, d, stamps) {
|
|
26787
26849
|
if (d.fedIncoming) {
|
|
26788
26850
|
const { count: count2, total } = store.incomingEdgeCoverage?.(
|
|
26789
26851
|
d.fedIncoming.label,
|
|
@@ -26803,7 +26865,7 @@ function evaluateFed(store, d) {
|
|
|
26803
26865
|
if (!d.fed) return { count: 0, total: 0, fraction: 0 };
|
|
26804
26866
|
const column2 = COLUMN_INPUTS[d.fed.attr];
|
|
26805
26867
|
const fed = d.fed;
|
|
26806
|
-
const all = fed.labels.flatMap((label) => store.findNodeStampsByLabel ? store.findNodeStampsByLabel(label, fed.attr) : store.findNodeAttrsByLabel(label));
|
|
26868
|
+
const all = fed.labels.flatMap((label) => stamps?.(label, fed.attr) ?? (store.findNodeStampsByLabel ? store.findNodeStampsByLabel(label, fed.attr) : store.findNodeAttrsByLabel(label)));
|
|
26807
26869
|
const isCloudMirror = (n) => n.cloud ?? n.attrs?.["source"] === "cloud";
|
|
26808
26870
|
const nodes = fed.localOnly ? all.filter((n) => !isCloudMirror(n)) : all;
|
|
26809
26871
|
const has = (n) => column2 ? column2(n) : n.has ?? n.attrs?.[fed.attr] !== void 0;
|
|
@@ -26826,8 +26888,15 @@ function evaluateMechanisms(store, invocations = /* @__PURE__ */ new Map(), mech
|
|
|
26826
26888
|
if (!counts.has(label)) counts.set(label, store.countNodesByLabel(label));
|
|
26827
26889
|
return counts.get(label);
|
|
26828
26890
|
};
|
|
26891
|
+
const wanted = /* @__PURE__ */ new Map();
|
|
26892
|
+
for (const d of mechanisms) for (const l of d.fed?.labels ?? []) wanted.set(l, (wanted.get(l) ?? /* @__PURE__ */ new Set()).add(d.fed.attr));
|
|
26893
|
+
const byLabel = /* @__PURE__ */ new Map();
|
|
26894
|
+
const stamps = store.findNodeStampsByLabelAttrs ? (label, attr) => {
|
|
26895
|
+
if (!byLabel.has(label)) byLabel.set(label, store.findNodeStampsByLabelAttrs(label, [...wanted.get(label) ?? [attr]]));
|
|
26896
|
+
return byLabel.get(label).get(attr);
|
|
26897
|
+
} : void 0;
|
|
26829
26898
|
return mechanisms.map((d) => {
|
|
26830
|
-
const fed = evaluateFed(store, d);
|
|
26899
|
+
const fed = evaluateFed(store, d, stamps);
|
|
26831
26900
|
const inv = invocations.get(d.pass);
|
|
26832
26901
|
const effectCount = d.effectIsLevel ? Math.max(
|
|
26833
26902
|
0,
|
|
@@ -34598,7 +34667,10 @@ function embedCodeNodes(store, opts = {}) {
|
|
|
34598
34667
|
return embedNodesByLabels(store, CODE_LABELS, opts);
|
|
34599
34668
|
}
|
|
34600
34669
|
function embedSemanticNodes(store, opts = {}) {
|
|
34601
|
-
return embedNodesByLabels(store, SEMANTIC_LABELS,
|
|
34670
|
+
return embedNodesByLabels(store, SEMANTIC_LABELS, {
|
|
34671
|
+
incrementalKey: `embed-scan:semantic:${opts.mode === "hash" ? "hash" : "model"}`,
|
|
34672
|
+
...opts
|
|
34673
|
+
});
|
|
34602
34674
|
}
|
|
34603
34675
|
async function ensureNodeEmbedded(store, nodeId, opts = {}) {
|
|
34604
34676
|
const node2 = store.getNode(nodeId);
|
|
@@ -34671,14 +34743,32 @@ async function embedNodesByLabels(store, labels, opts = {}) {
|
|
|
34671
34743
|
};
|
|
34672
34744
|
const hashOnly = opts.mode === "hash";
|
|
34673
34745
|
const candidates = [];
|
|
34674
|
-
|
|
34675
|
-
|
|
34676
|
-
|
|
34677
|
-
|
|
34678
|
-
|
|
34746
|
+
const watermarkKey = opts.incrementalKey;
|
|
34747
|
+
const seqBefore = watermarkKey ? store.changeSeq?.() : void 0;
|
|
34748
|
+
let held = null;
|
|
34749
|
+
try {
|
|
34750
|
+
held = watermarkKey ? JSON.parse(store.getMeta?.(watermarkKey) ?? "null") : null;
|
|
34751
|
+
} catch {
|
|
34752
|
+
held = null;
|
|
34753
|
+
}
|
|
34754
|
+
const changedIds = !opts.force && held && seqBefore !== void 0 ? store.changedNodeIds?.(held.seq, labels) ?? null : null;
|
|
34755
|
+
const scan2 = function* () {
|
|
34756
|
+
if (changedIds === null) {
|
|
34757
|
+
for (const label of labels) yield* store.selectNodes({ labels: [label], attrs: EMBED_SCAN_ATTRS });
|
|
34758
|
+
return;
|
|
34759
|
+
}
|
|
34760
|
+
for (let i2 = 0; i2 < changedIds.length; i2 += ID_CHUNK) {
|
|
34761
|
+
yield* store.selectNodes({ labels, attrs: EMBED_SCAN_ATTRS, where: [{ idIn: changedIds.slice(i2, i2 + ID_CHUNK) }] });
|
|
34762
|
+
}
|
|
34763
|
+
};
|
|
34764
|
+
const complete = (version3) => {
|
|
34765
|
+
if (watermarkKey && seqBefore !== void 0 && report.failed === 0) store.setMeta?.(watermarkKey, JSON.stringify({ seq: seqBefore, version: version3 }));
|
|
34766
|
+
};
|
|
34767
|
+
{
|
|
34768
|
+
for (const node2 of scan2()) {
|
|
34679
34769
|
report.scanned++;
|
|
34680
34770
|
if (!eligibleForEmbedding(store, node2)) {
|
|
34681
|
-
clearWithdrawnVector(store, node2.id);
|
|
34771
|
+
if (node2.embeddingDim > 0 || node2.attrs["embeddingVersion"] !== void 0) clearWithdrawnVector(store, node2.id);
|
|
34682
34772
|
continue;
|
|
34683
34773
|
}
|
|
34684
34774
|
const existingVersion = node2.attrs["embeddingVersion"] ?? null;
|
|
@@ -34693,10 +34783,18 @@ async function embedNodesByLabels(store, labels, opts = {}) {
|
|
|
34693
34783
|
}
|
|
34694
34784
|
}
|
|
34695
34785
|
if (candidates.length === 0) {
|
|
34786
|
+
complete(held?.version ?? (hashOnly ? EMBEDDING_VERSION : null));
|
|
34696
34787
|
report.durationMs = Date.now() - started2;
|
|
34697
34788
|
return report;
|
|
34698
34789
|
}
|
|
34699
34790
|
const currentVersion = hashOnly ? EMBEDDING_VERSION : (await embedBatchWithModelOrHash(["probe"]))[0]?.version ?? "unknown";
|
|
34791
|
+
if (changedIds !== null && held?.version && held.version !== currentVersion) {
|
|
34792
|
+
const { incrementalKey: _incremental, ...fullOpts } = opts;
|
|
34793
|
+
return embedNodesByLabels(store, labels, fullOpts).then((full) => {
|
|
34794
|
+
if (full.failed === 0 && watermarkKey && seqBefore !== void 0) store.setMeta?.(watermarkKey, JSON.stringify({ seq: seqBefore, version: currentVersion }));
|
|
34795
|
+
return full;
|
|
34796
|
+
});
|
|
34797
|
+
}
|
|
34700
34798
|
const todo = [];
|
|
34701
34799
|
for (const { node: node2, existingVersion } of candidates) {
|
|
34702
34800
|
if (!opts.force && existingVersion === currentVersion && node2.embeddingDim > 0) {
|
|
@@ -34732,6 +34830,7 @@ async function embedNodesByLabels(store, labels, opts = {}) {
|
|
|
34732
34830
|
});
|
|
34733
34831
|
if (opts.progress) opts.progress(report.embedded, todo.length);
|
|
34734
34832
|
}
|
|
34833
|
+
complete(currentVersion);
|
|
34735
34834
|
report.durationMs = Date.now() - started2;
|
|
34736
34835
|
return report;
|
|
34737
34836
|
}
|
|
@@ -34752,7 +34851,7 @@ function findDocstring(store, nodeId) {
|
|
|
34752
34851
|
}
|
|
34753
34852
|
return null;
|
|
34754
34853
|
}
|
|
34755
|
-
var CODE_LABELS, SEMANTIC_LABELS, BATCH_SIZE, EMBED_SCAN_ATTRS;
|
|
34854
|
+
var CODE_LABELS, SEMANTIC_LABELS, BATCH_SIZE, ID_CHUNK, EMBED_SCAN_ATTRS;
|
|
34756
34855
|
var init_embed_code = __esm({
|
|
34757
34856
|
"../../packages/generalizer/src/embed-code.ts"() {
|
|
34758
34857
|
"use strict";
|
|
@@ -34761,6 +34860,7 @@ var init_embed_code = __esm({
|
|
|
34761
34860
|
CODE_LABELS = ["Function", "Method", "Class", "Const"];
|
|
34762
34861
|
SEMANTIC_LABELS = ["Problem", "RootCause", "Solution", "Pattern", "Claim"];
|
|
34763
34862
|
BATCH_SIZE = 32;
|
|
34863
|
+
ID_CHUNK = 500;
|
|
34764
34864
|
EMBED_SCAN_ATTRS = [
|
|
34765
34865
|
"cloudNodeId",
|
|
34766
34866
|
"source",
|
|
@@ -86568,6 +86668,8 @@ function createLivenessWatch(deps) {
|
|
|
86568
86668
|
// src/workspace-maintenance.ts
|
|
86569
86669
|
init_src8();
|
|
86570
86670
|
init_src5();
|
|
86671
|
+
var WM_CALIBRATION_AT_KEY = "wm-calibration-at";
|
|
86672
|
+
var WM_CALIBRATION_INTERVAL_MS = 30 * 6e4;
|
|
86571
86673
|
function runWorkspaceMaintenance(store, workspaceId2) {
|
|
86572
86674
|
const seqBefore = store.currentIngestSeq();
|
|
86573
86675
|
const report = runSemanticMaintenance(store);
|
|
@@ -86578,9 +86680,13 @@ function runWorkspaceMaintenance(store, workspaceId2) {
|
|
|
86578
86680
|
}
|
|
86579
86681
|
let calibration = null;
|
|
86580
86682
|
const calibrationStarted = Date.now();
|
|
86581
|
-
|
|
86582
|
-
|
|
86583
|
-
|
|
86683
|
+
const lastCalibration = Number(store.getMeta?.(WM_CALIBRATION_AT_KEY) ?? 0);
|
|
86684
|
+
if (!(calibrationStarted - lastCalibration < WM_CALIBRATION_INTERVAL_MS)) {
|
|
86685
|
+
try {
|
|
86686
|
+
calibration = wmCalibrationCells(store);
|
|
86687
|
+
store.setMeta?.(WM_CALIBRATION_AT_KEY, String(calibrationStarted));
|
|
86688
|
+
} catch {
|
|
86689
|
+
}
|
|
86584
86690
|
}
|
|
86585
86691
|
return {
|
|
86586
86692
|
report,
|
|
@@ -86711,7 +86817,7 @@ function createWatchBreaker(deps) {
|
|
|
86711
86817
|
}
|
|
86712
86818
|
|
|
86713
86819
|
// src/engine.ts
|
|
86714
|
-
var DAEMON_VERSION = true ? "2.0.22-dev.
|
|
86820
|
+
var DAEMON_VERSION = true ? "2.0.22-dev.4624" : "2.0.0-alpha.0";
|
|
86715
86821
|
var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
|
|
86716
86822
|
var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
|
|
86717
86823
|
var GIT_OP_MUTE_MS = 4e3;
|
|
@@ -93254,7 +93360,7 @@ function isDoorDeferral(outcome) {
|
|
|
93254
93360
|
return outcome.status === "error" && !!outcome.parked && !!outcome.runId && ["deferred-context", "deferred-budget"].includes(outcome.errorCode ?? "");
|
|
93255
93361
|
}
|
|
93256
93362
|
var DEFAULT_LOOKAHEAD = 2;
|
|
93257
|
-
var CONTENT_TRANSPORT_VERSION =
|
|
93363
|
+
var CONTENT_TRANSPORT_VERSION = 2;
|
|
93258
93364
|
async function defaultGit(root, args2) {
|
|
93259
93365
|
const { stdout } = await execFileAsync2("git", ["-C", root, ...args2], { maxBuffer: 64 * 1024 * 1024, windowsHide: true });
|
|
93260
93366
|
return stdout;
|
|
@@ -93816,6 +93922,9 @@ function applyStopRequest(profile, now = () => /* @__PURE__ */ new Date()) {
|
|
|
93816
93922
|
}
|
|
93817
93923
|
};
|
|
93818
93924
|
}
|
|
93925
|
+
function historyIngestEnabled() {
|
|
93926
|
+
return process.env.ERRATA_HISTORY === "1";
|
|
93927
|
+
}
|
|
93819
93928
|
async function startHistoryStage(root, input, client, walk11 = {}) {
|
|
93820
93929
|
let profile = input;
|
|
93821
93930
|
if (!profile.projectId) return { started: false, reason: "no-project", profile };
|
|
@@ -93829,6 +93938,7 @@ async function startHistoryStage(root, input, client, walk11 = {}) {
|
|
|
93829
93938
|
await reportHistoryStage(client, stopped);
|
|
93830
93939
|
return { started: false, reason: "stopped", profile: stopped };
|
|
93831
93940
|
}
|
|
93941
|
+
if (!historyIngestEnabled()) return { started: false, reason: "disabled", profile };
|
|
93832
93942
|
if (!consents.mineHistory) return { started: false, reason: "consent-pending", profile };
|
|
93833
93943
|
const outcome = await runHistoryWalk(root, profile, client, walk11);
|
|
93834
93944
|
return { started: true, profile: outcome.profile };
|
|
@@ -95544,7 +95654,8 @@ async function startMultiDaemon(opts = {}) {
|
|
|
95544
95654
|
for (const record2 of records) record2.engine.markContextDirty();
|
|
95545
95655
|
}
|
|
95546
95656
|
try {
|
|
95547
|
-
if (!
|
|
95657
|
+
if (!historyIngestEnabled()) revisionBacklog.delete(r.root);
|
|
95658
|
+
else if (!r.engine.storeReconciled()) revisionBacklog.add(r.root);
|
|
95548
95659
|
else {
|
|
95549
95660
|
const revision = await syncHistoryRevision({ root: r.root, profile: r.engine.profile, store: r.engine.store, cloud: client, salt });
|
|
95550
95661
|
if (revision.remaining) revisionBacklog.add(r.root);
|
|
@@ -95728,6 +95839,10 @@ async function startMultiDaemon(opts = {}) {
|
|
|
95728
95839
|
Object.assign(engine.profile, stage2.profile);
|
|
95729
95840
|
saveProfile(root, engine.profile);
|
|
95730
95841
|
}
|
|
95842
|
+
if (!stage2.started && stage2.reason === "disabled") {
|
|
95843
|
+
console.log(`[errata] history ingestion is off in this build (v1.1); no walk for ${engine.profile.name}`);
|
|
95844
|
+
return;
|
|
95845
|
+
}
|
|
95731
95846
|
const state = engine.profile.historyStage?.state ?? "consent-pending";
|
|
95732
95847
|
const c2 = engine.profile.historyStage?.counts;
|
|
95733
95848
|
console.log(
|
|
@@ -95746,7 +95861,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
95746
95861
|
void ambientLinkAll();
|
|
95747
95862
|
let historyRetryRunning = false;
|
|
95748
95863
|
const historyRetry = setInterval(() => {
|
|
95749
|
-
if (stopping || historyRetryRunning) return;
|
|
95864
|
+
if (stopping || historyRetryRunning || !historyIngestEnabled()) return;
|
|
95750
95865
|
historyRetryRunning = true;
|
|
95751
95866
|
void ambientLinkAll(true).catch((err2) => {
|
|
95752
95867
|
console.warn(`[errata] history retry pass failed: ${err2 instanceof Error ? err2.message : err2}`);
|
package/generalize-worker.mjs
CHANGED
|
@@ -15753,6 +15753,12 @@ function changeLogCovers(db, sinceSeq) {
|
|
|
15753
15753
|
const prunedTo = Number(db.prepare("SELECT value FROM store_meta WHERE key = ?").get(PRUNED_TO_KEY)?.["value"] ?? NaN);
|
|
15754
15754
|
return Number.isFinite(prunedTo) && sinceSeq >= prunedTo;
|
|
15755
15755
|
}
|
|
15756
|
+
function readChangedNodeIds(db, sinceSeq, labels) {
|
|
15757
|
+
if (!changeLogCovers(db, sinceSeq)) return null;
|
|
15758
|
+
if (!labels.length) return [];
|
|
15759
|
+
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);
|
|
15760
|
+
return rows.map((r) => r.id);
|
|
15761
|
+
}
|
|
15756
15762
|
function readLineageChangedFiles(db, sinceSeq) {
|
|
15757
15763
|
if (!changeLogCovers(db, sinceSeq)) return null;
|
|
15758
15764
|
const rows = db.prepare(LINEAGE_CHANGED_FILES_SQL).all({ s: sinceSeq });
|
|
@@ -15960,6 +15966,7 @@ var DEMAND_HOT = Math.log1p(10);
|
|
|
15960
15966
|
var DEMAND_WARM = Math.log1p(5);
|
|
15961
15967
|
|
|
15962
15968
|
// ../../packages/local-graph/src/node-select.ts
|
|
15969
|
+
var SELECT_INDEXES = ["nodes_live_local_community", "nodes_live_fold_candidate"];
|
|
15963
15970
|
var EMBEDDING_DIM_SQL = "(CASE WHEN length(embedding_blob) >= 4 THEN length(embedding_blob) / 4 ELSE COALESCE(json_array_length(embedding_json), 0) END)";
|
|
15964
15971
|
var KEY = /^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$/;
|
|
15965
15972
|
var COLUMNS = /* @__PURE__ */ new Set([
|
|
@@ -15975,6 +15982,10 @@ function path(key) {
|
|
|
15975
15982
|
if (!KEY.test(key)) throw new Error(`selectNodes: bad attr key ${key}`);
|
|
15976
15983
|
return `'$.${key}'`;
|
|
15977
15984
|
}
|
|
15985
|
+
function selectIndex(i) {
|
|
15986
|
+
if (!SELECT_INDEXES.includes(i)) throw new Error(`selectNodes: bad index ${i}`);
|
|
15987
|
+
return i;
|
|
15988
|
+
}
|
|
15978
15989
|
function column(c) {
|
|
15979
15990
|
if (!COLUMNS.has(c)) throw new Error(`selectNodes: bad column ${c}`);
|
|
15980
15991
|
return c;
|
|
@@ -15990,6 +16001,12 @@ function compileNodeWhere(q) {
|
|
|
15990
16001
|
} else if ("idPrefix" in w) {
|
|
15991
16002
|
conds.push("substr(id, 1, ?) = ?");
|
|
15992
16003
|
params.push(w.idPrefix.length, w.idPrefix);
|
|
16004
|
+
} else if ("idIn" in w) {
|
|
16005
|
+
if (w.idIn.length === 0) conds.push("0");
|
|
16006
|
+
else {
|
|
16007
|
+
conds.push(`id IN (${w.idIn.map(() => "?").join(", ")})`);
|
|
16008
|
+
params.push(...w.idIn);
|
|
16009
|
+
}
|
|
15993
16010
|
} else if ("descriptionIn" in w) {
|
|
15994
16011
|
if (w.descriptionIn.length === 0) conds.push("0");
|
|
15995
16012
|
else {
|
|
@@ -16046,7 +16063,7 @@ function compileNodeSelect(q) {
|
|
|
16046
16063
|
}
|
|
16047
16064
|
const orderParams = !q.orderBy && q.labels.length > 1 ? [...q.labels] : [];
|
|
16048
16065
|
const limit = q.limit !== void 0 ? ` LIMIT ${Math.max(0, Math.floor(q.limit))}` : "";
|
|
16049
|
-
const sql = `SELECT id, label, description, created_at, last_updated_at, page_rank, cumulative_hits, extraction_confidence, extraction_source, community, created_at_seq, last_reinforced_at_seq, valid_to, logical_id, version, ${EMBEDDING_DIM_SQL} AS embedding_dim${q.withEmbedding ? ", embedding_blob, embedding_json" : ""}${attrCols} FROM nodes WHERE ${where} ORDER BY ${order}${limit}`;
|
|
16066
|
+
const sql = `SELECT id, label, description, created_at, last_updated_at, page_rank, cumulative_hits, extraction_confidence, extraction_source, community, created_at_seq, last_reinforced_at_seq, valid_to, logical_id, version, ${EMBEDDING_DIM_SQL} AS embedding_dim${q.withEmbedding ? ", embedding_blob, embedding_json" : ""}${attrCols} FROM nodes${q.indexedBy ? ` INDEXED BY ${selectIndex(q.indexedBy)}` : ""} WHERE ${where} ORDER BY ${order}${limit}`;
|
|
16050
16067
|
return { sql, params: [...params, ...orderParams] };
|
|
16051
16068
|
}
|
|
16052
16069
|
function decodeAttr(value, type) {
|
|
@@ -16244,6 +16261,19 @@ var TEMPORAL_INDEX_SQL = `
|
|
|
16244
16261
|
CREATE INDEX IF NOT EXISTS nodes_live ON nodes(label, valid_to);
|
|
16245
16262
|
CREATE INDEX IF NOT EXISTS nodes_live_count ON nodes(valid_to);
|
|
16246
16263
|
CREATE INDEX IF NOT EXISTS nodes_live_recent ON nodes(label, last_updated_at DESC) WHERE valid_to IS NULL;
|
|
16264
|
+
-- "Anything created since the watermark?" guards (the fold, regression and
|
|
16265
|
+
-- canonical-bind sweeps run one per session boundary). Without it the planner
|
|
16266
|
+
-- walks nodes_live and reads every live row of the label to test created_at:
|
|
16267
|
+
-- 56 MB per guard on a 1.1 GB store, to learn there was nothing to do. valid_to
|
|
16268
|
+
-- is a key column so the planner prefers this over nodes_live (one more term).
|
|
16269
|
+
CREATE INDEX IF NOT EXISTS nodes_live_created ON nodes(label, valid_to, created_at) WHERE valid_to IS NULL;
|
|
16270
|
+
-- Community members (the skills pull reads them twice per session boundary):
|
|
16271
|
+
-- ~15% of semantic nodes carry a localCommunity, and without this every live
|
|
16272
|
+
-- semantic row was read to find them (168 MB \u2192 18 MB per read).
|
|
16273
|
+
CREATE INDEX IF NOT EXISTS nodes_live_local_community ON nodes(label, valid_to, json_extract(attrs_json, '$.localCommunity')) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.localCommunity') IS NOT NULL;
|
|
16274
|
+
-- Problems parked for the fold judge (read every settle boundary): ~1 in 5 open
|
|
16275
|
+
-- Problems; without it every live Problem was read to find them (56 MB).
|
|
16276
|
+
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;
|
|
16247
16277
|
CREATE INDEX IF NOT EXISTS nodes_session_evidence ON nodes(id) WHERE ${SESSION_EVIDENCE_PREDICATE};
|
|
16248
16278
|
CREATE INDEX IF NOT EXISTS edges_session_evidence ON edges(id) WHERE ${SESSION_EVIDENCE_PREDICATE};
|
|
16249
16279
|
-- Covering + partial index for labelFingerprint over the instance labels: the
|
|
@@ -16258,6 +16288,10 @@ CREATE INDEX IF NOT EXISTS nodes_instance_fingerprint_v2 ON nodes(label, last_up
|
|
|
16258
16288
|
CREATE INDEX IF NOT EXISTS nodes_live_page_rank ON nodes(valid_to, page_rank);
|
|
16259
16289
|
CREATE INDEX IF NOT EXISTS nodes_logical ON nodes(logical_id, version);
|
|
16260
16290
|
CREATE INDEX IF NOT EXISTS edges_live ON edges(valid_to);
|
|
16291
|
+
-- Newest live edges first, so a "recent N matching edges" read stops early instead
|
|
16292
|
+
-- of matching and sorting every live edge (the hourly liveness check: 163 MB \u2192
|
|
16293
|
+
-- 69 MB). Named by recentEdgeAttrCoverage; the planner keeps edges_live otherwise.
|
|
16294
|
+
CREATE INDEX IF NOT EXISTS edges_live_created ON edges(created_at) WHERE valid_to IS NULL;
|
|
16261
16295
|
-- Expression index on the per-node relPath (stored in attrs_json) so the
|
|
16262
16296
|
-- incremental reindex can fetch just the changed files' symbols instead of
|
|
16263
16297
|
-- scanning every versioned node. Partial: only live rows are ever queried.
|
|
@@ -16954,6 +16988,27 @@ var SqliteGraphStore = class {
|
|
|
16954
16988
|
cloud: r.cloud === 1
|
|
16955
16989
|
}));
|
|
16956
16990
|
}
|
|
16991
|
+
findNodeStampsByLabelAttrs(label, attrKeys) {
|
|
16992
|
+
for (const k of attrKeys) if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(k)) throw new Error(`findNodeStampsByLabelAttrs: bad attr key ${k}`);
|
|
16993
|
+
const keys = [...new Set(attrKeys)];
|
|
16994
|
+
const rows = this.db.prepare(
|
|
16995
|
+
`SELECT id, created_at, created_at_seq,
|
|
16996
|
+
${keys.map((k, i) => `json_type(attrs_json, '$.${k}') IS NOT NULL AS h${i}`).join(", ")}${keys.length ? "," : ""}
|
|
16997
|
+
COALESCE(json_extract(attrs_json, '$.source') = 'cloud', 0) AS cloud
|
|
16998
|
+
FROM nodes WHERE label = ? AND valid_to IS NULL`
|
|
16999
|
+
).all(label);
|
|
17000
|
+
const out = new Map(keys.map((k) => [k, []]));
|
|
17001
|
+
for (const r of rows) {
|
|
17002
|
+
keys.forEach((k, i) => out.get(k).push({
|
|
17003
|
+
id: r.id,
|
|
17004
|
+
createdAt: r.created_at,
|
|
17005
|
+
...r.created_at_seq != null ? { createdAtSeq: r.created_at_seq } : {},
|
|
17006
|
+
has: r[`h${i}`] === 1,
|
|
17007
|
+
cloud: r.cloud === 1
|
|
17008
|
+
}));
|
|
17009
|
+
}
|
|
17010
|
+
return out;
|
|
17011
|
+
}
|
|
16957
17012
|
findNodeAttrsByLabel(label) {
|
|
16958
17013
|
const rows = this.stmts.attrsByLabel.all(label);
|
|
16959
17014
|
return rows.map((r) => ({
|
|
@@ -17195,6 +17250,9 @@ var SqliteGraphStore = class {
|
|
|
17195
17250
|
lineageChangedFiles(sinceSeq) {
|
|
17196
17251
|
return readLineageChangedFiles(this.db, sinceSeq);
|
|
17197
17252
|
}
|
|
17253
|
+
changedNodeIds(sinceSeq, labels) {
|
|
17254
|
+
return readChangedNodeIds(this.db, sinceSeq, labels);
|
|
17255
|
+
}
|
|
17198
17256
|
pruneChangeLog() {
|
|
17199
17257
|
return pruneChangeLog(this.db);
|
|
17200
17258
|
}
|
|
@@ -17389,7 +17447,7 @@ var SqliteGraphStore = class {
|
|
|
17389
17447
|
const r = this.db.prepare(
|
|
17390
17448
|
`SELECT COUNT(*) AS total,
|
|
17391
17449
|
COALESCE(SUM(CASE WHEN json_extract(attrs_json, '$.' || ?) = 1 THEN 1 ELSE 0 END), 0) AS count
|
|
17392
|
-
FROM (SELECT attrs_json FROM edges
|
|
17450
|
+
FROM (SELECT attrs_json FROM edges INDEXED BY edges_live_created
|
|
17393
17451
|
WHERE valid_to IS NULL AND attrs_json LIKE ?
|
|
17394
17452
|
ORDER BY created_at DESC LIMIT ?)`
|
|
17395
17453
|
).get(attr, `%${marker}%`, limit);
|
|
@@ -17537,8 +17595,9 @@ var SqliteGraphStore = class {
|
|
|
17537
17595
|
* so it must be a plain identifier. */
|
|
17538
17596
|
nodesAsOfWhereTrue(t, attr) {
|
|
17539
17597
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(attr)) throw new Error(`nodesAsOfWhereTrue: bad attr ${JSON.stringify(attr)}`);
|
|
17598
|
+
const indexed = attr === "revisit" ? " INDEXED BY nodes_revisit_true" : "";
|
|
17540
17599
|
const rows = this.db.prepare(
|
|
17541
|
-
`SELECT * FROM nodes WHERE valid_from <= :t AND (valid_to IS NULL OR valid_to > :t)
|
|
17600
|
+
`SELECT * FROM nodes${indexed} WHERE valid_from <= :t AND (valid_to IS NULL OR valid_to > :t)
|
|
17542
17601
|
AND json_type(attrs_json, '$.${attr}') = 'true'
|
|
17543
17602
|
ORDER BY rowid`
|
|
17544
17603
|
).all({ t });
|
package/package.json
CHANGED
package/pass-worker.mjs
CHANGED
|
@@ -26185,6 +26185,12 @@ function changeLogCovers(db, sinceSeq) {
|
|
|
26185
26185
|
const prunedTo = Number(db.prepare("SELECT value FROM store_meta WHERE key = ?").get(PRUNED_TO_KEY)?.["value"] ?? NaN);
|
|
26186
26186
|
return Number.isFinite(prunedTo) && sinceSeq >= prunedTo;
|
|
26187
26187
|
}
|
|
26188
|
+
function readChangedNodeIds(db, sinceSeq, labels) {
|
|
26189
|
+
if (!changeLogCovers(db, sinceSeq)) return null;
|
|
26190
|
+
if (!labels.length) return [];
|
|
26191
|
+
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);
|
|
26192
|
+
return rows.map((r) => r.id);
|
|
26193
|
+
}
|
|
26188
26194
|
function readLineageChangedFiles(db, sinceSeq) {
|
|
26189
26195
|
if (!changeLogCovers(db, sinceSeq)) return null;
|
|
26190
26196
|
const rows = db.prepare(LINEAGE_CHANGED_FILES_SQL).all({ s: sinceSeq });
|
|
@@ -26828,6 +26834,7 @@ function pagerankLocal(input) {
|
|
|
26828
26834
|
}
|
|
26829
26835
|
|
|
26830
26836
|
// ../../packages/local-graph/src/node-select.ts
|
|
26837
|
+
var SELECT_INDEXES = ["nodes_live_local_community", "nodes_live_fold_candidate"];
|
|
26831
26838
|
var EMBEDDING_DIM_SQL = "(CASE WHEN length(embedding_blob) >= 4 THEN length(embedding_blob) / 4 ELSE COALESCE(json_array_length(embedding_json), 0) END)";
|
|
26832
26839
|
var KEY = /^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$/;
|
|
26833
26840
|
var COLUMNS = /* @__PURE__ */ new Set([
|
|
@@ -26843,6 +26850,10 @@ function path(key) {
|
|
|
26843
26850
|
if (!KEY.test(key)) throw new Error(`selectNodes: bad attr key ${key}`);
|
|
26844
26851
|
return `'$.${key}'`;
|
|
26845
26852
|
}
|
|
26853
|
+
function selectIndex(i2) {
|
|
26854
|
+
if (!SELECT_INDEXES.includes(i2)) throw new Error(`selectNodes: bad index ${i2}`);
|
|
26855
|
+
return i2;
|
|
26856
|
+
}
|
|
26846
26857
|
function column(c) {
|
|
26847
26858
|
if (!COLUMNS.has(c)) throw new Error(`selectNodes: bad column ${c}`);
|
|
26848
26859
|
return c;
|
|
@@ -26858,6 +26869,12 @@ function compileNodeWhere(q) {
|
|
|
26858
26869
|
} else if ("idPrefix" in w) {
|
|
26859
26870
|
conds.push("substr(id, 1, ?) = ?");
|
|
26860
26871
|
params.push(w.idPrefix.length, w.idPrefix);
|
|
26872
|
+
} else if ("idIn" in w) {
|
|
26873
|
+
if (w.idIn.length === 0) conds.push("0");
|
|
26874
|
+
else {
|
|
26875
|
+
conds.push(`id IN (${w.idIn.map(() => "?").join(", ")})`);
|
|
26876
|
+
params.push(...w.idIn);
|
|
26877
|
+
}
|
|
26861
26878
|
} else if ("descriptionIn" in w) {
|
|
26862
26879
|
if (w.descriptionIn.length === 0) conds.push("0");
|
|
26863
26880
|
else {
|
|
@@ -26914,7 +26931,7 @@ function compileNodeSelect(q) {
|
|
|
26914
26931
|
}
|
|
26915
26932
|
const orderParams = !q.orderBy && q.labels.length > 1 ? [...q.labels] : [];
|
|
26916
26933
|
const limit = q.limit !== void 0 ? ` LIMIT ${Math.max(0, Math.floor(q.limit))}` : "";
|
|
26917
|
-
const sql = `SELECT id, label, description, created_at, last_updated_at, page_rank, cumulative_hits, extraction_confidence, extraction_source, community, created_at_seq, last_reinforced_at_seq, valid_to, logical_id, version, ${EMBEDDING_DIM_SQL} AS embedding_dim${q.withEmbedding ? ", embedding_blob, embedding_json" : ""}${attrCols} FROM nodes WHERE ${where} ORDER BY ${order}${limit}`;
|
|
26934
|
+
const sql = `SELECT id, label, description, created_at, last_updated_at, page_rank, cumulative_hits, extraction_confidence, extraction_source, community, created_at_seq, last_reinforced_at_seq, valid_to, logical_id, version, ${EMBEDDING_DIM_SQL} AS embedding_dim${q.withEmbedding ? ", embedding_blob, embedding_json" : ""}${attrCols} FROM nodes${q.indexedBy ? ` INDEXED BY ${selectIndex(q.indexedBy)}` : ""} WHERE ${where} ORDER BY ${order}${limit}`;
|
|
26918
26935
|
return { sql, params: [...params, ...orderParams] };
|
|
26919
26936
|
}
|
|
26920
26937
|
function decodeAttr(value, type) {
|
|
@@ -27112,6 +27129,19 @@ var TEMPORAL_INDEX_SQL = `
|
|
|
27112
27129
|
CREATE INDEX IF NOT EXISTS nodes_live ON nodes(label, valid_to);
|
|
27113
27130
|
CREATE INDEX IF NOT EXISTS nodes_live_count ON nodes(valid_to);
|
|
27114
27131
|
CREATE INDEX IF NOT EXISTS nodes_live_recent ON nodes(label, last_updated_at DESC) WHERE valid_to IS NULL;
|
|
27132
|
+
-- "Anything created since the watermark?" guards (the fold, regression and
|
|
27133
|
+
-- canonical-bind sweeps run one per session boundary). Without it the planner
|
|
27134
|
+
-- walks nodes_live and reads every live row of the label to test created_at:
|
|
27135
|
+
-- 56 MB per guard on a 1.1 GB store, to learn there was nothing to do. valid_to
|
|
27136
|
+
-- is a key column so the planner prefers this over nodes_live (one more term).
|
|
27137
|
+
CREATE INDEX IF NOT EXISTS nodes_live_created ON nodes(label, valid_to, created_at) WHERE valid_to IS NULL;
|
|
27138
|
+
-- Community members (the skills pull reads them twice per session boundary):
|
|
27139
|
+
-- ~15% of semantic nodes carry a localCommunity, and without this every live
|
|
27140
|
+
-- semantic row was read to find them (168 MB \u2192 18 MB per read).
|
|
27141
|
+
CREATE INDEX IF NOT EXISTS nodes_live_local_community ON nodes(label, valid_to, json_extract(attrs_json, '$.localCommunity')) WHERE valid_to IS NULL AND json_extract(attrs_json, '$.localCommunity') IS NOT NULL;
|
|
27142
|
+
-- Problems parked for the fold judge (read every settle boundary): ~1 in 5 open
|
|
27143
|
+
-- Problems; without it every live Problem was read to find them (56 MB).
|
|
27144
|
+
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;
|
|
27115
27145
|
CREATE INDEX IF NOT EXISTS nodes_session_evidence ON nodes(id) WHERE ${SESSION_EVIDENCE_PREDICATE};
|
|
27116
27146
|
CREATE INDEX IF NOT EXISTS edges_session_evidence ON edges(id) WHERE ${SESSION_EVIDENCE_PREDICATE};
|
|
27117
27147
|
-- Covering + partial index for labelFingerprint over the instance labels: the
|
|
@@ -27126,6 +27156,10 @@ CREATE INDEX IF NOT EXISTS nodes_instance_fingerprint_v2 ON nodes(label, last_up
|
|
|
27126
27156
|
CREATE INDEX IF NOT EXISTS nodes_live_page_rank ON nodes(valid_to, page_rank);
|
|
27127
27157
|
CREATE INDEX IF NOT EXISTS nodes_logical ON nodes(logical_id, version);
|
|
27128
27158
|
CREATE INDEX IF NOT EXISTS edges_live ON edges(valid_to);
|
|
27159
|
+
-- Newest live edges first, so a "recent N matching edges" read stops early instead
|
|
27160
|
+
-- of matching and sorting every live edge (the hourly liveness check: 163 MB \u2192
|
|
27161
|
+
-- 69 MB). Named by recentEdgeAttrCoverage; the planner keeps edges_live otherwise.
|
|
27162
|
+
CREATE INDEX IF NOT EXISTS edges_live_created ON edges(created_at) WHERE valid_to IS NULL;
|
|
27129
27163
|
-- Expression index on the per-node relPath (stored in attrs_json) so the
|
|
27130
27164
|
-- incremental reindex can fetch just the changed files' symbols instead of
|
|
27131
27165
|
-- scanning every versioned node. Partial: only live rows are ever queried.
|
|
@@ -27822,6 +27856,27 @@ var SqliteGraphStore = class {
|
|
|
27822
27856
|
cloud: r.cloud === 1
|
|
27823
27857
|
}));
|
|
27824
27858
|
}
|
|
27859
|
+
findNodeStampsByLabelAttrs(label, attrKeys) {
|
|
27860
|
+
for (const k of attrKeys) if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(k)) throw new Error(`findNodeStampsByLabelAttrs: bad attr key ${k}`);
|
|
27861
|
+
const keys = [...new Set(attrKeys)];
|
|
27862
|
+
const rows = this.db.prepare(
|
|
27863
|
+
`SELECT id, created_at, created_at_seq,
|
|
27864
|
+
${keys.map((k, i2) => `json_type(attrs_json, '$.${k}') IS NOT NULL AS h${i2}`).join(", ")}${keys.length ? "," : ""}
|
|
27865
|
+
COALESCE(json_extract(attrs_json, '$.source') = 'cloud', 0) AS cloud
|
|
27866
|
+
FROM nodes WHERE label = ? AND valid_to IS NULL`
|
|
27867
|
+
).all(label);
|
|
27868
|
+
const out2 = new Map(keys.map((k) => [k, []]));
|
|
27869
|
+
for (const r of rows) {
|
|
27870
|
+
keys.forEach((k, i2) => out2.get(k).push({
|
|
27871
|
+
id: r.id,
|
|
27872
|
+
createdAt: r.created_at,
|
|
27873
|
+
...r.created_at_seq != null ? { createdAtSeq: r.created_at_seq } : {},
|
|
27874
|
+
has: r[`h${i2}`] === 1,
|
|
27875
|
+
cloud: r.cloud === 1
|
|
27876
|
+
}));
|
|
27877
|
+
}
|
|
27878
|
+
return out2;
|
|
27879
|
+
}
|
|
27825
27880
|
findNodeAttrsByLabel(label) {
|
|
27826
27881
|
const rows = this.stmts.attrsByLabel.all(label);
|
|
27827
27882
|
return rows.map((r) => ({
|
|
@@ -28063,6 +28118,9 @@ var SqliteGraphStore = class {
|
|
|
28063
28118
|
lineageChangedFiles(sinceSeq) {
|
|
28064
28119
|
return readLineageChangedFiles(this.db, sinceSeq);
|
|
28065
28120
|
}
|
|
28121
|
+
changedNodeIds(sinceSeq, labels) {
|
|
28122
|
+
return readChangedNodeIds(this.db, sinceSeq, labels);
|
|
28123
|
+
}
|
|
28066
28124
|
pruneChangeLog() {
|
|
28067
28125
|
return pruneChangeLog(this.db);
|
|
28068
28126
|
}
|
|
@@ -28257,7 +28315,7 @@ var SqliteGraphStore = class {
|
|
|
28257
28315
|
const r = this.db.prepare(
|
|
28258
28316
|
`SELECT COUNT(*) AS total,
|
|
28259
28317
|
COALESCE(SUM(CASE WHEN json_extract(attrs_json, '$.' || ?) = 1 THEN 1 ELSE 0 END), 0) AS count
|
|
28260
|
-
FROM (SELECT attrs_json FROM edges
|
|
28318
|
+
FROM (SELECT attrs_json FROM edges INDEXED BY edges_live_created
|
|
28261
28319
|
WHERE valid_to IS NULL AND attrs_json LIKE ?
|
|
28262
28320
|
ORDER BY created_at DESC LIMIT ?)`
|
|
28263
28321
|
).get(attr, `%${marker}%`, limit);
|
|
@@ -28405,8 +28463,9 @@ var SqliteGraphStore = class {
|
|
|
28405
28463
|
* so it must be a plain identifier. */
|
|
28406
28464
|
nodesAsOfWhereTrue(t, attr) {
|
|
28407
28465
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(attr)) throw new Error(`nodesAsOfWhereTrue: bad attr ${JSON.stringify(attr)}`);
|
|
28466
|
+
const indexed = attr === "revisit" ? " INDEXED BY nodes_revisit_true" : "";
|
|
28408
28467
|
const rows = this.db.prepare(
|
|
28409
|
-
`SELECT * FROM nodes WHERE valid_from <= :t AND (valid_to IS NULL OR valid_to > :t)
|
|
28468
|
+
`SELECT * FROM nodes${indexed} WHERE valid_from <= :t AND (valid_to IS NULL OR valid_to > :t)
|
|
28410
28469
|
AND json_type(attrs_json, '$.${attr}') = 'true'
|
|
28411
28470
|
ORDER BY rowid`
|
|
28412
28471
|
).all({ t });
|
|
@@ -28492,19 +28551,16 @@ function conditionOf(node) {
|
|
|
28492
28551
|
}
|
|
28493
28552
|
function clearAnsweredRevisits(store2, ts) {
|
|
28494
28553
|
const report = { cleared: 0, standing: 0 };
|
|
28495
|
-
|
|
28496
|
-
|
|
28497
|
-
|
|
28498
|
-
|
|
28499
|
-
|
|
28500
|
-
|
|
28501
|
-
|
|
28502
|
-
report.standing++;
|
|
28503
|
-
continue;
|
|
28504
|
-
}
|
|
28505
|
-
clearRevisit(store2, node.id, ts);
|
|
28506
|
-
report.cleared++;
|
|
28554
|
+
const semantic = new Set(SEMANTIC_NODE_LABELS);
|
|
28555
|
+
for (const node of store2.nodesAsOfWhereTrue(ts, "revisit")) {
|
|
28556
|
+
if (!semantic.has(node.label) || node.validTo != null) continue;
|
|
28557
|
+
const condition = conditionOf(node);
|
|
28558
|
+
if (condition === void 0 || !isRevisitAnswered(store2, node, condition)) {
|
|
28559
|
+
report.standing++;
|
|
28560
|
+
continue;
|
|
28507
28561
|
}
|
|
28562
|
+
clearRevisit(store2, node.id, ts);
|
|
28563
|
+
report.cleared++;
|
|
28508
28564
|
}
|
|
28509
28565
|
return report;
|
|
28510
28566
|
}
|
|
@@ -30107,7 +30163,7 @@ var MECHANISMS = [
|
|
|
30107
30163
|
var COLUMN_INPUTS = {
|
|
30108
30164
|
__createdAtSeq: (n) => n.createdAtSeq !== void 0 && n.createdAtSeq !== null
|
|
30109
30165
|
};
|
|
30110
|
-
function evaluateFed(store2, d) {
|
|
30166
|
+
function evaluateFed(store2, d, stamps) {
|
|
30111
30167
|
if (d.fedIncoming) {
|
|
30112
30168
|
const { count: count2, total } = store2.incomingEdgeCoverage?.(
|
|
30113
30169
|
d.fedIncoming.label,
|
|
@@ -30127,7 +30183,7 @@ function evaluateFed(store2, d) {
|
|
|
30127
30183
|
if (!d.fed) return { count: 0, total: 0, fraction: 0 };
|
|
30128
30184
|
const column2 = COLUMN_INPUTS[d.fed.attr];
|
|
30129
30185
|
const fed = d.fed;
|
|
30130
|
-
const all = fed.labels.flatMap((label) => store2.findNodeStampsByLabel ? store2.findNodeStampsByLabel(label, fed.attr) : store2.findNodeAttrsByLabel(label));
|
|
30186
|
+
const all = fed.labels.flatMap((label) => stamps?.(label, fed.attr) ?? (store2.findNodeStampsByLabel ? store2.findNodeStampsByLabel(label, fed.attr) : store2.findNodeAttrsByLabel(label)));
|
|
30131
30187
|
const isCloudMirror = (n) => n.cloud ?? n.attrs?.["source"] === "cloud";
|
|
30132
30188
|
const nodes = fed.localOnly ? all.filter((n) => !isCloudMirror(n)) : all;
|
|
30133
30189
|
const has = (n) => column2 ? column2(n) : n.has ?? n.attrs?.[fed.attr] !== void 0;
|
|
@@ -30150,8 +30206,15 @@ function evaluateMechanisms(store2, invocations = /* @__PURE__ */ new Map(), mec
|
|
|
30150
30206
|
if (!counts.has(label)) counts.set(label, store2.countNodesByLabel(label));
|
|
30151
30207
|
return counts.get(label);
|
|
30152
30208
|
};
|
|
30209
|
+
const wanted = /* @__PURE__ */ new Map();
|
|
30210
|
+
for (const d of mechanisms) for (const l of d.fed?.labels ?? []) wanted.set(l, (wanted.get(l) ?? /* @__PURE__ */ new Set()).add(d.fed.attr));
|
|
30211
|
+
const byLabel = /* @__PURE__ */ new Map();
|
|
30212
|
+
const stamps = store2.findNodeStampsByLabelAttrs ? (label, attr) => {
|
|
30213
|
+
if (!byLabel.has(label)) byLabel.set(label, store2.findNodeStampsByLabelAttrs(label, [...wanted.get(label) ?? [attr]]));
|
|
30214
|
+
return byLabel.get(label).get(attr);
|
|
30215
|
+
} : void 0;
|
|
30153
30216
|
return mechanisms.map((d) => {
|
|
30154
|
-
const fed = evaluateFed(store2, d);
|
|
30217
|
+
const fed = evaluateFed(store2, d, stamps);
|
|
30155
30218
|
const inv = invocations.get(d.pass);
|
|
30156
30219
|
const effectCount = d.effectIsLevel ? Math.max(
|
|
30157
30220
|
0,
|
|
@@ -31204,6 +31267,8 @@ async function reconcileStaleFiles(store2, rootPath, workspaceId) {
|
|
|
31204
31267
|
}
|
|
31205
31268
|
|
|
31206
31269
|
// src/workspace-maintenance.ts
|
|
31270
|
+
var WM_CALIBRATION_AT_KEY = "wm-calibration-at";
|
|
31271
|
+
var WM_CALIBRATION_INTERVAL_MS = 30 * 6e4;
|
|
31207
31272
|
function runWorkspaceMaintenance(store2, workspaceId) {
|
|
31208
31273
|
const seqBefore = store2.currentIngestSeq();
|
|
31209
31274
|
const report = runSemanticMaintenance(store2);
|
|
@@ -31214,9 +31279,13 @@ function runWorkspaceMaintenance(store2, workspaceId) {
|
|
|
31214
31279
|
}
|
|
31215
31280
|
let calibration = null;
|
|
31216
31281
|
const calibrationStarted = Date.now();
|
|
31217
|
-
|
|
31218
|
-
|
|
31219
|
-
|
|
31282
|
+
const lastCalibration = Number(store2.getMeta?.(WM_CALIBRATION_AT_KEY) ?? 0);
|
|
31283
|
+
if (!(calibrationStarted - lastCalibration < WM_CALIBRATION_INTERVAL_MS)) {
|
|
31284
|
+
try {
|
|
31285
|
+
calibration = wmCalibrationCells(store2);
|
|
31286
|
+
store2.setMeta?.(WM_CALIBRATION_AT_KEY, String(calibrationStarted));
|
|
31287
|
+
} catch {
|
|
31288
|
+
}
|
|
31220
31289
|
}
|
|
31221
31290
|
return {
|
|
31222
31291
|
report,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
2f2ed72ed3ac78912e2f6d5afbfd43bb55b67b91e397c9c559d045d0b93f1bde .codex-plugin/plugin.json
|
|
2
2
|
a7bebae9fb01c838d6c75fc784129e28be571233fa66ff5ee8039b574f35ccf7 assets/icon.png
|
|
3
3
|
fc38b64e4df1e0b72c35764bef6c4c05430d97c9009ad0ecc8a9e75b4deab232 configure-local.mjs
|
|
4
4
|
73dc6028b3b070257c7c31f252d11e55a9e096f4aed0f187f10cb21716e1e6d7 dist/client.js
|