@tangle-network/agent-knowledge 23.0.0 → 23.0.2
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/CHANGELOG.md +10 -0
- package/README.md +4 -1
- package/dist/cli.js +1 -1
- package/dist/index-B2s20NlV.d.ts.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -9
- package/dist/index.js.map +1 -1
- package/dist/{inspect-tl0P7I7w.js → inspect-DgLNFlsA.js} +287 -212
- package/dist/inspect-DgLNFlsA.js.map +1 -0
- package/dist/memory-krApkeRS.js.map +1 -1
- package/docs/agent-tools.md +3 -1
- package/package.json +2 -2
- package/dist/inspect-tl0P7I7w.js.map +0 -1
|
@@ -194,7 +194,8 @@ async function listRegularFileMetadataWithinRoot(root, relativeDirectory) {
|
|
|
194
194
|
stat.size,
|
|
195
195
|
stat.mtimeNs,
|
|
196
196
|
stat.ctimeNs
|
|
197
|
-
].join(":")
|
|
197
|
+
].join(":"),
|
|
198
|
+
changedNs: stat.ctimeNs
|
|
198
199
|
});
|
|
199
200
|
}
|
|
200
201
|
}
|
|
@@ -1210,6 +1211,10 @@ var KnowledgeLockLostError = class extends Error {
|
|
|
1210
1211
|
function isKnowledgeMutationHeld(root) {
|
|
1211
1212
|
return activeRoots.getStore()?.get(resolve(root))?.active === true;
|
|
1212
1213
|
}
|
|
1214
|
+
/** Whether this context already reads or mutates `root`, so it must never wait on another reader. */
|
|
1215
|
+
function isKnowledgeRootHeld(root) {
|
|
1216
|
+
return isKnowledgeMutationHeld(root) || activeReadRoots.getStore()?.has(resolve(root)) === true;
|
|
1217
|
+
}
|
|
1213
1218
|
async function withKnowledgeMutation(root, mutate, options = {}) {
|
|
1214
1219
|
const resolvedRoot = resolve(root);
|
|
1215
1220
|
const existing = activeRoots.getStore()?.get(resolvedRoot);
|
|
@@ -2354,25 +2359,127 @@ async function initKnowledgeBaseUnlocked(root) {
|
|
|
2354
2359
|
return layout;
|
|
2355
2360
|
}
|
|
2356
2361
|
async function loadKnowledgePages(root, options = {}) {
|
|
2357
|
-
|
|
2362
|
+
const { views } = await readKnowledgePageViews([root], options);
|
|
2363
|
+
return views[0].pages.map((page) => structuredClone(page));
|
|
2358
2364
|
}
|
|
2359
|
-
|
|
2365
|
+
const MAX_CACHED_ROOTS = 8;
|
|
2366
|
+
const MAX_CACHED_SOURCE_BYTES = 67108864;
|
|
2367
|
+
const RACY_NS = 1000000000n;
|
|
2368
|
+
const cachedViews = /* @__PURE__ */ new Map();
|
|
2369
|
+
const cachedGroups = /* @__PURE__ */ new Map();
|
|
2370
|
+
const refreshes = /* @__PURE__ */ new Map();
|
|
2371
|
+
/**
|
|
2372
|
+
* Internal. Each root's pages under one pages directory; a file is read and parsed again only when
|
|
2373
|
+
* its identity (device, inode, size, mtime, ctime) changed, so a read of an unchanged store reads
|
|
2374
|
+
* no page. `pagesRead` counts the files this call's refreshes read from disk. Beyond a root and byte
|
|
2375
|
+
* bound, the least recently read roots are evicted, never one read with this call's roots.
|
|
2376
|
+
*/
|
|
2377
|
+
async function readKnowledgePageViews(roots, options = {}) {
|
|
2360
2378
|
const pagesDirectory = normalizePagesDirectory(options.pagesDirectory);
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2379
|
+
const keys = roots.map((root) => JSON.stringify([resolve(root), pagesDirectory]));
|
|
2380
|
+
const loaded = await Promise.all(roots.map((root, index) => refreshView(keys[index], resolve(root), pagesDirectory)));
|
|
2381
|
+
if (keys.length > 1) for (const key of keys) cachedGroups.set(key, keys);
|
|
2382
|
+
const live = new Set(keys.flatMap((key) => cachedGroups.get(key) ?? [key]));
|
|
2383
|
+
let bytes = 0;
|
|
2384
|
+
for (const view of cachedViews.values()) bytes += view.bytes;
|
|
2385
|
+
for (const [key, view] of cachedViews) {
|
|
2386
|
+
if (cachedViews.size <= MAX_CACHED_ROOTS && bytes <= MAX_CACHED_SOURCE_BYTES) break;
|
|
2387
|
+
if (live.has(key)) continue;
|
|
2388
|
+
cachedViews.delete(key);
|
|
2389
|
+
cachedGroups.delete(key);
|
|
2390
|
+
bytes -= view.bytes;
|
|
2367
2391
|
}
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2392
|
+
return {
|
|
2393
|
+
views: loaded.map((item) => item.view),
|
|
2394
|
+
pagesRead: loaded.reduce((sum, item) => sum + item.pagesRead, 0)
|
|
2395
|
+
};
|
|
2396
|
+
}
|
|
2397
|
+
function refreshView(key, root, pagesDirectory) {
|
|
2398
|
+
const start = () => loadView(root, pagesDirectory, cachedViews.get(key)).then((loaded) => {
|
|
2399
|
+
cachedViews.delete(key);
|
|
2400
|
+
cachedViews.set(key, loaded.view);
|
|
2401
|
+
return loaded;
|
|
2402
|
+
});
|
|
2403
|
+
if (isKnowledgeRootHeld(root)) return start();
|
|
2404
|
+
const state = refreshes.get(key) ?? {};
|
|
2405
|
+
refreshes.set(key, state);
|
|
2406
|
+
state.next ??= (state.running ?? Promise.resolve()).catch(() => void 0).then(() => {
|
|
2407
|
+
state.next = void 0;
|
|
2408
|
+
const running = start().finally(() => {
|
|
2409
|
+
if (state.running === running) state.running = void 0;
|
|
2410
|
+
if (!state.running && !state.next) refreshes.delete(key);
|
|
2411
|
+
});
|
|
2412
|
+
state.running = running;
|
|
2413
|
+
return running;
|
|
2414
|
+
});
|
|
2415
|
+
return state.next;
|
|
2416
|
+
}
|
|
2417
|
+
function loadView(root, pagesDirectory, previous) {
|
|
2418
|
+
return withKnowledgeRead(root, async () => {
|
|
2419
|
+
const recent = BigInt(Date.now()) * 1000000n - RACY_NS;
|
|
2420
|
+
const listed = await listRegularFileMetadataWithinRoot(root, pagesDirectory).catch((error) => isMissingFile(error) ? [] : Promise.reject(error));
|
|
2421
|
+
const trusted = (file) => file.changedNs < recent ? file.generation : "";
|
|
2422
|
+
const generation = listed.every((file) => trusted(file) !== "") ? JSON.stringify(listed.map((file) => [file.path, file.generation])) : void 0;
|
|
2423
|
+
if (generation !== void 0 && previous?.generation === generation) return {
|
|
2424
|
+
view: previous,
|
|
2425
|
+
pagesRead: 0
|
|
2426
|
+
};
|
|
2427
|
+
const files = /* @__PURE__ */ new Map();
|
|
2428
|
+
const changed = [];
|
|
2429
|
+
for (const file of listed) {
|
|
2430
|
+
const known = previous?.files.get(file.path);
|
|
2431
|
+
if (known?.generation === file.generation) files.set(file.path, known);
|
|
2432
|
+
else if (isKnowledgePagePath(file.path)) changed.push(file);
|
|
2433
|
+
}
|
|
2434
|
+
const parse = (file, bytes) => {
|
|
2435
|
+
const page = knowledgePageFromMarkdown(file.path, bytes.toString("utf8"), pagesDirectory);
|
|
2436
|
+
const known = previous?.files.get(file.path)?.page;
|
|
2437
|
+
const same = known?.text === page.text && JSON.stringify(known.frontmatter) === JSON.stringify(page.frontmatter);
|
|
2438
|
+
files.set(file.path, {
|
|
2439
|
+
generation: trusted(file),
|
|
2440
|
+
page: same ? known : freezeView(page)
|
|
2441
|
+
});
|
|
2442
|
+
};
|
|
2443
|
+
let pagesRead = changed.length;
|
|
2444
|
+
if (changed.length > listed.length / 2) {
|
|
2445
|
+
const wanted = new Map(changed.map((file) => [file.path, file]));
|
|
2446
|
+
const walked = await listRegularFilesWithinRoot(root, pagesDirectory);
|
|
2447
|
+
for (const file of walked) if (wanted.has(file.path)) parse(wanted.get(file.path), file.bytes);
|
|
2448
|
+
pagesRead = walked.length;
|
|
2449
|
+
} else await mapFileIo(changed, async (file) => {
|
|
2450
|
+
try {
|
|
2451
|
+
parse(file, (await readRegularFileWithinRoot(root, file.path)).bytes);
|
|
2452
|
+
} catch (error) {
|
|
2453
|
+
if (!isMissingFile(error)) throw error;
|
|
2454
|
+
}
|
|
2455
|
+
});
|
|
2456
|
+
const pages = [...files.values()].map((file) => file.page).sort((a, b) => a.path.localeCompare(b.path));
|
|
2457
|
+
const bytes = listed.reduce((sum, file) => sum + file.bytes, 0);
|
|
2458
|
+
if (previous?.pages.length === pages.length && pages.every((p, i) => p === previous.pages[i])) return {
|
|
2459
|
+
view: Object.assign(previous, {
|
|
2460
|
+
generation,
|
|
2461
|
+
bytes,
|
|
2462
|
+
files
|
|
2463
|
+
}),
|
|
2464
|
+
pagesRead
|
|
2465
|
+
};
|
|
2466
|
+
return {
|
|
2467
|
+
view: {
|
|
2468
|
+
pages: Object.freeze(pages),
|
|
2469
|
+
generation,
|
|
2470
|
+
bytes,
|
|
2471
|
+
files
|
|
2472
|
+
},
|
|
2473
|
+
pagesRead
|
|
2474
|
+
};
|
|
2475
|
+
});
|
|
2476
|
+
}
|
|
2477
|
+
function freezeView(value) {
|
|
2478
|
+
if (value !== null && typeof value === "object" && !Object.isFrozen(value)) {
|
|
2479
|
+
for (const child of Object.values(value)) freezeView(child);
|
|
2480
|
+
Object.freeze(value);
|
|
2373
2481
|
}
|
|
2374
|
-
|
|
2375
|
-
return pages;
|
|
2482
|
+
return value;
|
|
2376
2483
|
}
|
|
2377
2484
|
/** True when a root-relative path names authored Markdown rather than scaffolding. */
|
|
2378
2485
|
function isKnowledgePagePath(path) {
|
|
@@ -4154,25 +4261,22 @@ function prepareKnowledgeValidation(index) {
|
|
|
4154
4261
|
* the same report on every run.
|
|
4155
4262
|
*/
|
|
4156
4263
|
function detectNearDuplicatePages(pages, options = {}) {
|
|
4157
|
-
const threshold =
|
|
4158
|
-
const minCharacters = nonNegativeInteger(options.minCharacters ?? 80, "minCharacters");
|
|
4159
|
-
const wordShingleSize = positiveInteger(options.wordShingleSize ?? 5, "wordShingleSize");
|
|
4160
|
-
const characterShingleSize = positiveInteger(options.characterShingleSize ?? 16, "characterShingleSize");
|
|
4161
|
-
const maxDocumentFrequency = positiveInteger(options.maxDocumentFrequency ?? 64, "maxDocumentFrequency");
|
|
4264
|
+
const { threshold, minCharacters, wordShingleSize, characterShingleSize, maxDocumentFrequency } = settings(options);
|
|
4162
4265
|
const maxCandidatePairs = positiveInteger(options.maxCandidatePairs ?? 25e4, "maxCandidatePairs");
|
|
4163
4266
|
const maxReportedPairs = nonNegativeInteger(options.maxReportedPairs ?? 1e3, "maxReportedPairs");
|
|
4164
4267
|
const prepared = pages.map((page) => preparePage(page, wordShingleSize, characterShingleSize)).filter((item) => item !== null && item.normalized.length >= minCharacters).sort((left, right) => left.page.path.localeCompare(right.page.path));
|
|
4165
4268
|
const candidateKeys = /* @__PURE__ */ new Set();
|
|
4166
4269
|
const exactGroups = /* @__PURE__ */ new Map();
|
|
4167
4270
|
const byShingle = /* @__PURE__ */ new Map();
|
|
4271
|
+
const group = (groups, key) => groups.get(key) ?? groups.set(key, []).get(key);
|
|
4168
4272
|
for (const [index, item] of prepared.entries()) {
|
|
4169
|
-
exactGroups
|
|
4170
|
-
for (const shingle of item.shingles) byShingle
|
|
4273
|
+
group(exactGroups, item.normalized).push(index);
|
|
4274
|
+
for (const shingle of item.shingles) group(byShingle, shingle).push(index);
|
|
4171
4275
|
}
|
|
4172
4276
|
let truncated = false;
|
|
4173
4277
|
const addPair = (left, right) => {
|
|
4174
4278
|
if (left === right) return true;
|
|
4175
|
-
const key =
|
|
4279
|
+
const key = Math.min(left, right) * prepared.length + Math.max(left, right);
|
|
4176
4280
|
if (candidateKeys.has(key)) return true;
|
|
4177
4281
|
if (candidateKeys.size >= maxCandidatePairs) {
|
|
4178
4282
|
truncated = true;
|
|
@@ -4191,8 +4295,9 @@ function detectNearDuplicatePages(pages, options = {}) {
|
|
|
4191
4295
|
const pairs = [];
|
|
4192
4296
|
let duplicatePairCount = 0;
|
|
4193
4297
|
let comparedPairCount = 0;
|
|
4194
|
-
for (const key of [...candidateKeys].sort(
|
|
4195
|
-
const
|
|
4298
|
+
for (const key of [...candidateKeys].sort((left, right) => left - right)) {
|
|
4299
|
+
const leftIndex = Math.floor(key / prepared.length);
|
|
4300
|
+
const rightIndex = key % prepared.length;
|
|
4196
4301
|
const left = prepared[leftIndex];
|
|
4197
4302
|
const right = prepared[rightIndex];
|
|
4198
4303
|
comparedPairCount += 1;
|
|
@@ -4232,6 +4337,94 @@ function detectNearDuplicatePages(pages, options = {}) {
|
|
|
4232
4337
|
pairs
|
|
4233
4338
|
};
|
|
4234
4339
|
}
|
|
4340
|
+
function settings(options) {
|
|
4341
|
+
return {
|
|
4342
|
+
threshold: finiteUnitInterval(options.threshold ?? .82, "threshold"),
|
|
4343
|
+
minCharacters: nonNegativeInteger(options.minCharacters ?? 80, "minCharacters"),
|
|
4344
|
+
wordShingleSize: positiveInteger(options.wordShingleSize ?? 5, "wordShingleSize"),
|
|
4345
|
+
characterShingleSize: positiveInteger(options.characterShingleSize ?? 16, "characterShingleSize"),
|
|
4346
|
+
maxDocumentFrequency: positiveInteger(options.maxDocumentFrequency ?? 64, "maxDocumentFrequency")
|
|
4347
|
+
};
|
|
4348
|
+
}
|
|
4349
|
+
const sketches = /* @__PURE__ */ new Map();
|
|
4350
|
+
/**
|
|
4351
|
+
* Every pair between a candidate and another page of `pages` (which holds the candidates) at or
|
|
4352
|
+
* above the threshold. A pair qualifies as in `detectNearDuplicatePages`, an exact normalized copy
|
|
4353
|
+
* or a shared shingle held by at most `maxDocumentFrequency` pages, but no pair cap applies: only
|
|
4354
|
+
* the candidates' shingles index the corpus, as sorted 32-bit shingle hashes kept per frozen page.
|
|
4355
|
+
*/
|
|
4356
|
+
function detectCandidateNearDuplicates(candidates, pages, options = {}) {
|
|
4357
|
+
const config = settings(options);
|
|
4358
|
+
const key = `${config.wordShingleSize}:${config.characterShingleSize}`;
|
|
4359
|
+
const cache = sketches.get(key) ?? sketches.set(key, /* @__PURE__ */ new WeakMap()).get(key);
|
|
4360
|
+
const sketchOf = (page) => {
|
|
4361
|
+
let sketch = cache.get(page);
|
|
4362
|
+
if (sketch === void 0) {
|
|
4363
|
+
const item = preparePage(page, config.wordShingleSize, config.characterShingleSize);
|
|
4364
|
+
const sorted = Uint32Array.from(item?.shingles ?? [], hash32).sort();
|
|
4365
|
+
sketch = item && {
|
|
4366
|
+
length: item.normalized.length,
|
|
4367
|
+
exact: hash32(item.normalized),
|
|
4368
|
+
hashes: sorted.filter((value, index) => index === 0 || sorted[index - 1] !== value)
|
|
4369
|
+
};
|
|
4370
|
+
if (Object.isFrozen(page)) cache.set(page, sketch);
|
|
4371
|
+
}
|
|
4372
|
+
return sketch && sketch.length >= config.minCharacters ? sketch : null;
|
|
4373
|
+
};
|
|
4374
|
+
const eligible = pages.flatMap((page) => {
|
|
4375
|
+
const sketch = sketchOf(page);
|
|
4376
|
+
return sketch ? [{
|
|
4377
|
+
page,
|
|
4378
|
+
sketch
|
|
4379
|
+
}] : [];
|
|
4380
|
+
}).sort((left, right) => left.page.path.localeCompare(right.page.path));
|
|
4381
|
+
const position = new Map(eligible.map((entry, index) => [entry.page, index]));
|
|
4382
|
+
const holders = /* @__PURE__ */ new Map();
|
|
4383
|
+
for (const candidate of candidates.filter((page) => position.has(page))) {
|
|
4384
|
+
const sketch = eligible[position.get(candidate)].sketch;
|
|
4385
|
+
for (const hash of [~sketch.exact, ...sketch.hashes]) holders.set(hash, []);
|
|
4386
|
+
}
|
|
4387
|
+
for (const [index, { sketch }] of eligible.entries()) {
|
|
4388
|
+
holders.get(~sketch.exact)?.push(index);
|
|
4389
|
+
for (const hash of sketch.hashes) {
|
|
4390
|
+
const group = holders.get(hash);
|
|
4391
|
+
if (group && group.length <= config.maxDocumentFrequency) group.push(index);
|
|
4392
|
+
}
|
|
4393
|
+
}
|
|
4394
|
+
const text = (page) => normalizePageText(`${page.title}\n${page.text}`);
|
|
4395
|
+
const pairs = candidates.flatMap((candidate) => {
|
|
4396
|
+
const self = position.get(candidate);
|
|
4397
|
+
if (self === void 0) return [];
|
|
4398
|
+
const { sketch } = eligible[self];
|
|
4399
|
+
const matches = new Set(holders.get(~sketch.exact));
|
|
4400
|
+
for (const hash of sketch.hashes) {
|
|
4401
|
+
const group = holders.get(hash);
|
|
4402
|
+
if (group.length <= config.maxDocumentFrequency) for (const index of group) matches.add(index);
|
|
4403
|
+
}
|
|
4404
|
+
return [...matches].flatMap((index) => {
|
|
4405
|
+
const matched = eligible[index];
|
|
4406
|
+
if (index === self || index < self && candidates.includes(matched.page)) return [];
|
|
4407
|
+
const exact = matched.sketch.exact === sketch.exact && text(matched.page) === text(candidate);
|
|
4408
|
+
const { similarity } = sortedJaccard(sketch.hashes, matched.sketch.hashes);
|
|
4409
|
+
if (!exact && similarity < config.threshold) return [];
|
|
4410
|
+
return [{
|
|
4411
|
+
candidate,
|
|
4412
|
+
matched: matched.page,
|
|
4413
|
+
similarity: exact ? 1 : round(similarity),
|
|
4414
|
+
exact
|
|
4415
|
+
}];
|
|
4416
|
+
});
|
|
4417
|
+
});
|
|
4418
|
+
return {
|
|
4419
|
+
threshold: config.threshold,
|
|
4420
|
+
pairs
|
|
4421
|
+
};
|
|
4422
|
+
}
|
|
4423
|
+
function hash32(value) {
|
|
4424
|
+
let hash = 2166136261;
|
|
4425
|
+
for (let index = 0; index < value.length; index += 1) hash = Math.imul(hash ^ value.charCodeAt(index), 16777619);
|
|
4426
|
+
return hash >>> 0;
|
|
4427
|
+
}
|
|
4235
4428
|
function preparePage(page, wordShingleSize, characterShingleSize) {
|
|
4236
4429
|
const normalized = normalizePageText(`${page.title}\n${page.text}`);
|
|
4237
4430
|
if (normalized.length === 0) return null;
|
|
@@ -4268,25 +4461,26 @@ function jaccard(left, right) {
|
|
|
4268
4461
|
const [small, large] = left.size <= right.size ? [left, right] : [right, left];
|
|
4269
4462
|
let intersectionSize = 0;
|
|
4270
4463
|
for (const item of small) if (large.has(item)) intersectionSize += 1;
|
|
4271
|
-
|
|
4464
|
+
return overlap(left.size, right.size, intersectionSize);
|
|
4465
|
+
}
|
|
4466
|
+
function sortedJaccard(left, right) {
|
|
4467
|
+
let shared = 0;
|
|
4468
|
+
for (let i = 0, j = 0; i < left.length && j < right.length;) {
|
|
4469
|
+
if (left[i] === right[j]) shared += 1;
|
|
4470
|
+
const step = left[i] - right[j];
|
|
4471
|
+
if (step <= 0) i += 1;
|
|
4472
|
+
if (step >= 0) j += 1;
|
|
4473
|
+
}
|
|
4474
|
+
return overlap(left.length, right.length, shared);
|
|
4475
|
+
}
|
|
4476
|
+
function overlap(left, right, intersectionSize) {
|
|
4477
|
+
const unionSize = left + right - intersectionSize;
|
|
4272
4478
|
return {
|
|
4273
4479
|
similarity: unionSize === 0 ? 1 : intersectionSize / unionSize,
|
|
4274
4480
|
intersectionSize,
|
|
4275
4481
|
unionSize
|
|
4276
4482
|
};
|
|
4277
4483
|
}
|
|
4278
|
-
function pairKey(left, right) {
|
|
4279
|
-
return `${left}:${right}`;
|
|
4280
|
-
}
|
|
4281
|
-
function parsePairKey(value) {
|
|
4282
|
-
const [left, right] = value.split(":");
|
|
4283
|
-
return [Number(left), Number(right)];
|
|
4284
|
-
}
|
|
4285
|
-
function pairKeyOrder(left, right) {
|
|
4286
|
-
const [leftA, leftB] = parsePairKey(left);
|
|
4287
|
-
const [rightA, rightB] = parsePairKey(right);
|
|
4288
|
-
return leftA - rightA || leftB - rightB;
|
|
4289
|
-
}
|
|
4290
4484
|
function finiteUnitInterval(value, name) {
|
|
4291
4485
|
if (!Number.isFinite(value) || value < 0 || value > 1) throw new Error(`${name} must be a finite number in [0, 1]`);
|
|
4292
4486
|
return value;
|
|
@@ -4573,11 +4767,12 @@ const toolViews = /* @__PURE__ */ new WeakSet();
|
|
|
4573
4767
|
function isKnowledgeToolChain(chain) {
|
|
4574
4768
|
return toolViews.has(chain);
|
|
4575
4769
|
}
|
|
4576
|
-
const MAX_CACHED_ROOTS = 8;
|
|
4577
|
-
const MAX_CACHED_SOURCE_BYTES = 67108864;
|
|
4578
4770
|
/** Internal tool path. Custom stores retain their existing loadChain contract. */
|
|
4579
|
-
function loadKnowledgeToolChain(stores, runId) {
|
|
4580
|
-
return toolViewLoaders.get(stores)?.(runId) ??
|
|
4771
|
+
async function loadKnowledgeToolChain(stores, runId) {
|
|
4772
|
+
return await toolViewLoaders.get(stores)?.(runId) ?? {
|
|
4773
|
+
chain: await stores.loadChain(runId),
|
|
4774
|
+
timing: {}
|
|
4775
|
+
};
|
|
4581
4776
|
}
|
|
4582
4777
|
function createRunScopedStores(options) {
|
|
4583
4778
|
if (!options || typeof options !== "object") throw new TypeError("createRunScopedStores options are required");
|
|
@@ -4629,118 +4824,14 @@ function createRunScopedStores(options) {
|
|
|
4629
4824
|
},
|
|
4630
4825
|
lineage: resolveLineage,
|
|
4631
4826
|
async loadChain(runId) {
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
pages = await loadKnowledgePages(root, { pagesDirectory });
|
|
4638
|
-
} catch (error) {
|
|
4639
|
-
if (isMissingFile(error)) return;
|
|
4640
|
-
throw error;
|
|
4641
|
-
}
|
|
4642
|
-
for (const page of pages) out.push({
|
|
4643
|
-
page,
|
|
4644
|
-
origin
|
|
4645
|
-
});
|
|
4646
|
-
};
|
|
4647
|
-
await readInto(storePath(runId), "here");
|
|
4648
|
-
for (const ancestor of await resolveLineage(runId)) await readInto(storePath(ancestor), `inherited:${ancestor}`);
|
|
4649
|
-
if (options.sharedRoot) await readInto(options.sharedRoot, "shared");
|
|
4650
|
-
return out;
|
|
4651
|
-
}
|
|
4652
|
-
};
|
|
4653
|
-
const roots = /* @__PURE__ */ new Map();
|
|
4654
|
-
let current;
|
|
4655
|
-
const loadRoot = (root, previous) => withKnowledgeRead(root, async () => {
|
|
4656
|
-
let listed;
|
|
4657
|
-
try {
|
|
4658
|
-
listed = await listRegularFileMetadataWithinRoot(root, pagesDirectory);
|
|
4659
|
-
} catch (error) {
|
|
4660
|
-
if (!isMissingFile(error)) throw error;
|
|
4661
|
-
listed = [];
|
|
4662
|
-
}
|
|
4663
|
-
const generation = JSON.stringify(listed.map((file) => [file.path, file.generation]));
|
|
4664
|
-
if (previous?.generation === generation) return previous;
|
|
4665
|
-
const files = /* @__PURE__ */ new Map();
|
|
4666
|
-
const changed = [];
|
|
4667
|
-
for (const file of listed) {
|
|
4668
|
-
const known = previous?.files.get(file.path);
|
|
4669
|
-
if (known?.generation === file.generation) files.set(file.path, known);
|
|
4670
|
-
else if (!isKnowledgePagePath(file.path)) files.set(file.path, {
|
|
4671
|
-
generation: file.generation,
|
|
4672
|
-
page: void 0
|
|
4673
|
-
});
|
|
4674
|
-
else changed.push(file);
|
|
4675
|
-
}
|
|
4676
|
-
const parse = (path, generation, bytes) => files.set(path, {
|
|
4677
|
-
generation,
|
|
4678
|
-
page: freezeView(knowledgePageFromMarkdown(path, bytes.toString("utf8"), pagesDirectory))
|
|
4679
|
-
});
|
|
4680
|
-
if (changed.length > 64) {
|
|
4681
|
-
const wanted = new Map(changed.map((file) => [file.path, file.generation]));
|
|
4682
|
-
for (const file of await listRegularFilesWithinRoot(root, pagesDirectory)) {
|
|
4683
|
-
const generation = wanted.get(file.path);
|
|
4684
|
-
if (generation !== void 0) parse(file.path, generation, file.bytes);
|
|
4685
|
-
}
|
|
4686
|
-
} else await Promise.all(changed.map(async (file) => {
|
|
4687
|
-
try {
|
|
4688
|
-
parse(file.path, file.generation, (await readRegularFileWithinRoot(root, file.path)).bytes);
|
|
4689
|
-
} catch (error) {
|
|
4690
|
-
if (!isMissingFile(error)) throw error;
|
|
4691
|
-
}
|
|
4692
|
-
}));
|
|
4693
|
-
const pages = Object.freeze([...files.values()].flatMap((file) => file.page ? [file.page] : []).sort((a, b) => a.path.localeCompare(b.path)));
|
|
4694
|
-
return {
|
|
4695
|
-
generation,
|
|
4696
|
-
bytes: listed.reduce((sum, file) => sum + file.bytes, 0),
|
|
4697
|
-
files,
|
|
4698
|
-
pages
|
|
4699
|
-
};
|
|
4700
|
-
});
|
|
4701
|
-
const refreshes = /* @__PURE__ */ new Map();
|
|
4702
|
-
const readRoot = (root) => {
|
|
4703
|
-
let state = refreshes.get(root);
|
|
4704
|
-
if (!state) {
|
|
4705
|
-
state = {};
|
|
4706
|
-
refreshes.set(root, state);
|
|
4707
|
-
}
|
|
4708
|
-
const slot = state;
|
|
4709
|
-
const start = () => {
|
|
4710
|
-
const running = loadRoot(root, roots.get(root)).then((loaded) => retain(root, loaded)).finally(() => {
|
|
4711
|
-
if (slot.running === running) slot.running = void 0;
|
|
4712
|
-
if (!slot.running && !slot.next && refreshes.get(root) === slot) refreshes.delete(root);
|
|
4713
|
-
});
|
|
4714
|
-
slot.running = running;
|
|
4715
|
-
return running;
|
|
4716
|
-
};
|
|
4717
|
-
if (slot.next) return slot.next;
|
|
4718
|
-
if (!slot.running) return start();
|
|
4719
|
-
const next = slot.running.catch(() => void 0).then(() => {
|
|
4720
|
-
slot.next = void 0;
|
|
4721
|
-
return start();
|
|
4722
|
-
});
|
|
4723
|
-
slot.next = next;
|
|
4724
|
-
return next;
|
|
4725
|
-
};
|
|
4726
|
-
const retain = (root, loaded) => {
|
|
4727
|
-
const existing = roots.get(root);
|
|
4728
|
-
if (existing?.generation === loaded.generation) return existing;
|
|
4729
|
-
roots.delete(root);
|
|
4730
|
-
roots.set(root, loaded);
|
|
4731
|
-
const live = new Set(current?.identities.map((identity) => JSON.parse(identity)[0]));
|
|
4732
|
-
live.add(root);
|
|
4733
|
-
let bytes = 0;
|
|
4734
|
-
for (const part of roots.values()) bytes += part.bytes;
|
|
4735
|
-
for (const [candidate, part] of roots) {
|
|
4736
|
-
if (roots.size <= MAX_CACHED_ROOTS && bytes <= MAX_CACHED_SOURCE_BYTES) break;
|
|
4737
|
-
if (live.has(candidate)) continue;
|
|
4738
|
-
roots.delete(candidate);
|
|
4739
|
-
bytes -= part.bytes;
|
|
4827
|
+
const { scopes, views } = await readChain(runId);
|
|
4828
|
+
return views.flatMap((view, i) => view.pages.map((page) => ({
|
|
4829
|
+
page: structuredClone(page),
|
|
4830
|
+
origin: scopes[i].origin
|
|
4831
|
+
})));
|
|
4740
4832
|
}
|
|
4741
|
-
return loaded;
|
|
4742
4833
|
};
|
|
4743
|
-
|
|
4834
|
+
const readChain = async (runId) => {
|
|
4744
4835
|
assertRunId(runId);
|
|
4745
4836
|
const scopes = [{
|
|
4746
4837
|
root: storePath(runId),
|
|
@@ -4754,31 +4845,35 @@ function createRunScopedStores(options) {
|
|
|
4754
4845
|
root: options.sharedRoot,
|
|
4755
4846
|
origin: "shared"
|
|
4756
4847
|
});
|
|
4757
|
-
|
|
4758
|
-
|
|
4848
|
+
return {
|
|
4849
|
+
scopes,
|
|
4850
|
+
...await readKnowledgePageViews(scopes.map((scope) => scope.root), { pagesDirectory })
|
|
4851
|
+
};
|
|
4852
|
+
};
|
|
4853
|
+
let current;
|
|
4854
|
+
toolViewLoaders.set(stores, async (runId) => {
|
|
4855
|
+
const { scopes, views: parts, pagesRead } = await readChain(runId);
|
|
4856
|
+
const origins = scopes.map((scope) => scope.origin);
|
|
4759
4857
|
const latest = current;
|
|
4760
|
-
if (latest
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4858
|
+
if (latest?.parts.length !== parts.length || parts.some((part, i) => part !== latest.parts[i] || origins[i] !== latest.origins[i])) {
|
|
4859
|
+
const chain = Object.freeze(parts.flatMap((part, i) => part.pages.map((page) => Object.freeze({
|
|
4860
|
+
page,
|
|
4861
|
+
origin: origins[i]
|
|
4862
|
+
}))));
|
|
4863
|
+
toolViews.add(chain);
|
|
4864
|
+
current = {
|
|
4865
|
+
origins,
|
|
4866
|
+
parts,
|
|
4867
|
+
chain
|
|
4868
|
+
};
|
|
4869
|
+
}
|
|
4870
|
+
return {
|
|
4871
|
+
chain: current.chain,
|
|
4872
|
+
timing: { pagesRead }
|
|
4770
4873
|
};
|
|
4771
|
-
return chain;
|
|
4772
4874
|
});
|
|
4773
4875
|
return stores;
|
|
4774
4876
|
}
|
|
4775
|
-
function freezeView(value) {
|
|
4776
|
-
if (value !== null && typeof value === "object" && !Object.isFrozen(value)) {
|
|
4777
|
-
for (const child of Object.values(value)) freezeView(child);
|
|
4778
|
-
Object.freeze(value);
|
|
4779
|
-
}
|
|
4780
|
-
return value;
|
|
4781
|
-
}
|
|
4782
4877
|
/** File-backed authority used when the product has no separate run manifest. */
|
|
4783
4878
|
function createFileRunLineageAuthority(runStorePath) {
|
|
4784
4879
|
if (typeof runStorePath !== "function") throw new TypeError("createFileRunLineageAuthority requires a runStorePath function");
|
|
@@ -4893,46 +4988,26 @@ function assertKnowledgeWriteIntake(candidates, options) {
|
|
|
4893
4988
|
page,
|
|
4894
4989
|
origin: "here"
|
|
4895
4990
|
}))];
|
|
4896
|
-
assertNoUnrelatedDuplicate(
|
|
4991
|
+
assertNoUnrelatedDuplicate(candidates, corpus, options.nearDuplicates ?? {});
|
|
4897
4992
|
if (options.citations === false) return Object.freeze([]);
|
|
4898
4993
|
return assertKnowledgeCitationsResolved(corpus, candidates.flatMap((candidate) => (candidate.cites ?? []).map((persisted) => parseKnowledgeCitationReference(persisted))));
|
|
4899
4994
|
}
|
|
4900
|
-
function assertNoUnrelatedDuplicate(
|
|
4901
|
-
|
|
4902
|
-
const
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
if (!leftIsCandidate && !candidatePaths.has(pair.rightPath)) continue;
|
|
4912
|
-
if (pair.leftPageId === pair.rightPageId) continue;
|
|
4913
|
-
const left = byIdentity.get(identityKey(pair.leftPath, pair.leftPageId));
|
|
4914
|
-
const right = byIdentity.get(identityKey(pair.rightPath, pair.rightPageId));
|
|
4915
|
-
if (declaresRelation(left, right.id) || declaresRelation(right, left.id)) continue;
|
|
4916
|
-
const candidate = leftIsCandidate ? left : right;
|
|
4917
|
-
const matched = leftIsCandidate ? right : left;
|
|
4918
|
-
offending.push({
|
|
4919
|
-
candidatePageId: candidate.id,
|
|
4920
|
-
candidatePath: candidate.path,
|
|
4921
|
-
matchedPageId: matched.id,
|
|
4922
|
-
matchedPath: matched.path,
|
|
4923
|
-
similarity: pair.similarity,
|
|
4924
|
-
exact: pair.exact
|
|
4925
|
-
});
|
|
4926
|
-
}
|
|
4927
|
-
if (offending.length > 0) throw new KnowledgeDuplicateIntakeError(offending, report.threshold);
|
|
4928
|
-
}
|
|
4929
|
-
function identityKey(path, id) {
|
|
4930
|
-
return `${path}\u0000${id}`;
|
|
4995
|
+
function assertNoUnrelatedDuplicate(candidates, corpus, nearDuplicates) {
|
|
4996
|
+
const { threshold, pairs } = detectCandidateNearDuplicates(candidates, corpus.map((entry) => entry.page), nearDuplicates);
|
|
4997
|
+
const offending = pairs.filter(({ candidate: c, matched: m }) => c.id !== m.id && !relates(c, m) && !relates(m, c)).map(({ candidate, matched, similarity, exact }) => ({
|
|
4998
|
+
candidatePageId: candidate.id,
|
|
4999
|
+
candidatePath: candidate.path,
|
|
5000
|
+
matchedPageId: matched.id,
|
|
5001
|
+
matchedPath: matched.path,
|
|
5002
|
+
similarity,
|
|
5003
|
+
exact
|
|
5004
|
+
}));
|
|
5005
|
+
if (offending.length > 0) throw new KnowledgeDuplicateIntakeError(offending, threshold);
|
|
4931
5006
|
}
|
|
4932
|
-
/** Whether `page` already relates itself to `
|
|
4933
|
-
function
|
|
4934
|
-
for (const persisted of page.cites ?? []) if (parseKnowledgeCitationReference(persisted).pageId ===
|
|
4935
|
-
return (page.contradicts ?? []).includes(
|
|
5007
|
+
/** Whether `page` already relates itself to `target` through `cites` or `contradicts`. */
|
|
5008
|
+
function relates(page, target) {
|
|
5009
|
+
for (const persisted of page.cites ?? []) if (parseKnowledgeCitationReference(persisted).pageId === target.id) return true;
|
|
5010
|
+
return (page.contradicts ?? []).includes(target.id);
|
|
4936
5011
|
}
|
|
4937
5012
|
// src/write-protocol.ts
|
|
4938
5013
|
const OPENER_LINE = /^---\s*FILE:\s*(.+?)\s*---\s*$/i;
|
|
@@ -5053,10 +5128,10 @@ async function applyKnowledgeWriteBlocks(root, proposalText, options = {}) {
|
|
|
5053
5128
|
}
|
|
5054
5129
|
if (intake) {
|
|
5055
5130
|
const { inheritedPages = [], ...settings } = intake;
|
|
5056
|
-
const
|
|
5131
|
+
const { views } = await readKnowledgePageViews([root], { pagesDirectory });
|
|
5057
5132
|
assertKnowledgeWriteIntake(mutations.filter((mutation) => isKnowledgePagePath(mutation.path)).map((mutation) => knowledgePageFromMarkdown(mutation.path, mutation.content, pagesDirectory)), {
|
|
5058
5133
|
...settings,
|
|
5059
|
-
visiblePages: [...originatedPages(
|
|
5134
|
+
visiblePages: [...originatedPages(views[0].pages), ...inheritedPages]
|
|
5060
5135
|
});
|
|
5061
5136
|
}
|
|
5062
5137
|
await commitKnowledgeFileMutations({
|
|
@@ -5157,4 +5232,4 @@ function explainKnowledgeTarget(index, target) {
|
|
|
5157
5232
|
}
|
|
5158
5233
|
export { loadSourceRegistry as $, writeFileDurableWithinRoot as $n, KNOWLEDGE_EVENT_TYPES as $t, validateKnowledgePage as A, finishKnowledgeFileTransaction as An, isScaffoldPath as At, CHECKABLE_RUNG_THRESHOLD as B, canonicalRelativeWithinRoot as Bn, KnowledgeEventSchema as Bt, immutablePageDigest as C, runInKnowledgeMutationScope as Cn, resolveKnowledgeCitation as Ct, normalizeVisibilityArtifact as D, applyKnowledgeFileTransaction as Dn, SCAFFOLD_PAGE_BASENAMES as Dt, nonEmpty as E, withKnowledgeRead as En, resolveRunScopedCitations as Et, detectNearDuplicatePages as F, recordLockOwner as Fn, WIKILINK_REGEX as Ft, gradeClaims as G, readRegularFileWithinRoot as Gn, KnowledgePageSchema as Gt, UncheckableClaimError as H, isMissingFile as Hn, KnowledgeGraphNodeSchema as Ht, normalizePageText as I, takeOverDeadHolder as In, extractWikilinks as It, verifyGradeableEvidence as J, renameDurable as Jn, ResearchClaimLedgerSchema as Jt, gradeFor as K, relativeWithinRoot as Kn, KnowledgeRelationSchema as Kt, prepareKnowledgeValidation as L, DEFAULT_PAGES_DIRECTORY as Ln, normalizeLinkTarget as Lt, verifyKnowledgeVisibilitySnapshot as M, prepareKnowledgeFileTransaction as Mn, layoutFor as Mt, visibilityIdentity as N, rollbackKnowledgeFileTransaction as Nn, loadKnowledgePages as Nt, normalizeVisibilityRef as O, assertKnowledgeMutationPath as On, initKnowledgeBase as Ot, visibilityIndexOf as P, RUN_LEASE as Pn, writeJson as Pt, addSourceText as Q, writeFileDurable as Qn, SourceRecordSchema as Qt, validateKnowledgeIndex as R, normalizePagesDirectory as Rn, DeepQuestionSchema as Rt, encodeKnowledgeVisibilitySnapshot as S, recoverPendingKnowledgeMutation as Sn, parseKnowledgeCitationReference as St, knowledgeVisibilityArtifactRef as T, withKnowledgeMutationLabel as Tn, resolveRunScopedCitation as Tt, assertGradeableEvidence as U, listRegularFilesWithinRoot as Un, KnowledgeIndexSchema as Ut, DEADLINE_EXIT_CODE as V, isKernelAnchoredPath as Vn, KnowledgeGraphEdgeSchema as Vt, claimCheckKey as W, readRegularFileNoFollow as Wn, KnowledgePageInvalidationSchema as Wt, writeKnowledgeIndex as X, withSafeDescendant as Xn, ResearchSourceVersionSchema as Xt, buildKnowledgeIndex as Y, syncDirectory as Yn, ResearchClaimRecordSchema as Yt, addSourcePath as Z, withSafeDirectory as Zn, SourceAnchorSchema as Zt, KNOWLEDGE_USE_RECEIPT_SCHEMA_VERSION as _, parseFrontmatter as _n, assertKnowledgeCitationsResolved as _t, applyKnowledgeWriteBlocksFile as a, claimEvidenceId as an, KB_CLAIM_LEDGER_DIR as at, decodeKnowledgeVisibilitySnapshot as b, isKnowledgeMutationHeld as bn, auditKnowledgeCitations as bt, KnowledgeDuplicateIntakeError as c, deepQuestionId as cn, KB_STORE_DIR as ct, createFileRunLineageAuthority as d, materializeRegisteredClaimEvidence as dn, buildKnowledgeGraph as dt, ClaimLedgerGoalConflictError as en, writeJsonDurable as er, snapshotSourceTextInput as et, createRunScopedStores as f, mergeClaimLedgers as fn, knowledgePageRelations as ft, KNOWLEDGE_RECEIPT_DIGEST_ALGORITHM as g, formatFrontmatter as gn, assertKnowledgeCitationAudit as gt, originatedPages as h, researchSourceVersionKey as hn, assertCurrentRunCitationsResolved as ht, applyKnowledgeWriteBlocks as i, assertTrackedClaimIntegrity as in, FileSystemKbStore as it, validateOrigin as j, knowledgeFileTransactionPlanHash as jn, knowledgePageFromMarkdown as jt, snapshotImmutableVisibility as k, commitKnowledgeFileMutations as kn, isKnowledgePagePath as kt, assertKnowledgeWriteIntake as l, emptyClaimLedger as ln, MemoryKbStore as lt, loadKnowledgeToolChain as m, normalizeClaimText as mn, KnowledgeCitationResolutionError as mt, inspectKnowledgeIndex as n, assertResearchClaimEvidenceIntegrity as nn, mediaTypeFor$1 as nr, writeSourceRegistry as nt, isSafeKnowledgePath as o, claimId as on, KB_EVENTS_PATH as ot, isKnowledgeToolChain as p, mergeTrackedClaims as pn, KnowledgeCitationAuditError as pt, verdictFor as q, removeDurable as qn, ResearchClaimEvidenceSchema as qt, stringMetadata as r, assertResearchClaimLedgerIntegrity as rn, textSourceAdapter as rr, ClaimLedgerMigrationRequiredError as rt, parseKnowledgeWriteBlocks as s, claimSourceHost as sn, KB_INDEX_PATH as st, explainKnowledgeTarget as t, assertDeepQuestionIntegrity as tn, writeJsonDurableWithinRoot as tr, sourceRegistryPath as tt, RUN_LINEAGE_BASENAME as u, linkClaimContradictions as un, assertClaimLedgerId as ut, assertSchemaVersion as v, acquireDurableFileLock as vn, assertRunScopedCitationsResolved as vt, knowledgePageDigest as w, withKnowledgeMutation as wn, resolveKnowledgeCitations as wt, digest as x, measureKnowledgeMutation as xn, formatKnowledgeCitationReference as xt, createKnowledgeVisibilitySnapshot as y, inspectPendingKnowledgeMutation as yn, auditCurrentRunCitations as yt, lintKnowledgeIndex as z, canonicalPathsEqual as zn, KnowledgeBaseCandidateSchema as zt };
|
|
5159
5234
|
|
|
5160
|
-
//# sourceMappingURL=inspect-
|
|
5235
|
+
//# sourceMappingURL=inspect-DgLNFlsA.js.map
|