@inerrata-corporation/errata 2.0.6-dev.2028 → 2.0.6-dev.2047
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 +13 -0
- package/errata.mjs +320 -52
- package/package.json +1 -1
- package/pass-worker.mjs +13 -0
package/consolidate-worker.mjs
CHANGED
|
@@ -16208,6 +16208,11 @@ var SqliteGraphStore = class {
|
|
|
16208
16208
|
countByLabel: this.db.prepare(
|
|
16209
16209
|
"SELECT COUNT(*) AS n FROM nodes WHERE label = ? AND valid_to IS NULL"
|
|
16210
16210
|
),
|
|
16211
|
+
// Identity twin of findByLabel — same live-row semantics, two attrs read in
|
|
16212
|
+
// SQL, no attrs JSON.parse or embedding decode (PJ-anchor-reach).
|
|
16213
|
+
identitiesByLabel: this.db.prepare(
|
|
16214
|
+
"SELECT id, json_extract(attrs_json, '$.relPath') AS relPath, json_extract(attrs_json, '$.qname') AS qname FROM nodes WHERE label = ? AND valid_to IS NULL"
|
|
16215
|
+
),
|
|
16211
16216
|
// ALL versions of a label — incl frozen/closed (valid_to set). Used by the
|
|
16212
16217
|
// clean-reindex purge so a true wipe removes history too, not just live rows.
|
|
16213
16218
|
findByLabelAll: this.db.prepare("SELECT * FROM nodes WHERE label = ?"),
|
|
@@ -16508,6 +16513,14 @@ var SqliteGraphStore = class {
|
|
|
16508
16513
|
countNodesByLabel(label) {
|
|
16509
16514
|
return Number(this.stmts.countByLabel.get(label).n);
|
|
16510
16515
|
}
|
|
16516
|
+
findNodeIdentitiesByLabel(label) {
|
|
16517
|
+
const rows = this.stmts.identitiesByLabel.all(label);
|
|
16518
|
+
return rows.map((r) => ({
|
|
16519
|
+
id: r.id,
|
|
16520
|
+
relPath: typeof r.relPath === "string" ? r.relPath : null,
|
|
16521
|
+
qname: typeof r.qname === "string" ? r.qname : null
|
|
16522
|
+
}));
|
|
16523
|
+
}
|
|
16511
16524
|
findAllVersionsByLabel(label) {
|
|
16512
16525
|
const rows = this.stmts.findByLabelAll.all(label);
|
|
16513
16526
|
return rows.map(rowToNode);
|
package/errata.mjs
CHANGED
|
@@ -15962,6 +15962,9 @@ function projectSymbolId(projectSalt, projectId, relPath, qname, kind) {
|
|
|
15962
15962
|
const path2 = normalizeSymbolPath(relPath);
|
|
15963
15963
|
return `sym_${createHmac("sha256", projectSalt).update(`${projectId}:${path2}:${qname}:${kind}`).digest("hex").slice(0, 24)}`;
|
|
15964
15964
|
}
|
|
15965
|
+
function projectSaltedNodeId(canonicalId, salt) {
|
|
15966
|
+
return `projn_${createHmac("sha256", salt).update(canonicalId).digest("hex").slice(0, 24)}`;
|
|
15967
|
+
}
|
|
15965
15968
|
function isMembraneSaltedId(id) {
|
|
15966
15969
|
return typeof id === "string" && /^(orgn_|teamn_|projn_)/.test(id);
|
|
15967
15970
|
}
|
|
@@ -16460,6 +16463,7 @@ __export(src_exports, {
|
|
|
16460
16463
|
parsePnpmLock: () => parsePnpmLock,
|
|
16461
16464
|
prefilterEntities: () => prefilterEntities,
|
|
16462
16465
|
problemIdFromError: () => problemIdFromError,
|
|
16466
|
+
projectSaltedNodeId: () => projectSaltedNodeId,
|
|
16463
16467
|
projectSymbolId: () => projectSymbolId,
|
|
16464
16468
|
provenanceBand: () => provenanceBand,
|
|
16465
16469
|
resolveCanonical: () => resolveCanonical,
|
|
@@ -17255,6 +17259,11 @@ CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.re
|
|
|
17255
17259
|
countByLabel: this.db.prepare(
|
|
17256
17260
|
"SELECT COUNT(*) AS n FROM nodes WHERE label = ? AND valid_to IS NULL"
|
|
17257
17261
|
),
|
|
17262
|
+
// Identity twin of findByLabel — same live-row semantics, two attrs read in
|
|
17263
|
+
// SQL, no attrs JSON.parse or embedding decode (PJ-anchor-reach).
|
|
17264
|
+
identitiesByLabel: this.db.prepare(
|
|
17265
|
+
"SELECT id, json_extract(attrs_json, '$.relPath') AS relPath, json_extract(attrs_json, '$.qname') AS qname FROM nodes WHERE label = ? AND valid_to IS NULL"
|
|
17266
|
+
),
|
|
17258
17267
|
// ALL versions of a label — incl frozen/closed (valid_to set). Used by the
|
|
17259
17268
|
// clean-reindex purge so a true wipe removes history too, not just live rows.
|
|
17260
17269
|
findByLabelAll: this.db.prepare("SELECT * FROM nodes WHERE label = ?"),
|
|
@@ -17555,6 +17564,14 @@ CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.re
|
|
|
17555
17564
|
countNodesByLabel(label) {
|
|
17556
17565
|
return Number(this.stmts.countByLabel.get(label).n);
|
|
17557
17566
|
}
|
|
17567
|
+
findNodeIdentitiesByLabel(label) {
|
|
17568
|
+
const rows = this.stmts.identitiesByLabel.all(label);
|
|
17569
|
+
return rows.map((r) => ({
|
|
17570
|
+
id: r.id,
|
|
17571
|
+
relPath: typeof r.relPath === "string" ? r.relPath : null,
|
|
17572
|
+
qname: typeof r.qname === "string" ? r.qname : null
|
|
17573
|
+
}));
|
|
17574
|
+
}
|
|
17558
17575
|
findAllVersionsByLabel(label) {
|
|
17559
17576
|
const rows = this.stmts.findByLabelAll.all(label);
|
|
17560
17577
|
return rows.map(rowToNode);
|
|
@@ -25213,6 +25230,28 @@ var init_client = __esm({
|
|
|
25213
25230
|
}));
|
|
25214
25231
|
return { nodes, edges, anchorEdges, symbolIds: res.symbolIds ?? [] };
|
|
25215
25232
|
}
|
|
25233
|
+
/**
|
|
25234
|
+
* PJ-anchor-reach: one cursor page of the project's symbol anchors, newest
|
|
25235
|
+
* stamp last. `since` is the cursor from the previous page (0 = from the
|
|
25236
|
+
* beginning); the returned `cursor` continues it. Rank-independent — the
|
|
25237
|
+
* daemon filters the page against its own checkout, and nothing about which
|
|
25238
|
+
* symbols it holds is sent. Same membership gate as priors.
|
|
25239
|
+
*/
|
|
25240
|
+
async getProjectAnchors(projectId, since, limit = 200) {
|
|
25241
|
+
const res = await this.json(
|
|
25242
|
+
"GET",
|
|
25243
|
+
`/v2/projects/${encodeURIComponent(projectId)}/anchors?since=${String(Math.max(0, Math.floor(since)))}&limit=${String(limit)}`
|
|
25244
|
+
);
|
|
25245
|
+
const { nodes } = this.toCastalia({ nodes: res.nodes ?? [], edges: [] });
|
|
25246
|
+
const anchors = (res.anchors ?? []).map((a) => ({
|
|
25247
|
+
from: a.fromCanonicalId,
|
|
25248
|
+
toSymbolId: a.toSymbolId,
|
|
25249
|
+
type: a.type,
|
|
25250
|
+
attrs: a.attrs ?? {},
|
|
25251
|
+
stampedAt: typeof a.stampedAt === "number" ? a.stampedAt : 0
|
|
25252
|
+
}));
|
|
25253
|
+
return { nodes, anchors, cursor: typeof res.cursor === "number" ? res.cursor : since };
|
|
25254
|
+
}
|
|
25216
25255
|
/** Pull cloud-induced skills + the session-bootstrap prime payload. `seed` is the
|
|
25217
25256
|
* canonical ids of the agent's recent problems — the cloud ranks the skills whose
|
|
25218
25257
|
* motifs those problems match first (the rest are baseline). `techSeed`
|
|
@@ -70009,13 +70048,13 @@ function esc3(s) {
|
|
|
70009
70048
|
function slug(name2) {
|
|
70010
70049
|
return name2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
|
|
70011
70050
|
}
|
|
70012
|
-
function
|
|
70051
|
+
function isLive3(verb) {
|
|
70013
70052
|
return LIVE_VERBS.has(verb);
|
|
70014
70053
|
}
|
|
70015
70054
|
function nextHtml(n, opts) {
|
|
70016
70055
|
if (!n) return "";
|
|
70017
70056
|
const cmd2 = `errata ${n.verb}${n.arg ? " " + n.arg : ""}`;
|
|
70018
|
-
const live2 =
|
|
70057
|
+
const live2 = isLive3(n.verb);
|
|
70019
70058
|
if (!live2 && !opts.includeFutureVerbs) return "";
|
|
70020
70059
|
const hint = n.hint ? ` \u2014 ${esc3(n.hint)}` : "";
|
|
70021
70060
|
const cls = live2 ? "next" : "next soon";
|
|
@@ -77008,7 +77047,7 @@ function createWatchBreaker(deps) {
|
|
|
77008
77047
|
}
|
|
77009
77048
|
|
|
77010
77049
|
// src/engine.ts
|
|
77011
|
-
var DAEMON_VERSION = true ? "2.0.6-dev.
|
|
77050
|
+
var DAEMON_VERSION = true ? "2.0.6-dev.2047" : "2.0.0-alpha.0";
|
|
77012
77051
|
var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
|
|
77013
77052
|
var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
|
|
77014
77053
|
var GIT_OP_MUTE_MS = 4e3;
|
|
@@ -80448,42 +80487,12 @@ async function ensureProjectLink(root, profile, client) {
|
|
|
80448
80487
|
}
|
|
80449
80488
|
|
|
80450
80489
|
// src/project-hydrate.ts
|
|
80451
|
-
|
|
80452
|
-
async function hydrateProject(opts) {
|
|
80453
|
-
const { root, profile, store, cloud } = opts;
|
|
80454
|
-
if (!profile.projectId) return { hydrated: false, reason: "no-project" };
|
|
80455
|
-
if (profile.projectHydratedAt) return { hydrated: false, reason: "already-hydrated" };
|
|
80456
|
-
try {
|
|
80457
|
-
const res = await cloud.getProjectPriors(profile.projectId, opts.limit ?? DEFAULT_LIMIT);
|
|
80458
|
-
let landedNodes = 0;
|
|
80459
|
-
for (const n of res.nodes) {
|
|
80460
|
-
if (store.getNode(n.id)) continue;
|
|
80461
|
-
store.mergeNode(n);
|
|
80462
|
-
landedNodes++;
|
|
80463
|
-
}
|
|
80464
|
-
let landedEdges = 0;
|
|
80465
|
-
for (const e of res.edges) {
|
|
80466
|
-
if (!store.getNode(e.from) || !store.getNode(e.to)) continue;
|
|
80467
|
-
if (store.getEdge(e.id)) continue;
|
|
80468
|
-
store.mergeEdge(e);
|
|
80469
|
-
landedEdges++;
|
|
80470
|
-
}
|
|
80471
|
-
profile.projectHydratedAt = Date.now();
|
|
80472
|
-
saveProfile(root, profile);
|
|
80473
|
-
return { hydrated: true, nodes: landedNodes, edges: landedEdges };
|
|
80474
|
-
} catch (err2) {
|
|
80475
|
-
return {
|
|
80476
|
-
hydrated: false,
|
|
80477
|
-
reason: "error",
|
|
80478
|
-
detail: err2 instanceof Error ? err2.message : String(err2)
|
|
80479
|
-
};
|
|
80480
|
-
}
|
|
80481
|
-
}
|
|
80490
|
+
init_src11();
|
|
80482
80491
|
|
|
80483
80492
|
// src/project-reanchor.ts
|
|
80484
80493
|
init_src();
|
|
80485
80494
|
init_src11();
|
|
80486
|
-
var
|
|
80495
|
+
var DEFAULT_LIMIT = 50;
|
|
80487
80496
|
var ANCHOR_SYMBOL_KINDS = [
|
|
80488
80497
|
"Function",
|
|
80489
80498
|
"Class",
|
|
@@ -80506,16 +80515,92 @@ function buildReverseSymbolMap(opts) {
|
|
|
80506
80515
|
const { store, salt, projectId } = opts;
|
|
80507
80516
|
const reverse = /* @__PURE__ */ new Map();
|
|
80508
80517
|
for (const kind of ANCHOR_SYMBOL_KINDS) {
|
|
80509
|
-
for (const n of store.
|
|
80510
|
-
const relPath = n
|
|
80511
|
-
|
|
80512
|
-
|
|
80513
|
-
const sym = projectSymbolId(salt, projectId, relPath, qname, n.label);
|
|
80518
|
+
for (const n of store.findNodeIdentitiesByLabel(kind)) {
|
|
80519
|
+
const { relPath, qname } = n;
|
|
80520
|
+
if (relPath === null || qname === null) continue;
|
|
80521
|
+
const sym = projectSymbolId(salt, projectId, relPath, qname, kind);
|
|
80514
80522
|
if (!reverse.has(sym)) reverse.set(sym, n.id);
|
|
80523
|
+
const twin = projectSaltedNodeId(sym, salt);
|
|
80524
|
+
if (!reverse.has(twin)) reverse.set(twin, n.id);
|
|
80515
80525
|
}
|
|
80516
80526
|
}
|
|
80517
80527
|
return reverse;
|
|
80518
80528
|
}
|
|
80529
|
+
function anchorConfidence(attrs) {
|
|
80530
|
+
const v = typeof attrs["extractionConfidence"] === "number" ? attrs["extractionConfidence"] : attrs["confidence"];
|
|
80531
|
+
return typeof v === "number" && Number.isFinite(v) ? Math.max(0, Math.min(1, v)) : null;
|
|
80532
|
+
}
|
|
80533
|
+
var ANCHOR_SOURCE_KINDS = [
|
|
80534
|
+
"Problem",
|
|
80535
|
+
"Solution",
|
|
80536
|
+
"RootCause",
|
|
80537
|
+
"Pattern",
|
|
80538
|
+
"Technique",
|
|
80539
|
+
"AntiPattern"
|
|
80540
|
+
];
|
|
80541
|
+
function buildOwnKnowledgeMap(opts) {
|
|
80542
|
+
const { store, salt } = opts;
|
|
80543
|
+
const own = /* @__PURE__ */ new Map();
|
|
80544
|
+
for (const kind of ANCHOR_SOURCE_KINDS) {
|
|
80545
|
+
for (const n of store.findNodeIdentitiesByLabel(kind)) {
|
|
80546
|
+
if (n.id.startsWith("projn_")) continue;
|
|
80547
|
+
own.set(projectSaltedNodeId(n.id, salt), n.id);
|
|
80548
|
+
}
|
|
80549
|
+
}
|
|
80550
|
+
return own;
|
|
80551
|
+
}
|
|
80552
|
+
function resolveAnchorSource(store, from, own) {
|
|
80553
|
+
if (store.getNode(from)) return from;
|
|
80554
|
+
return own().get(from) ?? null;
|
|
80555
|
+
}
|
|
80556
|
+
function existingAnchor(store, from, to) {
|
|
80557
|
+
const matches = store.outEdges(from, ["ANCHORED_AT"]).filter((e) => e.to === to);
|
|
80558
|
+
return matches.find((e) => e.attrs["source"] !== "cloud") ?? matches[0] ?? null;
|
|
80559
|
+
}
|
|
80560
|
+
var reverseCache = /* @__PURE__ */ new WeakMap();
|
|
80561
|
+
var ownCache = /* @__PURE__ */ new WeakMap();
|
|
80562
|
+
function cachedMap(cache2, store, key, build) {
|
|
80563
|
+
let refreshed = false;
|
|
80564
|
+
const put = () => {
|
|
80565
|
+
const map2 = build();
|
|
80566
|
+
cache2.set(store, { key, map: map2 });
|
|
80567
|
+
return map2;
|
|
80568
|
+
};
|
|
80569
|
+
return {
|
|
80570
|
+
get: () => {
|
|
80571
|
+
const hit = cache2.get(store);
|
|
80572
|
+
if (hit && hit.key === key) return hit.map;
|
|
80573
|
+
refreshed = true;
|
|
80574
|
+
return put();
|
|
80575
|
+
},
|
|
80576
|
+
refresh: () => {
|
|
80577
|
+
if (refreshed) return cache2.get(store)?.map ?? put();
|
|
80578
|
+
refreshed = true;
|
|
80579
|
+
return put();
|
|
80580
|
+
}
|
|
80581
|
+
};
|
|
80582
|
+
}
|
|
80583
|
+
function lookupLive(store, m, id) {
|
|
80584
|
+
const first = m.get().get(id);
|
|
80585
|
+
if (first && isLive2(store, first)) return first;
|
|
80586
|
+
const second = m.refresh().get(id);
|
|
80587
|
+
return second && isLive2(store, second) ? second : null;
|
|
80588
|
+
}
|
|
80589
|
+
function isLive2(store, id) {
|
|
80590
|
+
const n = store.getNode(id);
|
|
80591
|
+
return n !== null && (n.validTo === null || n.validTo === void 0);
|
|
80592
|
+
}
|
|
80593
|
+
function lazy2(make) {
|
|
80594
|
+
let value;
|
|
80595
|
+
let made = false;
|
|
80596
|
+
return () => {
|
|
80597
|
+
if (!made) {
|
|
80598
|
+
value = make();
|
|
80599
|
+
made = true;
|
|
80600
|
+
}
|
|
80601
|
+
return value;
|
|
80602
|
+
};
|
|
80603
|
+
}
|
|
80519
80604
|
async function reanchorProject(opts) {
|
|
80520
80605
|
const { root, profile, store, cloud, salt } = opts;
|
|
80521
80606
|
if (!profile.projectId) return { reanchored: false, reason: "no-project" };
|
|
@@ -80524,8 +80609,9 @@ async function reanchorProject(opts) {
|
|
|
80524
80609
|
const reverse = buildReverseSymbolMap({ store, salt, projectId: profile.projectId });
|
|
80525
80610
|
if (reverse.size === 0) return { reanchored: false, reason: "no-local-symbols" };
|
|
80526
80611
|
try {
|
|
80527
|
-
const res = await cloud.getProjectPriors(profile.projectId, opts.limit ??
|
|
80612
|
+
const res = await cloud.getProjectPriors(profile.projectId, opts.limit ?? DEFAULT_LIMIT);
|
|
80528
80613
|
const now = Date.now();
|
|
80614
|
+
const own = lazy2(() => buildOwnKnowledgeMap({ store, salt }));
|
|
80529
80615
|
let anchors = 0;
|
|
80530
80616
|
let dropped = 0;
|
|
80531
80617
|
for (const a of res.anchorEdges) {
|
|
@@ -80534,18 +80620,19 @@ async function reanchorProject(opts) {
|
|
|
80534
80620
|
dropped++;
|
|
80535
80621
|
continue;
|
|
80536
80622
|
}
|
|
80537
|
-
|
|
80623
|
+
const from = resolveAnchorSource(store, a.from, own);
|
|
80624
|
+
if (!from) {
|
|
80538
80625
|
dropped++;
|
|
80539
80626
|
continue;
|
|
80540
80627
|
}
|
|
80541
|
-
|
|
80542
|
-
|
|
80628
|
+
if (existingAnchor(store, from, localTo)) continue;
|
|
80629
|
+
const id = edgeId(from, "ANCHORED_AT", localTo);
|
|
80543
80630
|
const edge2 = {
|
|
80544
80631
|
id,
|
|
80545
|
-
from
|
|
80632
|
+
from,
|
|
80546
80633
|
to: localTo,
|
|
80547
80634
|
type: "ANCHORED_AT",
|
|
80548
|
-
confidence:
|
|
80635
|
+
confidence: anchorConfidence(a.attrs) ?? 0.5,
|
|
80549
80636
|
extractionSource: "agent-observed",
|
|
80550
80637
|
createdAt: now,
|
|
80551
80638
|
lastSeenAt: now,
|
|
@@ -80570,6 +80657,136 @@ async function reanchorProject(opts) {
|
|
|
80570
80657
|
};
|
|
80571
80658
|
}
|
|
80572
80659
|
}
|
|
80660
|
+
var SYNC_PAGE = 200;
|
|
80661
|
+
var SYNC_MAX_PAGES = 25;
|
|
80662
|
+
async function syncProjectAnchors(opts) {
|
|
80663
|
+
const { root, profile, store, cloud, salt } = opts;
|
|
80664
|
+
if (!profile.projectId) return { synced: false, reason: "no-project" };
|
|
80665
|
+
const projectId = profile.projectId;
|
|
80666
|
+
const key = `${projectId}|${salt}`;
|
|
80667
|
+
const reverse = cachedMap(reverseCache, store, key, () => buildReverseSymbolMap({ store, salt, projectId }));
|
|
80668
|
+
const own = cachedMap(ownCache, store, salt, () => buildOwnKnowledgeMap({ store, salt }));
|
|
80669
|
+
const limit = opts.limit ?? SYNC_PAGE;
|
|
80670
|
+
const maxPages = opts.maxPages ?? SYNC_MAX_PAGES;
|
|
80671
|
+
let anchors = 0;
|
|
80672
|
+
let updated = 0;
|
|
80673
|
+
let dropped = 0;
|
|
80674
|
+
let landed = 0;
|
|
80675
|
+
let cursor = profile.projectAnchorCursor ?? 0;
|
|
80676
|
+
try {
|
|
80677
|
+
for (let page = 0; page < maxPages; page++) {
|
|
80678
|
+
const res = await cloud.getProjectAnchors(projectId, cursor, limit);
|
|
80679
|
+
if (res.anchors.length === 0) break;
|
|
80680
|
+
if (reverse.get().size === 0 && reverse.refresh().size === 0) {
|
|
80681
|
+
return { synced: false, reason: "no-local-symbols" };
|
|
80682
|
+
}
|
|
80683
|
+
const byId = new Map(res.nodes.map((n) => [n.id, n]));
|
|
80684
|
+
const now = Date.now();
|
|
80685
|
+
for (const a of res.anchors) {
|
|
80686
|
+
if (a.type !== "ANCHORED_AT") continue;
|
|
80687
|
+
const localTo = lookupLive(store, reverse, a.toSymbolId);
|
|
80688
|
+
if (!localTo) {
|
|
80689
|
+
dropped++;
|
|
80690
|
+
continue;
|
|
80691
|
+
}
|
|
80692
|
+
let from = store.getNode(a.from) ? a.from : lookupLive(store, own, a.from);
|
|
80693
|
+
if (!from) {
|
|
80694
|
+
const node2 = byId.get(a.from);
|
|
80695
|
+
if (!node2) {
|
|
80696
|
+
dropped++;
|
|
80697
|
+
continue;
|
|
80698
|
+
}
|
|
80699
|
+
store.mergeNode(node2);
|
|
80700
|
+
landed++;
|
|
80701
|
+
from = node2.id;
|
|
80702
|
+
}
|
|
80703
|
+
const existing = existingAnchor(store, from, localTo);
|
|
80704
|
+
const id = existing?.id ?? edgeId(from, "ANCHORED_AT", localTo);
|
|
80705
|
+
if (existing) {
|
|
80706
|
+
if (existing.attrs["source"] !== "cloud") continue;
|
|
80707
|
+
if (existing.attrs["symbolContentHash"] === a.attrs["symbolContentHash"] && existing.attrs["observedAtCommit"] === a.attrs["observedAtCommit"]) {
|
|
80708
|
+
continue;
|
|
80709
|
+
}
|
|
80710
|
+
}
|
|
80711
|
+
const edge2 = {
|
|
80712
|
+
id,
|
|
80713
|
+
from,
|
|
80714
|
+
to: localTo,
|
|
80715
|
+
type: "ANCHORED_AT",
|
|
80716
|
+
confidence: anchorConfidence(a.attrs) ?? existing?.confidence ?? 0.5,
|
|
80717
|
+
extractionSource: "agent-observed",
|
|
80718
|
+
createdAt: existing?.createdAt ?? now,
|
|
80719
|
+
lastSeenAt: now,
|
|
80720
|
+
navSuccesses: existing?.navSuccesses ?? 0,
|
|
80721
|
+
navFailures: existing?.navFailures ?? 0,
|
|
80722
|
+
attrs: {
|
|
80723
|
+
...existing?.attrs ?? {},
|
|
80724
|
+
...a.attrs,
|
|
80725
|
+
source: "cloud",
|
|
80726
|
+
anchorState: classifyAnchorVersion(a.attrs, store.getNode(localTo))
|
|
80727
|
+
}
|
|
80728
|
+
};
|
|
80729
|
+
store.mergeEdge(edge2);
|
|
80730
|
+
if (existing) updated++;
|
|
80731
|
+
else anchors++;
|
|
80732
|
+
}
|
|
80733
|
+
const next = typeof res.cursor === "number" && res.cursor > cursor ? res.cursor : cursor;
|
|
80734
|
+
if (next !== cursor) {
|
|
80735
|
+
cursor = next;
|
|
80736
|
+
profile.projectAnchorCursor = cursor;
|
|
80737
|
+
saveProfile(root, profile);
|
|
80738
|
+
}
|
|
80739
|
+
if (res.anchors.length < limit) break;
|
|
80740
|
+
}
|
|
80741
|
+
return { synced: true, anchors, updated, dropped, landed, cursor };
|
|
80742
|
+
} catch (err2) {
|
|
80743
|
+
return { synced: false, reason: "error", detail: err2 instanceof Error ? err2.message : String(err2) };
|
|
80744
|
+
}
|
|
80745
|
+
}
|
|
80746
|
+
|
|
80747
|
+
// src/project-hydrate.ts
|
|
80748
|
+
var DEFAULT_LIMIT2 = 50;
|
|
80749
|
+
async function hydrateProject(opts) {
|
|
80750
|
+
const { root, profile, store, cloud, salt } = opts;
|
|
80751
|
+
if (!profile.projectId) return { hydrated: false, reason: "no-project" };
|
|
80752
|
+
if (profile.projectHydratedAt) return { hydrated: false, reason: "already-hydrated" };
|
|
80753
|
+
try {
|
|
80754
|
+
const res = await cloud.getProjectPriors(profile.projectId, opts.limit ?? DEFAULT_LIMIT2);
|
|
80755
|
+
const own = buildOwnKnowledgeMap({ store, salt });
|
|
80756
|
+
const localId = (id) => store.getNode(id) ? id : own.get(id) ?? null;
|
|
80757
|
+
let landedNodes = 0;
|
|
80758
|
+
let ownNodes = 0;
|
|
80759
|
+
for (const n of res.nodes) {
|
|
80760
|
+
if (store.getNode(n.id)) continue;
|
|
80761
|
+
if (own.has(n.id)) {
|
|
80762
|
+
ownNodes++;
|
|
80763
|
+
continue;
|
|
80764
|
+
}
|
|
80765
|
+
store.mergeNode(n);
|
|
80766
|
+
landedNodes++;
|
|
80767
|
+
}
|
|
80768
|
+
let landedEdges = 0;
|
|
80769
|
+
for (const e of res.edges) {
|
|
80770
|
+
const from = localId(e.from);
|
|
80771
|
+
const to = localId(e.to);
|
|
80772
|
+
if (!from || !to) continue;
|
|
80773
|
+
if (store.outEdges(from, [e.type]).some((x) => x.to === to)) continue;
|
|
80774
|
+
const id = from === e.from && to === e.to ? e.id : edgeId(from, e.type, to);
|
|
80775
|
+
if (store.getEdge(id)) continue;
|
|
80776
|
+
store.mergeEdge({ ...e, id, from, to });
|
|
80777
|
+
landedEdges++;
|
|
80778
|
+
}
|
|
80779
|
+
profile.projectHydratedAt = Date.now();
|
|
80780
|
+
saveProfile(root, profile);
|
|
80781
|
+
return { hydrated: true, nodes: landedNodes, edges: landedEdges, own: ownNodes };
|
|
80782
|
+
} catch (err2) {
|
|
80783
|
+
return {
|
|
80784
|
+
hydrated: false,
|
|
80785
|
+
reason: "error",
|
|
80786
|
+
detail: err2 instanceof Error ? err2.message : String(err2)
|
|
80787
|
+
};
|
|
80788
|
+
}
|
|
80789
|
+
}
|
|
80573
80790
|
|
|
80574
80791
|
// src/project-history.ts
|
|
80575
80792
|
init_src7();
|
|
@@ -83559,6 +83776,44 @@ async function startMultiDaemon(opts = {}) {
|
|
|
83559
83776
|
// eslint-disable-next-line no-console
|
|
83560
83777
|
log: (line) => console.log(line)
|
|
83561
83778
|
});
|
|
83779
|
+
const pullProjectAnchors = async (r, client, salt) => {
|
|
83780
|
+
const sync = await syncProjectAnchors({
|
|
83781
|
+
root: r.root,
|
|
83782
|
+
profile: r.engine.profile,
|
|
83783
|
+
store: r.engine.store,
|
|
83784
|
+
cloud: client,
|
|
83785
|
+
salt
|
|
83786
|
+
});
|
|
83787
|
+
if (sync.synced) {
|
|
83788
|
+
if (sync.anchors + sync.updated > 0) {
|
|
83789
|
+
r.engine.markContextDirty();
|
|
83790
|
+
console.log(
|
|
83791
|
+
`[errata] anchors pulled for ${r.engine.profile.name} \u2014 ${sync.anchors} new, ${sync.updated} re-versioned, ${sync.dropped} unresolved (${sync.landed} priors landed, cursor ${sync.cursor})`
|
|
83792
|
+
);
|
|
83793
|
+
}
|
|
83794
|
+
} else if (sync.reason === "error") {
|
|
83795
|
+
console.warn(`[errata] anchor pull failed for ${r.engine.profile.name}: ${sync.detail}`);
|
|
83796
|
+
}
|
|
83797
|
+
};
|
|
83798
|
+
const ANCHOR_PULL_INTERVAL_MS = 10 * 60 * 1e3;
|
|
83799
|
+
const anchorPullAll = async () => {
|
|
83800
|
+
const c = loadConfig();
|
|
83801
|
+
if (!c.consent.sync || !hasCloudCredential(c)) return;
|
|
83802
|
+
const client = cloudNow();
|
|
83803
|
+
for (const r of records) {
|
|
83804
|
+
const projectId = r.engine.profile.projectId;
|
|
83805
|
+
if (!projectId) continue;
|
|
83806
|
+
try {
|
|
83807
|
+
const { salt } = await resolveProjectSymbolSalt(client, projectId);
|
|
83808
|
+
await pullProjectAnchors(r, client, salt);
|
|
83809
|
+
} catch (err2) {
|
|
83810
|
+
console.warn(`[errata] anchor pull skipped for ${r.engine.profile.name}: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
83811
|
+
}
|
|
83812
|
+
}
|
|
83813
|
+
};
|
|
83814
|
+
const anchorPull = setInterval(() => {
|
|
83815
|
+
void anchorPullAll();
|
|
83816
|
+
}, ANCHOR_PULL_INTERVAL_MS);
|
|
83562
83817
|
const ambientLinkAll = async () => {
|
|
83563
83818
|
const c = loadConfig();
|
|
83564
83819
|
if (!c.consent.sync || !hasCloudCredential(c)) return;
|
|
@@ -83572,18 +83827,29 @@ async function startMultiDaemon(opts = {}) {
|
|
|
83572
83827
|
} else if (out2.reason === "error") {
|
|
83573
83828
|
console.warn(`[errata] project link failed for ${r.engine.profile.name}: ${out2.detail}`);
|
|
83574
83829
|
}
|
|
83575
|
-
|
|
83830
|
+
let primeSalt = null;
|
|
83831
|
+
if (r.engine.profile.projectId && !r.engine.profile.projectHydratedAt) {
|
|
83832
|
+
try {
|
|
83833
|
+
primeSalt = (await resolveProjectSymbolSalt(client, r.engine.profile.projectId)).salt;
|
|
83834
|
+
} catch (err2) {
|
|
83835
|
+
console.warn(
|
|
83836
|
+
`[errata] project prime deferred for ${r.engine.profile.name}: salt unavailable (${err2 instanceof Error ? err2.message : String(err2)})`
|
|
83837
|
+
);
|
|
83838
|
+
}
|
|
83839
|
+
}
|
|
83840
|
+
const hyd = primeSalt ? await hydrateProject({
|
|
83576
83841
|
root: r.root,
|
|
83577
83842
|
profile: r.engine.profile,
|
|
83578
83843
|
store: r.engine.store,
|
|
83579
|
-
cloud: client
|
|
83580
|
-
|
|
83581
|
-
|
|
83844
|
+
cloud: client,
|
|
83845
|
+
salt: primeSalt
|
|
83846
|
+
}) : null;
|
|
83847
|
+
if (hyd?.hydrated) {
|
|
83582
83848
|
r.engine.markContextDirty();
|
|
83583
83849
|
console.log(
|
|
83584
|
-
`[errata] primed ${r.engine.profile.name} from project \u2014 ${hyd.nodes} priors, ${hyd.edges} links`
|
|
83850
|
+
`[errata] primed ${r.engine.profile.name} from project \u2014 ${hyd.nodes} priors, ${hyd.edges} links (${hyd.own} already this checkout's own)`
|
|
83585
83851
|
);
|
|
83586
|
-
} else if (hyd.reason === "error") {
|
|
83852
|
+
} else if (hyd && !hyd.hydrated && hyd.reason === "error") {
|
|
83587
83853
|
console.warn(`[errata] project prime failed for ${r.engine.profile.name}: ${hyd.detail}`);
|
|
83588
83854
|
}
|
|
83589
83855
|
if (r.engine.profile.projectId) {
|
|
@@ -83604,6 +83870,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
83604
83870
|
} else if (re.reason === "error") {
|
|
83605
83871
|
console.warn(`[errata] project reanchor failed for ${r.engine.profile.name}: ${re.detail}`);
|
|
83606
83872
|
}
|
|
83873
|
+
await pullProjectAnchors(r, client, salt);
|
|
83607
83874
|
if (r.engine.profile.projectHydratedAt && !historyInFlight.has(r.root)) {
|
|
83608
83875
|
historyInFlight.add(r.root);
|
|
83609
83876
|
const engine = r.engine;
|
|
@@ -84255,6 +84522,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
84255
84522
|
async stop() {
|
|
84256
84523
|
clearInterval(idleSweep);
|
|
84257
84524
|
clearInterval(codexWake);
|
|
84525
|
+
clearInterval(anchorPull);
|
|
84258
84526
|
clearTimeout(codexWakeBoot);
|
|
84259
84527
|
try {
|
|
84260
84528
|
const cur = readFileSync30(lockPath, "utf8");
|
package/package.json
CHANGED
package/pass-worker.mjs
CHANGED
|
@@ -24662,6 +24662,11 @@ var SqliteGraphStore = class {
|
|
|
24662
24662
|
countByLabel: this.db.prepare(
|
|
24663
24663
|
"SELECT COUNT(*) AS n FROM nodes WHERE label = ? AND valid_to IS NULL"
|
|
24664
24664
|
),
|
|
24665
|
+
// Identity twin of findByLabel — same live-row semantics, two attrs read in
|
|
24666
|
+
// SQL, no attrs JSON.parse or embedding decode (PJ-anchor-reach).
|
|
24667
|
+
identitiesByLabel: this.db.prepare(
|
|
24668
|
+
"SELECT id, json_extract(attrs_json, '$.relPath') AS relPath, json_extract(attrs_json, '$.qname') AS qname FROM nodes WHERE label = ? AND valid_to IS NULL"
|
|
24669
|
+
),
|
|
24665
24670
|
// ALL versions of a label — incl frozen/closed (valid_to set). Used by the
|
|
24666
24671
|
// clean-reindex purge so a true wipe removes history too, not just live rows.
|
|
24667
24672
|
findByLabelAll: this.db.prepare("SELECT * FROM nodes WHERE label = ?"),
|
|
@@ -24962,6 +24967,14 @@ var SqliteGraphStore = class {
|
|
|
24962
24967
|
countNodesByLabel(label) {
|
|
24963
24968
|
return Number(this.stmts.countByLabel.get(label).n);
|
|
24964
24969
|
}
|
|
24970
|
+
findNodeIdentitiesByLabel(label) {
|
|
24971
|
+
const rows = this.stmts.identitiesByLabel.all(label);
|
|
24972
|
+
return rows.map((r) => ({
|
|
24973
|
+
id: r.id,
|
|
24974
|
+
relPath: typeof r.relPath === "string" ? r.relPath : null,
|
|
24975
|
+
qname: typeof r.qname === "string" ? r.qname : null
|
|
24976
|
+
}));
|
|
24977
|
+
}
|
|
24965
24978
|
findAllVersionsByLabel(label) {
|
|
24966
24979
|
const rows = this.stmts.findByLabelAll.all(label);
|
|
24967
24980
|
return rows.map(rowToNode);
|