@tangle-network/agent-knowledge 19.1.1 → 19.1.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.
@@ -1,12 +1,12 @@
1
1
  import { i as textSourceId, n as slugify, r as stableId, t as sha256 } from "./ids-Bevz_pXV.js";
2
- import { r as searchKnowledge } from "./search-Cbyj2shJ.js";
2
+ import { r as searchKnowledge } from "./search-BBr5eUyT.js";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
4
  import { lstat, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm } from "node:fs/promises";
5
5
  import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
6
6
  import { canonicalCandidateBytes, canonicalCandidateDigest, sha256Bytes, sha256DigestSchema } from "@tangle-network/agent-interface";
7
7
  import { contentHash } from "@tangle-network/agent-eval";
8
8
  import { z } from "zod";
9
- import { constants } from "node:fs";
9
+ import { constants, lstatSync } from "node:fs";
10
10
  import { AsyncLocalStorage } from "node:async_hooks";
11
11
  //#region src/adapters.ts
12
12
  const textSourceAdapter = {
@@ -95,8 +95,7 @@ async function renameDurable(source, target) {
95
95
  await withDirectoryHandle(sourceDir, async (sourceHandle, anchoredSourceDir) => {
96
96
  await withDirectoryHandle(targetDir, async (targetHandle, anchoredTargetDir) => {
97
97
  await rename(resolve(anchoredSourceDir, basename(source)), resolve(anchoredTargetDir, basename(target)));
98
- await syncDirectoryHandle(sourceHandle);
99
- if (targetDir !== sourceDir) await syncDirectoryHandle(targetHandle);
98
+ await Promise.all([syncDirectoryHandle(sourceHandle), targetDir === sourceDir ? void 0 : syncDirectoryHandle(targetHandle)]);
100
99
  });
101
100
  });
102
101
  }
@@ -172,15 +171,20 @@ async function listRegularFileMetadataWithinRoot(root, relativeDirectory) {
172
171
  const walk = async (directory, relative) => {
173
172
  const out = [];
174
173
  const entries = await readdir(directory, { withFileTypes: true });
175
- for (const entry of entries.filter((entry) => entry.isDirectory())) out.push(...await withSafeDirectory(directory, entry.name, false, (child) => walk(child, `${relative}/${entry.name}`)));
174
+ const directories = entries.filter((entry) => entry.isDirectory());
175
+ for (let start = 0; start < directories.length; start += 8) {
176
+ const children = await Promise.all(directories.slice(start, start + 8).map((entry) => withSafeDirectory(directory, entry.name, false, (child) => walk(child, `${relative}/${entry.name}`))));
177
+ for (const child of children) out.push(...child);
178
+ }
176
179
  const files = entries.filter((entry) => !entry.isDirectory());
177
- for (let start = 0; start < files.length; start += 64) {
178
- const batch = await Promise.all(files.slice(start, start + 64).map(async (entry) => {
180
+ for (let start = 0; start < files.length; start += 256) {
181
+ if (start > 0) await new Promise((resume) => setImmediate(resume));
182
+ for (const entry of files.slice(start, start + 256)) {
179
183
  const path = `${relative}/${entry.name}`;
180
184
  if (!entry.isFile()) throw new Error(`knowledge tree contains an unsupported filesystem entry: ${path}`);
181
- const stat = await lstat(resolve(directory, entry.name), { bigint: true });
185
+ const stat = lstatSync(resolve(directory, entry.name), { bigint: true });
182
186
  if (!stat.isFile()) throw new Error(`knowledge path is not a regular file: ${path}`);
183
- return [{
187
+ out.push({
184
188
  path,
185
189
  bytes: Number(stat.size),
186
190
  generation: [
@@ -190,9 +194,8 @@ async function listRegularFileMetadataWithinRoot(root, relativeDirectory) {
190
194
  stat.mtimeNs,
191
195
  stat.ctimeNs
192
196
  ].join(":")
193
- }];
194
- }));
195
- out.push(...batch.flat());
197
+ });
198
+ }
196
199
  }
197
200
  return out.sort((left, right) => left.path.localeCompare(right.path));
198
201
  };
@@ -488,10 +491,6 @@ async function prepareKnowledgeFileTransaction(input) {
488
491
  const preparationDir = await mkdtemp(join(transactionRoot, "prepare-"));
489
492
  let activated = false;
490
493
  try {
491
- for (const item of changed) {
492
- if (item.before) await writeFileDurable(snapshotPath(preparationDir, "before", item.entry.index), item.before);
493
- if (item.after) await writeFileDurable(snapshotPath(preparationDir, "after", item.entry.index), item.after);
494
- }
495
494
  const transaction = transactionSchema.parse({
496
495
  kind: "knowledge-file-transaction",
497
496
  transactionId: randomUUID(),
@@ -505,7 +504,11 @@ async function prepareKnowledgeFileTransaction(input) {
505
504
  createdAt: (input.now ?? (() => /* @__PURE__ */ new Date()))().toISOString(),
506
505
  entries: changed.map((item) => item.entry)
507
506
  });
508
- await writeJsonDurable(join(preparationDir, "transaction.json"), transaction);
507
+ for (const side of ["before", "after"]) if (changed.some((item) => item[side])) await mkdir(join(preparationDir, side));
508
+ await Promise.all([...changed.flatMap((item) => ["before", "after"].flatMap((side) => {
509
+ const bytes = item[side];
510
+ return bytes ? [writeFileDurable(snapshotPath(preparationDir, side, item.entry.index), bytes)] : [];
511
+ })), writeJsonDurable(join(preparationDir, "transaction.json"), transaction)]);
509
512
  const activeDir = join(transactionRoot, activeTransactionDirectoryName(transaction.transactionId));
510
513
  await renameDurable(preparationDir, activeDir);
511
514
  activated = true;
@@ -647,11 +650,13 @@ async function finishKnowledgeFileTransaction(input) {
647
650
  if (transaction.retainHistory === true) await withSafeDirectory(input.root, ".agent-knowledge/history", true, async (historyRoot) => {
648
651
  await renameDurable(join(transactionRoot, activeName), join(historyRoot, transaction.transactionId));
649
652
  });
650
- else await rm(join(transactionRoot, activeName), {
651
- recursive: true,
652
- force: false
653
- });
654
- await syncDirectory(transactionRoot);
653
+ else {
654
+ await rm(join(transactionRoot, activeName), {
655
+ recursive: true,
656
+ force: false
657
+ });
658
+ await syncDirectory(transactionRoot);
659
+ }
655
660
  });
656
661
  }
657
662
  async function hasRetainedTransaction(root, transaction) {
@@ -867,6 +872,86 @@ function loadLockfile() {
867
872
  }
868
873
  const activeRoots = new AsyncLocalStorage();
869
874
  const activeReadRoots = new AsyncLocalStorage();
875
+ const gates = /* @__PURE__ */ new Map();
876
+ /** How long a same-process writer waits for admission before failing like a contended lock. */
877
+ const WRITER_ADMISSION_MS = 6e4;
878
+ function gateOf(root) {
879
+ let gate = gates.get(root);
880
+ if (!gate) {
881
+ gate = {
882
+ readers: 0,
883
+ writing: false,
884
+ readersWaiting: [],
885
+ writersWaiting: []
886
+ };
887
+ gates.set(root, gate);
888
+ }
889
+ return gate;
890
+ }
891
+ /** Admit whoever may go next: waiting readers after a write, otherwise a waiting writer first. */
892
+ function admitNext(root, gate, afterWrite) {
893
+ if (gate.writing) return;
894
+ if (gate.readersWaiting.length > 0 && (afterWrite || gate.writersWaiting.length === 0)) {
895
+ const admitted = gate.readersWaiting.splice(0);
896
+ gate.readers += admitted.length;
897
+ for (const admit of admitted) admit();
898
+ } else if (gate.readers === 0 && gate.writersWaiting.length > 0) {
899
+ gate.writing = true;
900
+ gate.writersWaiting.shift()();
901
+ } else if (gate.readers === 0) gates.delete(root);
902
+ }
903
+ async function enterGate(root, mode, timeoutMs) {
904
+ const gate = gateOf(root);
905
+ const release = mode === "read" ? () => {
906
+ gate.readers -= 1;
907
+ if (gate.readers === 0) admitNext(root, gate, false);
908
+ } : () => {
909
+ gate.writing = false;
910
+ admitNext(root, gate, true);
911
+ };
912
+ if (mode === "read" ? !gate.writing && gate.writersWaiting.length === 0 : !gate.writing && gate.readers === 0 && gate.writersWaiting.length === 0) {
913
+ if (mode === "read") gate.readers += 1;
914
+ else gate.writing = true;
915
+ return once(release);
916
+ }
917
+ const waiting = mode === "read" ? gate.readersWaiting : gate.writersWaiting;
918
+ return new Promise((resolve, reject) => {
919
+ let timer;
920
+ const admit = () => {
921
+ if (timer) clearTimeout(timer);
922
+ resolve(once(release));
923
+ };
924
+ waiting.push(admit);
925
+ timer = setTimeout(() => {
926
+ const position = waiting.indexOf(admit);
927
+ if (position < 0) return;
928
+ waiting.splice(position, 1);
929
+ admitNext(root, gate, mode === "write");
930
+ reject(mode === "read" ? /* @__PURE__ */ new Error("knowledge read could not observe a stable mutation epoch") : Object.assign(/* @__PURE__ */ new Error(`knowledge store is locked by this process: ${root}`), { code: "ELOCKED" }));
931
+ }, timeoutMs);
932
+ timer.unref?.();
933
+ });
934
+ }
935
+ function once(release) {
936
+ let released = false;
937
+ return () => {
938
+ if (released) return;
939
+ released = true;
940
+ release();
941
+ };
942
+ }
943
+ const mutationTimings = new AsyncLocalStorage();
944
+ /** Run `body` and report the lock timings of the store mutations it performed. */
945
+ async function measureKnowledgeMutation(body) {
946
+ const timings = [];
947
+ return {
948
+ value: await mutationTimings.run(timings, body),
949
+ timing: timings.length === 0 ? void 0 : timings.reduce((sum, next) => ({
950
+ lockWaitMs: sum.lockWaitMs + next.lockWaitMs,
951
+ lockHoldMs: sum.lockHoldMs + next.lockHoldMs
952
+ }))
953
+ };
954
+ }
870
955
  var KnowledgeLockLostError = class extends Error {
871
956
  constructor(message, options) {
872
957
  super(message, options);
@@ -886,24 +971,42 @@ async function withKnowledgeMutation(root, mutate, options = {}) {
886
971
  const resolvedRoot = resolve(root);
887
972
  const existing = activeRoots.getStore()?.get(resolvedRoot);
888
973
  if (existing?.active) return await runInlineMutation(existing.lock, mutate);
889
- return withSafeDirectory(resolvedRoot, ".agent-knowledge", true, async (cacheDir) => {
890
- const acquired = await acquireDurableFileLock(resolvedRoot, {
891
- lockfilePath: join(cacheDir, "mutation.lock.durable"),
892
- staleMs: options.staleMs,
893
- retries: options.retries ?? {
894
- retries: 100,
895
- factor: 1.1,
896
- minTimeout: 10,
897
- maxTimeout: 200,
898
- randomize: true
974
+ if (activeReadRoots.getStore()?.has(resolvedRoot)) throw new Error("a knowledge mutation cannot start inside a read of the same store");
975
+ const started = performance.now();
976
+ const leave = await enterGate(resolvedRoot, "write", noRetries(options.retries) ? 0 : WRITER_ADMISSION_MS);
977
+ try {
978
+ return await withSafeDirectory(resolvedRoot, ".agent-knowledge", true, async (cacheDir) => {
979
+ const acquired = await acquireDurableFileLock(resolvedRoot, {
980
+ lockfilePath: join(cacheDir, "mutation.lock.durable"),
981
+ staleMs: options.staleMs,
982
+ retries: options.retries ?? {
983
+ retries: 100,
984
+ factor: 1.1,
985
+ minTimeout: 10,
986
+ maxTimeout: 200,
987
+ randomize: true
988
+ }
989
+ });
990
+ const held = performance.now();
991
+ try {
992
+ return await runOwnedMutation(resolvedRoot, cacheDir, acquired, options, mutate);
993
+ } finally {
994
+ try {
995
+ await acquired.release();
996
+ } finally {
997
+ mutationTimings.getStore()?.push({
998
+ lockWaitMs: Math.round(held - started),
999
+ lockHoldMs: Math.round(performance.now() - held)
1000
+ });
1001
+ }
899
1002
  }
900
1003
  });
901
- try {
902
- return await runOwnedMutation(resolvedRoot, cacheDir, acquired, options, mutate);
903
- } finally {
904
- await acquired.release();
905
- }
906
- });
1004
+ } finally {
1005
+ leave();
1006
+ }
1007
+ }
1008
+ function noRetries(retries) {
1009
+ return retries === 0 || typeof retries === "object" && !Array.isArray(retries) && retries.retries === 0;
907
1010
  }
908
1011
  /**
909
1012
  * Run `body` inside this package's mutation scope for `root`, on a lock the caller already holds.
@@ -1048,21 +1151,26 @@ async function withKnowledgeRead(root, read, options = {}) {
1048
1151
  if (activeReadRoots.getStore()?.has(resolvedRoot)) return await read();
1049
1152
  const readRoots = new Set(activeReadRoots.getStore());
1050
1153
  readRoots.add(resolvedRoot);
1051
- return activeReadRoots.run(readRoots, async () => {
1052
- const retries = options.retries ?? DEFAULT_READ_RETRIES;
1053
- for (let attempt = 0; attempt <= retries; attempt += 1) {
1054
- const before = await readMutationEpoch(resolvedRoot);
1055
- if (isOdd(before)) {
1056
- await waitForActiveMutationEpoch(resolvedRoot, before, options);
1057
- continue;
1154
+ const retries = options.retries ?? DEFAULT_READ_RETRIES;
1155
+ const leave = await enterGate(resolvedRoot, "read", (retries + 1) * (options.waitMs ?? DEFAULT_READ_WAIT_MS));
1156
+ try {
1157
+ return await activeReadRoots.run(readRoots, async () => {
1158
+ for (let attempt = 0; attempt <= retries; attempt += 1) {
1159
+ const before = await readMutationEpoch(resolvedRoot);
1160
+ if (isOdd(before)) {
1161
+ await waitForActiveMutationEpoch(resolvedRoot, before, options);
1162
+ continue;
1163
+ }
1164
+ const result = await read();
1165
+ const after = await readMutationEpoch(resolvedRoot);
1166
+ if (before === after && !isOdd(after)) return result;
1167
+ if (isOdd(after)) await waitForActiveMutationEpoch(resolvedRoot, after, options);
1058
1168
  }
1059
- const result = await read();
1060
- const after = await readMutationEpoch(resolvedRoot);
1061
- if (before === after && !isOdd(after)) return result;
1062
- if (isOdd(after)) await waitForActiveMutationEpoch(resolvedRoot, after, options);
1063
- }
1064
- throw new Error("knowledge read could not observe a stable mutation epoch");
1065
- });
1169
+ throw new Error("knowledge read could not observe a stable mutation epoch");
1170
+ });
1171
+ } finally {
1172
+ leave();
1173
+ }
1066
1174
  }
1067
1175
  async function acquireDurableFileLock(target, options) {
1068
1176
  const stale = Math.max(5e3, options.staleMs ?? DEFAULT_STALE_MS);
@@ -3686,6 +3794,26 @@ function knowledgePageDigest(page) {
3686
3794
  }
3687
3795
  /** Snapshot the exact ordered current/ancestor/shared page view. */
3688
3796
  function createKnowledgeVisibilitySnapshot(visiblePages) {
3797
+ return snapshotVisibility(visiblePages, knowledgePageDigest);
3798
+ }
3799
+ /**
3800
+ * Snapshot a view whose pages are immutable, hashing each page object once across views.
3801
+ * The caller guarantees no page in `visiblePages` can change after this call.
3802
+ */
3803
+ function snapshotImmutableVisibility(visiblePages) {
3804
+ return snapshotVisibility(visiblePages, immutablePageDigest);
3805
+ }
3806
+ const immutablePageDigests = /* @__PURE__ */ new WeakMap();
3807
+ /** `knowledgePageDigest` memoized by object identity, for pages that are deeply frozen. */
3808
+ function immutablePageDigest(page) {
3809
+ let pageDigest = immutablePageDigests.get(page);
3810
+ if (pageDigest === void 0) {
3811
+ pageDigest = knowledgePageDigest(page);
3812
+ if (Object.isFrozen(page)) immutablePageDigests.set(page, pageDigest);
3813
+ }
3814
+ return pageDigest;
3815
+ }
3816
+ function snapshotVisibility(visiblePages, digestOf) {
3689
3817
  if (!Array.isArray(visiblePages)) throw new TypeError("knowledge visibility must be an array");
3690
3818
  const identities = /* @__PURE__ */ new Set();
3691
3819
  const entries = visiblePages.map((entry, position) => {
@@ -3700,20 +3828,35 @@ function createKnowledgeVisibilitySnapshot(visiblePages) {
3700
3828
  pageId: entry.page.id,
3701
3829
  origin,
3702
3830
  path: entry.page.path,
3703
- pageDigest: knowledgePageDigest(entry.page),
3831
+ pageDigest: digestOf(entry.page),
3704
3832
  sourceIds: Object.freeze([...entry.page.sourceIds]),
3705
3833
  invalidated: entry.page.invalidation !== void 0
3706
3834
  });
3707
3835
  });
3708
3836
  const material = visibilityMaterial(entries);
3837
+ const materialBytes = canonicalCandidateBytes(material);
3838
+ const snapshotDigest = sha256Bytes(materialBytes);
3709
3839
  const snapshot = Object.freeze({
3710
3840
  ...material,
3711
- snapshotDigest: canonicalCandidateDigest(material),
3841
+ snapshotDigest,
3712
3842
  entries: Object.freeze(entries)
3713
3843
  });
3714
3844
  visibilityIndexes.set(snapshot, indexVisibility(snapshot));
3845
+ encodedSnapshots.set(snapshot, encodedFromMaterial(materialBytes, snapshotDigest));
3715
3846
  return snapshot;
3716
3847
  }
3848
+ const encodedSnapshots = /* @__PURE__ */ new WeakMap();
3849
+ /**
3850
+ * RFC 8785 orders `snapshotDigest` after every material key, so the stored encoding is the
3851
+ * material's canonical bytes with that one member appended before the closing brace.
3852
+ */
3853
+ function encodedFromMaterial(materialBytes, snapshotDigest) {
3854
+ const member = new TextEncoder().encode(`,"snapshotDigest":${JSON.stringify(snapshotDigest)}}`);
3855
+ const encoded = new Uint8Array(materialBytes.byteLength - 1 + member.byteLength);
3856
+ encoded.set(materialBytes.subarray(0, materialBytes.byteLength - 1));
3857
+ encoded.set(member, materialBytes.byteLength - 1);
3858
+ return encoded;
3859
+ }
3717
3860
  /**
3718
3861
  * Verify a snapshot's schema and canonical digest. The check runs once per
3719
3862
  * snapshot object; later calls with the same object are free.
@@ -3724,6 +3867,8 @@ function verifyKnowledgeVisibilitySnapshot(snapshot) {
3724
3867
  /** Canonical bytes of a verified snapshot, for durable storage. */
3725
3868
  function encodeKnowledgeVisibilitySnapshot(snapshot) {
3726
3869
  const verified = verifyKnowledgeVisibilitySnapshot(snapshot);
3870
+ const encoded = encodedSnapshots.get(verified);
3871
+ if (encoded !== void 0) return encoded.slice();
3727
3872
  return canonicalCandidateBytes({
3728
3873
  schemaVersion: verified.schemaVersion,
3729
3874
  digestAlgorithm: verified.digestAlgorithm,
@@ -3978,43 +4123,92 @@ function createRunScopedStores(options) {
3978
4123
  }
3979
4124
  };
3980
4125
  const roots = /* @__PURE__ */ new Map();
3981
- let cachedBytes = 0;
3982
4126
  let current;
3983
- const readRoot = async (root) => {
3984
- const loaded = await withKnowledgeRead(root, async () => {
3985
- let files;
4127
+ const loadRoot = (root, previous) => withKnowledgeRead(root, async () => {
4128
+ let listed;
4129
+ try {
4130
+ listed = await listRegularFileMetadataWithinRoot(root, pagesDirectory);
4131
+ } catch (error) {
4132
+ if (!isMissingFile(error)) throw error;
4133
+ listed = [];
4134
+ }
4135
+ const generation = JSON.stringify(listed.map((file) => [file.path, file.generation]));
4136
+ if (previous?.generation === generation) return previous;
4137
+ const files = /* @__PURE__ */ new Map();
4138
+ const changed = [];
4139
+ for (const file of listed) {
4140
+ const known = previous?.files.get(file.path);
4141
+ if (known?.generation === file.generation) files.set(file.path, known);
4142
+ else if (!isKnowledgePagePath(file.path)) files.set(file.path, {
4143
+ generation: file.generation,
4144
+ page: void 0
4145
+ });
4146
+ else changed.push(file);
4147
+ }
4148
+ const parse = (path, generation, bytes) => files.set(path, {
4149
+ generation,
4150
+ page: freezeView(knowledgePageFromMarkdown(path, bytes.toString("utf8"), pagesDirectory))
4151
+ });
4152
+ if (changed.length > 64) {
4153
+ const wanted = new Map(changed.map((file) => [file.path, file.generation]));
4154
+ for (const file of await listRegularFilesWithinRoot(root, pagesDirectory)) {
4155
+ const generation = wanted.get(file.path);
4156
+ if (generation !== void 0) parse(file.path, generation, file.bytes);
4157
+ }
4158
+ } else await Promise.all(changed.map(async (file) => {
3986
4159
  try {
3987
- files = await listRegularFileMetadataWithinRoot(root, pagesDirectory);
4160
+ parse(file.path, file.generation, (await readRegularFileWithinRoot(root, file.path)).bytes);
3988
4161
  } catch (error) {
3989
4162
  if (!isMissingFile(error)) throw error;
3990
- files = [];
3991
4163
  }
3992
- const generation = JSON.stringify(files.map((file) => [file.path, file.generation]));
3993
- const previous = roots.get(root);
3994
- if (previous?.generation === generation) return previous;
3995
- const pages = await loadKnowledgePages(root, { pagesDirectory });
3996
- return {
3997
- generation,
3998
- bytes: files.reduce((sum, file) => sum + file.bytes, 0),
3999
- pages: freezeView(pages)
4000
- };
4164
+ }));
4165
+ const pages = Object.freeze([...files.values()].flatMap((file) => file.page ? [file.page] : []).sort((a, b) => a.path.localeCompare(b.path)));
4166
+ return {
4167
+ generation,
4168
+ bytes: listed.reduce((sum, file) => sum + file.bytes, 0),
4169
+ files,
4170
+ pages
4171
+ };
4172
+ });
4173
+ const refreshes = /* @__PURE__ */ new Map();
4174
+ const readRoot = (root) => {
4175
+ let state = refreshes.get(root);
4176
+ if (!state) {
4177
+ state = {};
4178
+ refreshes.set(root, state);
4179
+ }
4180
+ const slot = state;
4181
+ const start = () => {
4182
+ const running = loadRoot(root, roots.get(root)).then((loaded) => retain(root, loaded)).finally(() => {
4183
+ if (slot.running === running) slot.running = void 0;
4184
+ if (!slot.running && !slot.next && refreshes.get(root) === slot) refreshes.delete(root);
4185
+ });
4186
+ slot.running = running;
4187
+ return running;
4188
+ };
4189
+ if (slot.next) return slot.next;
4190
+ if (!slot.running) return start();
4191
+ const next = slot.running.catch(() => void 0).then(() => {
4192
+ slot.next = void 0;
4193
+ return start();
4001
4194
  });
4195
+ slot.next = next;
4196
+ return next;
4197
+ };
4198
+ const retain = (root, loaded) => {
4002
4199
  const existing = roots.get(root);
4003
4200
  if (existing?.generation === loaded.generation) return existing;
4004
- if (existing) {
4005
- roots.delete(root);
4006
- cachedBytes -= existing.bytes;
4007
- }
4008
- if (loaded.bytes <= MAX_CACHED_SOURCE_BYTES) {
4009
- while (roots.size >= MAX_CACHED_ROOTS || cachedBytes + loaded.bytes > MAX_CACHED_SOURCE_BYTES) {
4010
- const oldest = roots.keys().next().value;
4011
- if (oldest === void 0) break;
4012
- cachedBytes -= roots.get(oldest).bytes;
4013
- roots.delete(oldest);
4014
- current = void 0;
4015
- }
4016
- roots.set(root, loaded);
4017
- cachedBytes += loaded.bytes;
4201
+ roots.delete(root);
4202
+ roots.set(root, loaded);
4203
+ const live = new Set(current?.identities.map((identity) => JSON.parse(identity)[0]));
4204
+ live.add(root);
4205
+ let bytes = 0;
4206
+ for (const part of roots.values()) bytes += part.bytes;
4207
+ for (const [candidate, part] of roots) {
4208
+ if (roots.size <= MAX_CACHED_ROOTS && bytes <= MAX_CACHED_SOURCE_BYTES) break;
4209
+ if (live.has(candidate)) continue;
4210
+ roots.delete(candidate);
4211
+ bytes -= part.bytes;
4018
4212
  }
4019
4213
  return loaded;
4020
4214
  };
@@ -4033,19 +4227,19 @@ function createRunScopedStores(options) {
4033
4227
  origin: "shared"
4034
4228
  });
4035
4229
  const identities = scopes.map(({ root, origin }) => JSON.stringify([root, origin]));
4036
- const parts = [];
4037
- for (const scope of scopes) parts.push(await readRoot(scope.root));
4038
- if (current && identities.length === current.identities.length && identities.every((id, i) => id === current.identities[i] && parts[i] === current.parts[i])) return current.chain;
4230
+ const parts = await Promise.all(scopes.map((scope) => readRoot(scope.root)));
4231
+ const latest = current;
4232
+ if (latest && identities.length === latest.identities.length && identities.every((id, i) => id === latest.identities[i] && parts[i] === latest.parts[i])) return latest.chain;
4039
4233
  const chain = Object.freeze(parts.flatMap((part, i) => part.pages.map((page) => Object.freeze({
4040
4234
  page,
4041
4235
  origin: scopes[i].origin
4042
4236
  }))));
4043
4237
  toolViews.add(chain);
4044
- current = parts.length <= MAX_CACHED_ROOTS && parts.reduce((sum, part) => sum + part.bytes, 0) <= MAX_CACHED_SOURCE_BYTES ? {
4238
+ current = {
4045
4239
  identities,
4046
4240
  parts,
4047
4241
  chain
4048
- } : void 0;
4242
+ };
4049
4243
  return chain;
4050
4244
  });
4051
4245
  return stores;
@@ -4439,6 +4633,6 @@ function explainKnowledgeTarget(index, target) {
4439
4633
  };
4440
4634
  }
4441
4635
  //#endregion
4442
- export { writeSourceRegistry as $, ClaimLedgerGoalConflictError as $t, verifyKnowledgeVisibilitySnapshot as A, rollbackKnowledgeFileTransaction as An, loadKnowledgePages as At, assertGradeableEvidence as B, relativeWithinRoot as Bn, KnowledgeIndexSchema as Bt, knowledgePageDigest as C, withKnowledgeRead as Cn, resolveRunScopedCitations as Ct, normalizeVisibilityRef as D, finishKnowledgeFileTransaction as Dn, isScaffoldPath as Dt, normalizeVisibilityArtifact as E, commitKnowledgeFileMutations as En, isKnowledgePagePath as Et, validateKnowledgeIndex as F, isKernelAnchoredPath as Fn, DeepQuestionSchema as Ft, verifyGradeableEvidence as G, withSafeDirectory as Gn, KnowledgeRelationSchema as Gt, gradeClaims as H, renameDurable as Hn, KnowledgePageSchema as Ht, lintKnowledgeIndex as I, isMissingFile as In, KnowledgeBaseCandidateSchema as It, addSourcePath as J, writeJsonDurable as Jn, ResearchClaimRecordSchema as Jt, buildKnowledgeIndex as K, writeFileDurable as Kn, ResearchClaimEvidenceSchema as Kt, CHECKABLE_RUNG_THRESHOLD as L, listRegularFilesWithinRoot as Ln, KnowledgeEventSchema as Lt, visibilityIndexOf as M, normalizePagesDirectory as Mn, WIKILINK_REGEX as Mt, detectNearDuplicatePages as N, canonicalPathsEqual as Nn, extractWikilinks as Nt, validateKnowledgePage as O, knowledgeFileTransactionPlanHash as On, knowledgePageFromMarkdown as Ot, normalizePageText as P, canonicalRelativeWithinRoot as Pn, normalizeLinkTarget as Pt, sourceRegistryPath as Q, KNOWLEDGE_EVENT_TYPES as Qt, DEADLINE_EXIT_CODE as R, readRegularFileNoFollow as Rn, KnowledgeGraphEdgeSchema as Rt, encodeKnowledgeVisibilitySnapshot as S, withKnowledgeMutation as Sn, resolveRunScopedCitation as St, nonEmpty as T, assertKnowledgeMutationPath as Tn, initKnowledgeBase as Tt, gradeFor as U, syncDirectory as Un, KnowledgeRelationGraphSchema as Ut, claimCheckKey as V, removeDurable as Vn, KnowledgePageInvalidationSchema as Vt, verdictFor as W, withSafeDescendant as Wn, KnowledgeRelationNodeSchema as Wt, loadSourceRegistry as X, mediaTypeFor$1 as Xn, SourceAnchorSchema as Xt, addSourceText as Y, writeJsonDurableWithinRoot as Yn, ResearchSourceVersionSchema as Yt, snapshotSourceTextInput as Z, textSourceAdapter as Zn, SourceRecordSchema as Zt, KNOWLEDGE_USE_RECEIPT_SCHEMA_VERSION as _, acquireDurableFileLock as _n, auditKnowledgeCitations as _t, applyKnowledgeWriteBlocksFile as a, claimId as an, KB_STORE_DIR as at, decodeKnowledgeVisibilitySnapshot as b, recoverPendingKnowledgeMutation as bn, resolveKnowledgeCitation as bt, KnowledgeDuplicateIntakeError as c, emptyClaimLedger as cn, buildKnowledgeGraph as ct, createFileRunLineageAuthority as d, mergeClaimLedgers as dn, KnowledgeCitationResolutionError as dt, assertDeepQuestionIntegrity as en, ClaimLedgerMigrationRequiredError as et, createRunScopedStores as f, mergeTrackedClaims as fn, assertCurrentRunCitationsResolved as ft, KNOWLEDGE_RECEIPT_DIGEST_ALGORITHM as g, parseFrontmatter as gn, auditCurrentRunCitations as gt, originatedPages as h, formatFrontmatter as hn, assertRunScopedCitationsResolved as ht, applyKnowledgeWriteBlocks as i, claimEvidenceId as in, KB_INDEX_PATH as it, visibilityIdentity as j, DEFAULT_PAGES_DIRECTORY as jn, writeJson as jt, validateOrigin as k, prepareKnowledgeFileTransaction as kn, layoutFor as kt, assertKnowledgeWriteIntake as l, linkClaimContradictions as ln, knowledgePageRelations as lt, loadKnowledgeToolChain as m, researchSourceVersionKey as mn, assertKnowledgeCitationsResolved as mt, inspectKnowledgeIndex as n, assertResearchClaimLedgerIntegrity as nn, KB_CLAIM_LEDGER_DIR as nt, isSafeKnowledgePath as o, claimSourceHost as on, MemoryKbStore as ot, isKnowledgeToolChain as p, normalizeClaimText as pn, assertKnowledgeCitationAudit as pt, writeKnowledgeIndex as q, writeFileDurableWithinRoot as qn, ResearchClaimLedgerSchema as qt, stringMetadata as r, assertTrackedClaimIntegrity as rn, KB_EVENTS_PATH as rt, parseKnowledgeWriteBlocks as s, deepQuestionId as sn, assertClaimLedgerId as st, explainKnowledgeTarget as t, assertResearchClaimEvidenceIntegrity as tn, FileSystemKbStore as tt, RUN_LINEAGE_BASENAME as u, materializeRegisteredClaimEvidence as un, KnowledgeCitationAuditError as ut, assertSchemaVersion as v, inspectPendingKnowledgeMutation as vn, formatKnowledgeCitationReference as vt, knowledgeVisibilityArtifactRef as w, applyKnowledgeFileTransaction as wn, SCAFFOLD_PAGE_BASENAMES as wt, digest as x, runInKnowledgeMutationScope as xn, resolveKnowledgeCitations as xt, createKnowledgeVisibilitySnapshot as y, isKnowledgeMutationHeld as yn, parseKnowledgeCitationReference as yt, UncheckableClaimError as z, readRegularFileWithinRoot as zn, KnowledgeGraphNodeSchema as zt };
4636
+ export { snapshotSourceTextInput as $, mediaTypeFor$1 as $n, SourceRecordSchema as $t, validateKnowledgePage as A, finishKnowledgeFileTransaction as An, knowledgePageFromMarkdown as At, DEADLINE_EXIT_CODE as B, listRegularFilesWithinRoot as Bn, KnowledgeGraphEdgeSchema as Bt, immutablePageDigest as C, recoverPendingKnowledgeMutation as Cn, resolveKnowledgeCitations as Ct, normalizeVisibilityArtifact as D, applyKnowledgeFileTransaction as Dn, initKnowledgeBase as Dt, nonEmpty as E, withKnowledgeRead as En, SCAFFOLD_PAGE_BASENAMES as Et, detectNearDuplicatePages as F, normalizePagesDirectory as Fn, extractWikilinks as Ft, gradeFor as G, renameDurable as Gn, KnowledgeRelationGraphSchema as Gt, assertGradeableEvidence as H, readRegularFileWithinRoot as Hn, KnowledgeIndexSchema as Ht, normalizePageText as I, canonicalPathsEqual as In, normalizeLinkTarget as It, buildKnowledgeIndex as J, withSafeDirectory as Jn, ResearchClaimEvidenceSchema as Jt, verdictFor as K, syncDirectory as Kn, KnowledgeRelationNodeSchema as Kt, validateKnowledgeIndex as L, canonicalRelativeWithinRoot as Ln, DeepQuestionSchema as Lt, verifyKnowledgeVisibilitySnapshot as M, prepareKnowledgeFileTransaction as Mn, loadKnowledgePages as Mt, visibilityIdentity as N, rollbackKnowledgeFileTransaction as Nn, writeJson as Nt, normalizeVisibilityRef as O, assertKnowledgeMutationPath as On, isKnowledgePagePath as Ot, visibilityIndexOf as P, DEFAULT_PAGES_DIRECTORY as Pn, WIKILINK_REGEX as Pt, loadSourceRegistry as Q, writeJsonDurableWithinRoot as Qn, SourceAnchorSchema as Qt, lintKnowledgeIndex as R, isKernelAnchoredPath as Rn, KnowledgeBaseCandidateSchema as Rt, encodeKnowledgeVisibilitySnapshot as S, measureKnowledgeMutation as Sn, resolveKnowledgeCitation as St, knowledgeVisibilityArtifactRef as T, withKnowledgeMutation as Tn, resolveRunScopedCitations as Tt, claimCheckKey as U, relativeWithinRoot as Un, KnowledgePageInvalidationSchema as Ut, UncheckableClaimError as V, readRegularFileNoFollow as Vn, KnowledgeGraphNodeSchema as Vt, gradeClaims as W, removeDurable as Wn, KnowledgePageSchema as Wt, addSourcePath as X, writeFileDurableWithinRoot as Xn, ResearchClaimRecordSchema as Xt, writeKnowledgeIndex as Y, writeFileDurable as Yn, ResearchClaimLedgerSchema as Yt, addSourceText as Z, writeJsonDurable as Zn, ResearchSourceVersionSchema as Zt, KNOWLEDGE_USE_RECEIPT_SCHEMA_VERSION as _, formatFrontmatter as _n, assertRunScopedCitationsResolved as _t, applyKnowledgeWriteBlocksFile as a, assertTrackedClaimIntegrity as an, KB_EVENTS_PATH as at, decodeKnowledgeVisibilitySnapshot as b, inspectPendingKnowledgeMutation as bn, formatKnowledgeCitationReference as bt, KnowledgeDuplicateIntakeError as c, claimSourceHost as cn, MemoryKbStore as ct, createFileRunLineageAuthority as d, linkClaimContradictions as dn, knowledgePageRelations as dt, KNOWLEDGE_EVENT_TYPES as en, textSourceAdapter as er, sourceRegistryPath as et, createRunScopedStores as f, materializeRegisteredClaimEvidence as fn, KnowledgeCitationAuditError as ft, KNOWLEDGE_RECEIPT_DIGEST_ALGORITHM as g, researchSourceVersionKey as gn, assertKnowledgeCitationsResolved as gt, originatedPages as h, normalizeClaimText as hn, assertKnowledgeCitationAudit as ht, applyKnowledgeWriteBlocks as i, assertResearchClaimLedgerIntegrity as in, KB_CLAIM_LEDGER_DIR as it, validateOrigin as j, knowledgeFileTransactionPlanHash as jn, layoutFor as jt, snapshotImmutableVisibility as k, commitKnowledgeFileMutations as kn, isScaffoldPath as kt, assertKnowledgeWriteIntake as l, deepQuestionId as ln, assertClaimLedgerId as lt, loadKnowledgeToolChain as m, mergeTrackedClaims as mn, assertCurrentRunCitationsResolved as mt, inspectKnowledgeIndex as n, assertDeepQuestionIntegrity as nn, ClaimLedgerMigrationRequiredError as nt, isSafeKnowledgePath as o, claimEvidenceId as on, KB_INDEX_PATH as ot, isKnowledgeToolChain as p, mergeClaimLedgers as pn, KnowledgeCitationResolutionError as pt, verifyGradeableEvidence as q, withSafeDescendant as qn, KnowledgeRelationSchema as qt, stringMetadata as r, assertResearchClaimEvidenceIntegrity as rn, FileSystemKbStore as rt, parseKnowledgeWriteBlocks as s, claimId as sn, KB_STORE_DIR as st, explainKnowledgeTarget as t, ClaimLedgerGoalConflictError as tn, writeSourceRegistry as tt, RUN_LINEAGE_BASENAME as u, emptyClaimLedger as un, buildKnowledgeGraph as ut, assertSchemaVersion as v, parseFrontmatter as vn, auditCurrentRunCitations as vt, knowledgePageDigest as w, runInKnowledgeMutationScope as wn, resolveRunScopedCitation as wt, digest as x, isKnowledgeMutationHeld as xn, parseKnowledgeCitationReference as xt, createKnowledgeVisibilitySnapshot as y, acquireDurableFileLock as yn, auditKnowledgeCitations as yt, CHECKABLE_RUNG_THRESHOLD as z, isMissingFile as zn, KnowledgeEventSchema as zt };
4443
4637
 
4444
- //# sourceMappingURL=inspect-CwIvMcno.js.map
4638
+ //# sourceMappingURL=inspect-C6c4zuKp.js.map