@inerrata-corporation/errata 2.0.0-dev.76 → 2.0.0-dev.78
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 +158 -3
- package/package.json +1 -1
package/errata.mjs
CHANGED
|
@@ -20899,6 +20899,11 @@ function toWirePayload(batch, runId) {
|
|
|
20899
20899
|
// Origin key (= project) for the refute channel's cross-origin gate; absent
|
|
20900
20900
|
// for id-less batches (principle sync) → those nodes stay fail-open.
|
|
20901
20901
|
...batch.originProject ? { originProject: batch.originProject } : {},
|
|
20902
|
+
// PJ-write (§5a): the AUDIENCE assertion — which project stratum this batch is
|
|
20903
|
+
// contributed to. Verified server-side against the projected link record; an
|
|
20904
|
+
// unverifiable assertion rejects the whole batch rather than downgrading it to a
|
|
20905
|
+
// wider audience. Absent ⇒ exactly today's wire.
|
|
20906
|
+
...batch.projectId ? { projectId: batch.projectId } : {},
|
|
20902
20907
|
nodes,
|
|
20903
20908
|
edges,
|
|
20904
20909
|
...symbolSummaries ? { symbolSummaries } : {}
|
|
@@ -21145,6 +21150,18 @@ var init_client = __esm({
|
|
|
21145
21150
|
async attachProject(projectId, locator) {
|
|
21146
21151
|
return this.json("POST", "/api/gate/projects/attach", { projectId, locator });
|
|
21147
21152
|
}
|
|
21153
|
+
/** Fetch the project's symbol salt (PJ-anchors · plan §6c) — the HMAC key this
|
|
21154
|
+
* workspace needs to derive project-canonical `sym_` anchor ids that CONVERGE
|
|
21155
|
+
* with every other member's.
|
|
21156
|
+
*
|
|
21157
|
+
* Members-only: the server gates it with the same fail-closed check the ingest
|
|
21158
|
+
* door uses (known + active + owned by the caller's org), and 403s otherwise.
|
|
21159
|
+
* Safe to hold locally for exactly one reason — a project member can already read
|
|
21160
|
+
* the project's code, so the salt lets them compute ids for symbols they can see
|
|
21161
|
+
* anyway. It is key material: keep it in memory, never log it. */
|
|
21162
|
+
async projectSymbolSalt(projectId) {
|
|
21163
|
+
return this.json("GET", `/v2/projects/${encodeURIComponent(projectId)}/symbol-salt`);
|
|
21164
|
+
}
|
|
21148
21165
|
/** Create a locator-less project — `errata link --name` for repos without a
|
|
21149
21166
|
* usable remote. Discovery can't find it; the caller stamps the id locally. */
|
|
21150
21167
|
async createProject(name2) {
|
|
@@ -21336,6 +21353,16 @@ var init_client = __esm({
|
|
|
21336
21353
|
if (q.seed?.length) qs.set("seed", q.seed.join(","));
|
|
21337
21354
|
if (q.q) qs.set("q", q.q);
|
|
21338
21355
|
const res = await this.json("GET", `/v2/search?${qs.toString()}`);
|
|
21356
|
+
const { nodes, edges } = this.toCastalia(res);
|
|
21357
|
+
return { nodes, edges, generatedAt: res.generatedAt };
|
|
21358
|
+
}
|
|
21359
|
+
/**
|
|
21360
|
+
* The castalia wire shape → local-mergeable graph. Extracted so the priming fetch
|
|
21361
|
+
* (`getProjectPriors`) lands nodes through the EXACT same mapper as `search`: a
|
|
21362
|
+
* second mapper would be a second convergence-key convention to keep in sync, and
|
|
21363
|
+
* an id-keying drift between the two is a silent no-op, not a crash.
|
|
21364
|
+
*/
|
|
21365
|
+
toCastalia(res) {
|
|
21339
21366
|
const now = Date.now();
|
|
21340
21367
|
const nodes = res.nodes.map((n) => ({
|
|
21341
21368
|
id: n.canonicalId,
|
|
@@ -21369,7 +21396,25 @@ var init_client = __esm({
|
|
|
21369
21396
|
navFailures: 0,
|
|
21370
21397
|
attrs: { ...e.attrs, source: "cloud" }
|
|
21371
21398
|
}));
|
|
21372
|
-
return { nodes, edges
|
|
21399
|
+
return { nodes, edges };
|
|
21400
|
+
}
|
|
21401
|
+
/**
|
|
21402
|
+
* PJ-hydrate: the one-time seeded prime a fresh clone pulls at attach.
|
|
21403
|
+
*
|
|
21404
|
+
* A clone can already READ its project stratum (query-time federation, PJ-read) —
|
|
21405
|
+
* what it can't do is PRIME, because the passive block builds from the local store
|
|
21406
|
+
* and the remote blend is seeded from local problems a new checkout doesn't have.
|
|
21407
|
+
* This lands the top of the stratum locally so both have something to stand on.
|
|
21408
|
+
*
|
|
21409
|
+
* Membership-gated server-side by the same fail-closed check as ingest: a caller
|
|
21410
|
+
* who couldn't write this project's stratum can't prime from it.
|
|
21411
|
+
*/
|
|
21412
|
+
async getProjectPriors(projectId, limit = 50) {
|
|
21413
|
+
const res = await this.json(
|
|
21414
|
+
"GET",
|
|
21415
|
+
`/v2/projects/${encodeURIComponent(projectId)}/priors?limit=${String(limit)}`
|
|
21416
|
+
);
|
|
21417
|
+
return this.toCastalia(res);
|
|
21373
21418
|
}
|
|
21374
21419
|
/** Pull cloud-induced skills + the session-bootstrap prime payload. `seed` is the
|
|
21375
21420
|
* canonical ids of the agent's recent problems — the cloud ranks the skills whose
|
|
@@ -43276,7 +43321,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
|
|
|
43276
43321
|
}
|
|
43277
43322
|
|
|
43278
43323
|
// src/engine.ts
|
|
43279
|
-
var DAEMON_VERSION = true ? "2.0.0-dev.
|
|
43324
|
+
var DAEMON_VERSION = true ? "2.0.0-dev.78" : "2.0.0-alpha.0";
|
|
43280
43325
|
var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
|
|
43281
43326
|
var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
|
|
43282
43327
|
var GIT_OP_MUTE_MS = 4e3;
|
|
@@ -45005,6 +45050,7 @@ function runLockfilePass(opts) {
|
|
|
45005
45050
|
init_src();
|
|
45006
45051
|
init_src2();
|
|
45007
45052
|
init_src8();
|
|
45053
|
+
init_src();
|
|
45008
45054
|
|
|
45009
45055
|
// src/contribution-ready.ts
|
|
45010
45056
|
init_src3();
|
|
@@ -45143,6 +45189,50 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
45143
45189
|
contextNodes.push(shareableContext(t, wireId));
|
|
45144
45190
|
}
|
|
45145
45191
|
}
|
|
45192
|
+
const project = opts.project;
|
|
45193
|
+
if (project) {
|
|
45194
|
+
const now = Date.now();
|
|
45195
|
+
for (const sourceId of anchorSources) {
|
|
45196
|
+
const source = store.getNode(sourceId);
|
|
45197
|
+
if (!source) continue;
|
|
45198
|
+
for (const e of store.outEdges(sourceId, ["ANCHORED_AT"])) {
|
|
45199
|
+
const target = store.getNode(e.to);
|
|
45200
|
+
if (!target) continue;
|
|
45201
|
+
const relPath = target.attrs["relPath"];
|
|
45202
|
+
const qname = target.attrs["qname"];
|
|
45203
|
+
if (typeof relPath !== "string" || typeof qname !== "string") continue;
|
|
45204
|
+
if (ignored(relPath) || ignored(qname)) continue;
|
|
45205
|
+
const symId = projectSymbolId(project.salt, project.projectId, relPath, qname, target.label);
|
|
45206
|
+
if (!contextSeen.has(symId)) {
|
|
45207
|
+
contextSeen.add(symId);
|
|
45208
|
+
contextNodes.push({
|
|
45209
|
+
id: symId,
|
|
45210
|
+
label: "Symbol",
|
|
45211
|
+
// The id IS the description: there is nothing that can be said about an
|
|
45212
|
+
// opaque symbol without disclosing it, and the wire requires a non-empty
|
|
45213
|
+
// string. It must never carry the name or the path.
|
|
45214
|
+
description: symId,
|
|
45215
|
+
// `kind` is coarse (Function/Class/…) — shape, not identity.
|
|
45216
|
+
attrs: { kind: target.label },
|
|
45217
|
+
embedding: [],
|
|
45218
|
+
extractionConfidence: 1,
|
|
45219
|
+
extractionSource: "daemon-extracted",
|
|
45220
|
+
cumulativeSurprise: 0,
|
|
45221
|
+
peakSurprise: 0,
|
|
45222
|
+
cumulativeHits: 0,
|
|
45223
|
+
lastUpdatedAt: now,
|
|
45224
|
+
createdAt: now,
|
|
45225
|
+
memoryTier: "short-term",
|
|
45226
|
+
pageRank: 0,
|
|
45227
|
+
isLandmark: false,
|
|
45228
|
+
community: null,
|
|
45229
|
+
stability: "unstable"
|
|
45230
|
+
});
|
|
45231
|
+
}
|
|
45232
|
+
anchorEdges.push({ ...e, to: symId, attrs: {} });
|
|
45233
|
+
}
|
|
45234
|
+
}
|
|
45235
|
+
}
|
|
45146
45236
|
if (nodes.length === 0 && anchorEdges.length === 0) return null;
|
|
45147
45237
|
let symbolSummaries;
|
|
45148
45238
|
if (opts.lexicon && opts.lexicon.size > 0 && nodes.length > 0) {
|
|
@@ -45401,6 +45491,39 @@ async function ensureProjectLink(root, profile, client) {
|
|
|
45401
45491
|
}
|
|
45402
45492
|
}
|
|
45403
45493
|
|
|
45494
|
+
// src/project-hydrate.ts
|
|
45495
|
+
var DEFAULT_LIMIT = 50;
|
|
45496
|
+
async function hydrateProject(opts) {
|
|
45497
|
+
const { root, profile, store, cloud } = opts;
|
|
45498
|
+
if (!profile.projectId) return { hydrated: false, reason: "no-project" };
|
|
45499
|
+
if (profile.projectHydratedAt) return { hydrated: false, reason: "already-hydrated" };
|
|
45500
|
+
try {
|
|
45501
|
+
const res = await cloud.getProjectPriors(profile.projectId, opts.limit ?? DEFAULT_LIMIT);
|
|
45502
|
+
let landedNodes = 0;
|
|
45503
|
+
for (const n of res.nodes) {
|
|
45504
|
+
if (store.getNode(n.id)) continue;
|
|
45505
|
+
store.mergeNode(n);
|
|
45506
|
+
landedNodes++;
|
|
45507
|
+
}
|
|
45508
|
+
let landedEdges = 0;
|
|
45509
|
+
for (const e of res.edges) {
|
|
45510
|
+
if (!store.getNode(e.from) || !store.getNode(e.to)) continue;
|
|
45511
|
+
if (store.getEdge(e.id)) continue;
|
|
45512
|
+
store.mergeEdge(e);
|
|
45513
|
+
landedEdges++;
|
|
45514
|
+
}
|
|
45515
|
+
profile.projectHydratedAt = Date.now();
|
|
45516
|
+
saveProfile(root, profile);
|
|
45517
|
+
return { hydrated: true, nodes: landedNodes, edges: landedEdges };
|
|
45518
|
+
} catch (err2) {
|
|
45519
|
+
return {
|
|
45520
|
+
hydrated: false,
|
|
45521
|
+
reason: "error",
|
|
45522
|
+
detail: err2 instanceof Error ? err2.message : String(err2)
|
|
45523
|
+
};
|
|
45524
|
+
}
|
|
45525
|
+
}
|
|
45526
|
+
|
|
45404
45527
|
// src/adopt.ts
|
|
45405
45528
|
import { existsSync as existsSync22 } from "node:fs";
|
|
45406
45529
|
import { dirname as dirname8, join as join25 } from "node:path";
|
|
@@ -45444,6 +45567,14 @@ function createRootAdopter(deps) {
|
|
|
45444
45567
|
}
|
|
45445
45568
|
|
|
45446
45569
|
// src/multi.ts
|
|
45570
|
+
var projectSymbolSalts = /* @__PURE__ */ new Map();
|
|
45571
|
+
async function resolveProjectSymbolSalt(client, projectId) {
|
|
45572
|
+
const cached2 = projectSymbolSalts.get(projectId);
|
|
45573
|
+
if (cached2) return { projectId, salt: cached2 };
|
|
45574
|
+
const { salt } = await client.projectSymbolSalt(projectId);
|
|
45575
|
+
projectSymbolSalts.set(projectId, salt);
|
|
45576
|
+
return { projectId, salt };
|
|
45577
|
+
}
|
|
45447
45578
|
function normPath(p) {
|
|
45448
45579
|
return p.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
45449
45580
|
}
|
|
@@ -45834,6 +45965,20 @@ async function startMultiDaemon(opts = {}) {
|
|
|
45834
45965
|
} else if (out2.reason === "error") {
|
|
45835
45966
|
console.warn(`[errata] project link failed for ${r.engine.profile.name}: ${out2.detail}`);
|
|
45836
45967
|
}
|
|
45968
|
+
const hyd = await hydrateProject({
|
|
45969
|
+
root: r.root,
|
|
45970
|
+
profile: r.engine.profile,
|
|
45971
|
+
store: r.engine.store,
|
|
45972
|
+
cloud: client
|
|
45973
|
+
});
|
|
45974
|
+
if (hyd.hydrated) {
|
|
45975
|
+
r.engine.markContextDirty();
|
|
45976
|
+
console.log(
|
|
45977
|
+
`[errata] primed ${r.engine.profile.name} from project \u2014 ${hyd.nodes} priors, ${hyd.edges} links`
|
|
45978
|
+
);
|
|
45979
|
+
} else if (hyd.reason === "error") {
|
|
45980
|
+
console.warn(`[errata] project prime failed for ${r.engine.profile.name}: ${hyd.detail}`);
|
|
45981
|
+
}
|
|
45837
45982
|
}
|
|
45838
45983
|
};
|
|
45839
45984
|
void ambientLinkAll();
|
|
@@ -46052,11 +46197,21 @@ async function startMultiDaemon(opts = {}) {
|
|
|
46052
46197
|
if (summaries.size > 0) lexicon = buildSymbolLexicon(store, summaries);
|
|
46053
46198
|
} catch {
|
|
46054
46199
|
}
|
|
46200
|
+
const projectId = r.engine.profile.projectId;
|
|
46201
|
+
let project;
|
|
46202
|
+
if (projectId) {
|
|
46203
|
+
try {
|
|
46204
|
+
project = await resolveProjectSymbolSalt(client, projectId);
|
|
46205
|
+
} catch {
|
|
46206
|
+
}
|
|
46207
|
+
}
|
|
46055
46208
|
const instances = buildInstanceIngest(store, r.engine.profile, DAEMON_VERSION, ignore, {
|
|
46056
46209
|
includePackages: cfg2.consent.contributePackages,
|
|
46057
|
-
...lexicon ? { lexicon } : {}
|
|
46210
|
+
...lexicon ? { lexicon } : {},
|
|
46211
|
+
...project ? { project } : {}
|
|
46058
46212
|
});
|
|
46059
46213
|
if (instances) {
|
|
46214
|
+
if (project) instances.projectId = project.projectId;
|
|
46060
46215
|
const res = await client.ingest(instances);
|
|
46061
46216
|
uploaded += res.accepted;
|
|
46062
46217
|
const seq = store.currentIngestSeq();
|