@tangle-network/agent-knowledge 2.0.0 → 2.0.1

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,5 +1,5 @@
1
1
  import '@tangle-network/agent-eval/campaign';
2
- import '../types-CjqPcTTK.js';
3
- export { B as BuildRetrievalBenchmarkCasesFromQrelsOptions, I as INDUSTRY_MEMORY_BENCHMARKS, d as INDUSTRY_RAG_BENCHMARKS, K as KnowledgeAnswerBenchmarkCase, e as KnowledgeAnswerBenchmarkTaskKind, f as KnowledgeBenchmarkArtifact, g as KnowledgeBenchmarkCase, h as KnowledgeBenchmarkCaseBase, i as KnowledgeBenchmarkDistribution, j as KnowledgeBenchmarkEvaluation, k as KnowledgeBenchmarkFamily, l as KnowledgeBenchmarkReport, m as KnowledgeBenchmarkResponder, n as KnowledgeBenchmarkScenario, o as KnowledgeBenchmarkSliceSummary, p as KnowledgeBenchmarkSource, q as KnowledgeBenchmarkSpec, r as KnowledgeBenchmarkSplit, s as KnowledgeBenchmarkTaskKind, t as KnowledgeClaimMatcher, u as KnowledgeMemoryBenchmarkCase, v as KnowledgeMemoryBenchmarkTaskKind, w as KnowledgeMemoryEvent, x as KnowledgeMemoryFactMatcher, y as KnowledgeRetrievalBenchmarkCase, z as KnowledgeRetrievalBenchmarkQrel, A as KnowledgeRetrievalBenchmarkQuery, M as MemoryAdapterBenchmarkCandidate, C as MemoryAdapterBenchmarkRankingRow, V as RunKnowledgeBenchmarkSuiteOptions, W as RunKnowledgeBenchmarkSuiteResult, X as RunMemoryAdapterBenchmarkOptions, Y as RunMemoryAdapterBenchmarkResult, Z as buildFirstPartyMemoryLifecycleBenchmarkCases, _ as buildIndustryMemoryBenchmarkSmokeCases, $ as buildIndustryRagBenchmarkSmokeCases, a0 as buildKnowledgeBenchmarkScenarios, a1 as buildRetrievalBenchmarkCasesFromQrels, a4 as createInMemoryBenchmarkAdapter, a5 as createMemoryAdapterBenchmarkResponder, a6 as createNoopMemoryBenchmarkAdapter, a7 as isKnowledgeMemoryBenchmarkCase, a8 as knowledgeBenchmarkJudge, a9 as parseKnowledgeBenchmarkJsonl, aa as parseKnowledgeBenchmarkQrels, ab as renderKnowledgeBenchmarkReportMarkdown, ac as respondToIndustryMemoryBenchmarkSmokeCase, ad as respondToIndustryRagBenchmarkSmokeCase, ai as runKnowledgeBenchmarkSuite, aj as runMemoryAdapterBenchmark, al as scoreKnowledgeBenchmarkArtifact, am as scoreMemoryBenchmarkArtifact, ao as summarizeKnowledgeBenchmarkCampaign } from '../index-BLxw1I_F.js';
4
- import '../types-C17sAROL.js';
2
+ import '../types-BFRyr390.js';
3
+ export { B as BuildRetrievalBenchmarkCasesFromQrelsOptions, I as INDUSTRY_MEMORY_BENCHMARKS, d as INDUSTRY_RAG_BENCHMARKS, K as KnowledgeAnswerBenchmarkCase, e as KnowledgeAnswerBenchmarkTaskKind, f as KnowledgeBenchmarkArtifact, g as KnowledgeBenchmarkCase, h as KnowledgeBenchmarkCaseBase, i as KnowledgeBenchmarkDistribution, j as KnowledgeBenchmarkEvaluation, k as KnowledgeBenchmarkFamily, l as KnowledgeBenchmarkReport, m as KnowledgeBenchmarkResponder, n as KnowledgeBenchmarkScenario, o as KnowledgeBenchmarkSliceSummary, p as KnowledgeBenchmarkSource, q as KnowledgeBenchmarkSpec, r as KnowledgeBenchmarkSplit, s as KnowledgeBenchmarkTaskKind, t as KnowledgeClaimMatcher, u as KnowledgeMemoryBenchmarkCase, v as KnowledgeMemoryBenchmarkTaskKind, w as KnowledgeMemoryEvent, x as KnowledgeMemoryFactMatcher, y as KnowledgeRetrievalBenchmarkCase, z as KnowledgeRetrievalBenchmarkQrel, A as KnowledgeRetrievalBenchmarkQuery, M as MemoryAdapterBenchmarkCandidate, C as MemoryAdapterBenchmarkRankingRow, V as RunKnowledgeBenchmarkSuiteOptions, W as RunKnowledgeBenchmarkSuiteResult, X as RunMemoryAdapterBenchmarkOptions, Y as RunMemoryAdapterBenchmarkResult, Z as buildFirstPartyMemoryLifecycleBenchmarkCases, _ as buildIndustryMemoryBenchmarkSmokeCases, $ as buildIndustryRagBenchmarkSmokeCases, a0 as buildKnowledgeBenchmarkScenarios, a1 as buildRetrievalBenchmarkCasesFromQrels, a4 as createInMemoryBenchmarkAdapter, a5 as createMemoryAdapterBenchmarkResponder, a6 as createNoopMemoryBenchmarkAdapter, a7 as isKnowledgeMemoryBenchmarkCase, a8 as knowledgeBenchmarkJudge, a9 as parseKnowledgeBenchmarkJsonl, aa as parseKnowledgeBenchmarkQrels, ab as renderKnowledgeBenchmarkReportMarkdown, ac as respondToIndustryMemoryBenchmarkSmokeCase, ad as respondToIndustryRagBenchmarkSmokeCase, ai as runKnowledgeBenchmarkSuite, aj as runMemoryAdapterBenchmark, al as scoreKnowledgeBenchmarkArtifact, am as scoreMemoryBenchmarkArtifact, ao as summarizeKnowledgeBenchmarkCampaign } from '../index-BHQk7jOT.js';
4
+ import '../types-6x0OpfW6.js';
5
5
  import '@tangle-network/agent-eval/rl';
@@ -49,6 +49,94 @@ function anchorsForText(uri, text) {
49
49
  return anchors;
50
50
  }
51
51
 
52
+ // src/wikilinks.ts
53
+ var WIKILINK_REGEX = /\[\[([^\]|]+?)(?:\|[^\]]+?)?\]\]/g;
54
+ function extractWikilinks(content) {
55
+ const links = [];
56
+ const regex = new RegExp(WIKILINK_REGEX.source, "g");
57
+ let match;
58
+ match = regex.exec(content);
59
+ while (match !== null) {
60
+ links.push(match[1].trim());
61
+ match = regex.exec(content);
62
+ }
63
+ return [...new Set(links)];
64
+ }
65
+ function normalizeLinkTarget(target) {
66
+ return target.trim().replace(/\.md$/i, "").toLowerCase().replace(/\s+/g, "-");
67
+ }
68
+
69
+ // src/graph.ts
70
+ function buildKnowledgeGraph(pages) {
71
+ const byId = /* @__PURE__ */ new Map();
72
+ const bySlug = /* @__PURE__ */ new Map();
73
+ for (const page of pages) {
74
+ byId.set(page.id, page);
75
+ bySlug.set(normalizeLinkTarget(page.id), page);
76
+ bySlug.set(normalizeLinkTarget(page.title), page);
77
+ bySlug.set(normalizeLinkTarget(page.path.split("/").pop().replace(/\.md$/, "")), page);
78
+ }
79
+ const incoming = /* @__PURE__ */ new Map();
80
+ const outgoing = /* @__PURE__ */ new Map();
81
+ const edgesByKey = /* @__PURE__ */ new Map();
82
+ for (const page of pages) {
83
+ outgoing.set(page.id, 0);
84
+ incoming.set(page.id, 0);
85
+ }
86
+ for (const page of pages) {
87
+ for (const raw of page.outLinks) {
88
+ const target = bySlug.get(normalizeLinkTarget(raw));
89
+ if (!target || target.id === page.id) continue;
90
+ const key = `${page.id}->${target.id}`;
91
+ const edge = edgesByKey.get(key);
92
+ if (edge) edge.weight += 1;
93
+ else
94
+ edgesByKey.set(key, {
95
+ source: page.id,
96
+ target: target.id,
97
+ weight: 1,
98
+ reasons: ["wikilink"]
99
+ });
100
+ outgoing.set(page.id, (outgoing.get(page.id) ?? 0) + 1);
101
+ incoming.set(target.id, (incoming.get(target.id) ?? 0) + 1);
102
+ }
103
+ }
104
+ addSourceOverlapEdges(pages, edgesByKey);
105
+ const nodes = pages.map((page) => ({
106
+ id: page.id,
107
+ title: page.title,
108
+ path: page.path,
109
+ tags: page.tags,
110
+ sourceIds: page.sourceIds,
111
+ outDegree: outgoing.get(page.id) ?? 0,
112
+ inDegree: incoming.get(page.id) ?? 0
113
+ }));
114
+ return { nodes, edges: [...edgesByKey.values()].sort((a, b) => b.weight - a.weight) };
115
+ }
116
+ function addSourceOverlapEdges(pages, edges) {
117
+ for (let i = 0; i < pages.length; i++) {
118
+ for (let j = i + 1; j < pages.length; j++) {
119
+ const a = pages[i];
120
+ const b = pages[j];
121
+ const overlap = a.sourceIds.filter((source) => b.sourceIds.includes(source));
122
+ if (overlap.length === 0) continue;
123
+ const key = `${a.id}->${b.id}`;
124
+ const edge = edges.get(key);
125
+ if (edge) {
126
+ edge.weight += overlap.length * 0.5;
127
+ edge.reasons.push("shared-source");
128
+ } else {
129
+ edges.set(key, {
130
+ source: a.id,
131
+ target: b.id,
132
+ weight: overlap.length * 0.5,
133
+ reasons: ["shared-source"]
134
+ });
135
+ }
136
+ }
137
+ }
138
+ }
139
+
52
140
  // src/mutation-lock.ts
53
141
  import { AsyncLocalStorage } from "async_hooks";
54
142
  import { mkdir as mkdir2, readFile } from "fs/promises";
@@ -1097,156 +1185,6 @@ function isOdd(value) {
1097
1185
  return value % 2 === 1;
1098
1186
  }
1099
1187
 
1100
- // src/frontmatter.ts
1101
- function parseFrontmatter(content) {
1102
- const normalized = content.replace(/\r\n/g, "\n");
1103
- if (!normalized.startsWith("---\n")) return { frontmatter: {}, body: normalized };
1104
- const end = normalized.indexOf("\n---", 4);
1105
- if (end < 0) return { frontmatter: {}, body: normalized };
1106
- const raw = normalized.slice(4, end);
1107
- const after = normalized.slice(end).replace(/^\n---\s*\n?/, "");
1108
- return { frontmatter: parseSimpleYaml(raw), body: after };
1109
- }
1110
- function formatFrontmatter(frontmatter, body) {
1111
- const lines = Object.entries(frontmatter).filter(([, value]) => value !== void 0).flatMap(([key, value]) => formatYamlField(key, value));
1112
- if (lines.length === 0) return body;
1113
- return `---
1114
- ${lines.join("\n")}
1115
- ---
1116
- ${body.replace(/^\n+/, "")}`;
1117
- }
1118
- function parseSimpleYaml(raw) {
1119
- const out = {};
1120
- const lines = raw.split("\n");
1121
- for (let i = 0; i < lines.length; i++) {
1122
- const line = lines[i];
1123
- const scalar = /^([A-Za-z0-9_-]+):\s*(.*)$/.exec(line);
1124
- if (!scalar) continue;
1125
- const key = scalar[1];
1126
- const rest = scalar[2].trim();
1127
- if (rest === "") {
1128
- const items = [];
1129
- while (i + 1 < lines.length) {
1130
- const item = /^\s*-\s*(.+?)\s*$/.exec(lines[i + 1]);
1131
- if (!item) break;
1132
- items.push(unquote(item[1]));
1133
- i++;
1134
- }
1135
- out[key] = items;
1136
- continue;
1137
- }
1138
- if (rest.startsWith("[") && rest.endsWith("]")) {
1139
- out[key] = rest.slice(1, -1).split(",").map((part) => unquote(part.trim())).filter(Boolean);
1140
- } else if (rest === "true" || rest === "false") {
1141
- out[key] = rest === "true";
1142
- } else if (/^-?\d+(?:\.\d+)?$/.test(rest)) {
1143
- out[key] = Number(rest);
1144
- } else {
1145
- out[key] = unquote(rest);
1146
- }
1147
- }
1148
- return out;
1149
- }
1150
- function formatYamlField(key, value) {
1151
- if (Array.isArray(value)) {
1152
- return [`${key}:`, ...value.map((item) => ` - ${String(item)}`)];
1153
- }
1154
- if (typeof value === "string") return [`${key}: ${value}`];
1155
- if (typeof value === "number" || typeof value === "boolean") return [`${key}: ${String(value)}`];
1156
- return [`${key}: ${JSON.stringify(value)}`];
1157
- }
1158
- function unquote(value) {
1159
- return value.replace(/^['"]|['"]$/g, "");
1160
- }
1161
-
1162
- // src/wikilinks.ts
1163
- var WIKILINK_REGEX = /\[\[([^\]|]+?)(?:\|[^\]]+?)?\]\]/g;
1164
- function extractWikilinks(content) {
1165
- const links = [];
1166
- const regex = new RegExp(WIKILINK_REGEX.source, "g");
1167
- let match;
1168
- match = regex.exec(content);
1169
- while (match !== null) {
1170
- links.push(match[1].trim());
1171
- match = regex.exec(content);
1172
- }
1173
- return [...new Set(links)];
1174
- }
1175
- function normalizeLinkTarget(target) {
1176
- return target.trim().replace(/\.md$/i, "").toLowerCase().replace(/\s+/g, "-");
1177
- }
1178
-
1179
- // src/graph.ts
1180
- function buildKnowledgeGraph(pages) {
1181
- const byId = /* @__PURE__ */ new Map();
1182
- const bySlug = /* @__PURE__ */ new Map();
1183
- for (const page of pages) {
1184
- byId.set(page.id, page);
1185
- bySlug.set(normalizeLinkTarget(page.id), page);
1186
- bySlug.set(normalizeLinkTarget(page.title), page);
1187
- bySlug.set(normalizeLinkTarget(page.path.split("/").pop().replace(/\.md$/, "")), page);
1188
- }
1189
- const incoming = /* @__PURE__ */ new Map();
1190
- const outgoing = /* @__PURE__ */ new Map();
1191
- const edgesByKey = /* @__PURE__ */ new Map();
1192
- for (const page of pages) {
1193
- outgoing.set(page.id, 0);
1194
- incoming.set(page.id, 0);
1195
- }
1196
- for (const page of pages) {
1197
- for (const raw of page.outLinks) {
1198
- const target = bySlug.get(normalizeLinkTarget(raw));
1199
- if (!target || target.id === page.id) continue;
1200
- const key = `${page.id}->${target.id}`;
1201
- const edge = edgesByKey.get(key);
1202
- if (edge) edge.weight += 1;
1203
- else
1204
- edgesByKey.set(key, {
1205
- source: page.id,
1206
- target: target.id,
1207
- weight: 1,
1208
- reasons: ["wikilink"]
1209
- });
1210
- outgoing.set(page.id, (outgoing.get(page.id) ?? 0) + 1);
1211
- incoming.set(target.id, (incoming.get(target.id) ?? 0) + 1);
1212
- }
1213
- }
1214
- addSourceOverlapEdges(pages, edgesByKey);
1215
- const nodes = pages.map((page) => ({
1216
- id: page.id,
1217
- title: page.title,
1218
- path: page.path,
1219
- tags: page.tags,
1220
- sourceIds: page.sourceIds,
1221
- outDegree: outgoing.get(page.id) ?? 0,
1222
- inDegree: incoming.get(page.id) ?? 0
1223
- }));
1224
- return { nodes, edges: [...edgesByKey.values()].sort((a, b) => b.weight - a.weight) };
1225
- }
1226
- function addSourceOverlapEdges(pages, edges) {
1227
- for (let i = 0; i < pages.length; i++) {
1228
- for (let j = i + 1; j < pages.length; j++) {
1229
- const a = pages[i];
1230
- const b = pages[j];
1231
- const overlap = a.sourceIds.filter((source) => b.sourceIds.includes(source));
1232
- if (overlap.length === 0) continue;
1233
- const key = `${a.id}->${b.id}`;
1234
- const edge = edges.get(key);
1235
- if (edge) {
1236
- edge.weight += overlap.length * 0.5;
1237
- edge.reasons.push("shared-source");
1238
- } else {
1239
- edges.set(key, {
1240
- source: a.id,
1241
- target: b.id,
1242
- weight: overlap.length * 0.5,
1243
- reasons: ["shared-source"]
1244
- });
1245
- }
1246
- }
1247
- }
1248
- }
1249
-
1250
1188
  // src/schemas.ts
1251
1189
  import { z as z2 } from "zod";
1252
1190
  var SourceAnchorSchema = z2.object({
@@ -1370,6 +1308,68 @@ var KnowledgeBaseCandidateSchema = z2.object({
1370
1308
  metadata: z2.record(z2.string(), z2.unknown()).optional()
1371
1309
  });
1372
1310
 
1311
+ // src/frontmatter.ts
1312
+ function parseFrontmatter(content) {
1313
+ const normalized = content.replace(/\r\n/g, "\n");
1314
+ if (!normalized.startsWith("---\n")) return { frontmatter: {}, body: normalized };
1315
+ const end = normalized.indexOf("\n---", 4);
1316
+ if (end < 0) return { frontmatter: {}, body: normalized };
1317
+ const raw = normalized.slice(4, end);
1318
+ const after = normalized.slice(end).replace(/^\n---\s*\n?/, "");
1319
+ return { frontmatter: parseSimpleYaml(raw), body: after };
1320
+ }
1321
+ function formatFrontmatter(frontmatter, body) {
1322
+ const lines = Object.entries(frontmatter).filter(([, value]) => value !== void 0).flatMap(([key, value]) => formatYamlField(key, value));
1323
+ if (lines.length === 0) return body;
1324
+ return `---
1325
+ ${lines.join("\n")}
1326
+ ---
1327
+ ${body.replace(/^\n+/, "")}`;
1328
+ }
1329
+ function parseSimpleYaml(raw) {
1330
+ const out = {};
1331
+ const lines = raw.split("\n");
1332
+ for (let i = 0; i < lines.length; i++) {
1333
+ const line = lines[i];
1334
+ const scalar = /^([A-Za-z0-9_-]+):\s*(.*)$/.exec(line);
1335
+ if (!scalar) continue;
1336
+ const key = scalar[1];
1337
+ const rest = scalar[2].trim();
1338
+ if (rest === "") {
1339
+ const items = [];
1340
+ while (i + 1 < lines.length) {
1341
+ const item = /^\s*-\s*(.+?)\s*$/.exec(lines[i + 1]);
1342
+ if (!item) break;
1343
+ items.push(unquote(item[1]));
1344
+ i++;
1345
+ }
1346
+ out[key] = items;
1347
+ continue;
1348
+ }
1349
+ if (rest.startsWith("[") && rest.endsWith("]")) {
1350
+ out[key] = rest.slice(1, -1).split(",").map((part) => unquote(part.trim())).filter(Boolean);
1351
+ } else if (rest === "true" || rest === "false") {
1352
+ out[key] = rest === "true";
1353
+ } else if (/^-?\d+(?:\.\d+)?$/.test(rest)) {
1354
+ out[key] = Number(rest);
1355
+ } else {
1356
+ out[key] = unquote(rest);
1357
+ }
1358
+ }
1359
+ return out;
1360
+ }
1361
+ function formatYamlField(key, value) {
1362
+ if (Array.isArray(value)) {
1363
+ return [`${key}:`, ...value.map((item) => ` - ${String(item)}`)];
1364
+ }
1365
+ if (typeof value === "string") return [`${key}: ${value}`];
1366
+ if (typeof value === "number" || typeof value === "boolean") return [`${key}: ${String(value)}`];
1367
+ return [`${key}: ${JSON.stringify(value)}`];
1368
+ }
1369
+ function unquote(value) {
1370
+ return value.replace(/^['"]|['"]$/g, "");
1371
+ }
1372
+
1373
1373
  // src/store.ts
1374
1374
  import { join as join3 } from "path";
1375
1375
  function layoutFor(root) {
@@ -1869,66 +1869,31 @@ function extractSourceRefs(text) {
1869
1869
  return refs;
1870
1870
  }
1871
1871
 
1872
- // src/metadata.ts
1873
- function stringMetadata(metadata, key) {
1874
- const value = metadata?.[key];
1875
- return typeof value === "string" ? value : void 0;
1876
- }
1877
-
1878
- // src/inspect.ts
1879
- function inspectKnowledgeIndex(index, options = {}) {
1880
- const now = options.now ?? /* @__PURE__ */ new Date();
1881
- const findings = lintKnowledgeIndex(index);
1882
- const degree = new Map(index.graph.nodes.map((node) => [node.id, node.inDegree + node.outDegree]));
1883
- const sourceFreshness = index.sources.map((source) => inspectSourceFreshness(source, now));
1884
- return {
1885
- pageCount: index.pages.length,
1886
- sourceCount: index.sources.length,
1887
- expiredSourceCount: sourceFreshness.filter((source) => source.status === "expired").length,
1888
- staleSourceCount: sourceFreshness.filter((source) => source.status !== "fresh").length,
1889
- edgeCount: index.graph.edges.length,
1890
- findingCount: findings.length,
1891
- blockingFindingCount: findings.filter((finding) => finding.severity === "error").length,
1892
- topPages: [...index.pages].sort((a, b) => (degree.get(b.id) ?? 0) - (degree.get(a.id) ?? 0)).slice(0, 10).map((page) => ({
1893
- path: page.path,
1894
- title: page.title,
1895
- degree: degree.get(page.id) ?? 0,
1896
- sources: page.sourceIds.length
1897
- })),
1898
- sourceFreshness,
1899
- findings
1900
- };
1901
- }
1902
- function inspectSourceFreshness(source, now) {
1903
- const validUntil = source.validUntil ?? stringMetadata(source.metadata, "validUntil") ?? stringMetadata(source.metadata, "expiresAt");
1904
- const lastVerifiedAt = source.lastVerifiedAt ?? stringMetadata(source.metadata, "lastVerifiedAt");
1905
- const status = validUntil && Number.isFinite(Date.parse(validUntil)) ? Date.parse(validUntil) <= now.getTime() ? "expired" : "fresh" : "unknown";
1906
- return { id: source.id, title: source.title, uri: source.uri, status, validUntil, lastVerifiedAt };
1907
- }
1908
- function explainKnowledgeTarget(index, target) {
1909
- const page = index.pages.find(
1910
- (candidate) => candidate.path === target || candidate.id === target || candidate.title.toLowerCase() === target.toLowerCase()
1911
- );
1912
- const inbound = page ? index.graph.edges.filter((edge) => edge.target === page.id).map(
1913
- (edge) => index.pages.find((candidate) => candidate.id === edge.source)?.path ?? edge.source
1914
- ) : [];
1915
- const related = page ? searchKnowledge(index, `${page.title} ${page.tags.join(" ")}`, 6).filter((result) => result.page.id !== page.id).map((result) => ({
1916
- path: result.page.path,
1917
- title: result.page.title,
1918
- score: result.score
1919
- })) : searchKnowledge(index, target, 6).map((result) => ({
1920
- path: result.page.path,
1921
- title: result.page.title,
1922
- score: result.score
1923
- }));
1924
- return {
1925
- target,
1926
- page,
1927
- sources: page ? index.sources.filter((source) => page.sourceIds.includes(source.id)).map((source) => ({ id: source.id, title: source.title, uri: source.uri })) : [],
1928
- links: page?.outLinks ?? [],
1929
- inbound,
1930
- related
1931
- };
1872
+ // src/validate.ts
1873
+ function validateKnowledgeIndex(index, options = {}) {
1874
+ const findings = [...lintKnowledgeIndex(index)];
1875
+ const parsed = KnowledgeIndexSchema.safeParse(index);
1876
+ if (!parsed.success) {
1877
+ findings.push({
1878
+ type: "missing-frontmatter",
1879
+ severity: "error",
1880
+ message: parsed.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ")
1881
+ });
1882
+ }
1883
+ if (options.strict) {
1884
+ for (const page of index.pages) {
1885
+ if (isScaffoldPath(page.path)) continue;
1886
+ if (!page.frontmatter.id || !page.frontmatter.title) {
1887
+ findings.push({
1888
+ type: "missing-frontmatter",
1889
+ severity: "error",
1890
+ page: page.path,
1891
+ message: "Strict mode requires id and title frontmatter."
1892
+ });
1893
+ }
1894
+ }
1895
+ }
1896
+ return { ok: !findings.some((finding) => finding.severity === "error"), findings };
1932
1897
  }
1933
1898
 
1934
1899
  // src/write-protocol.ts
@@ -2031,37 +1996,71 @@ async function applyKnowledgeWriteBlocksFile(root, proposalPath) {
2031
1996
  return applyKnowledgeWriteBlocks(root, await readFile3(proposalPath, "utf8"));
2032
1997
  }
2033
1998
 
2034
- // src/validate.ts
2035
- function validateKnowledgeIndex(index, options = {}) {
2036
- const findings = [...lintKnowledgeIndex(index)];
2037
- const parsed = KnowledgeIndexSchema.safeParse(index);
2038
- if (!parsed.success) {
2039
- findings.push({
2040
- type: "missing-frontmatter",
2041
- severity: "error",
2042
- message: parsed.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ")
2043
- });
2044
- }
2045
- if (options.strict) {
2046
- for (const page of index.pages) {
2047
- if (isScaffoldPath(page.path)) continue;
2048
- if (!page.frontmatter.id || !page.frontmatter.title) {
2049
- findings.push({
2050
- type: "missing-frontmatter",
2051
- severity: "error",
2052
- page: page.path,
2053
- message: "Strict mode requires id and title frontmatter."
2054
- });
2055
- }
2056
- }
2057
- }
2058
- return { ok: !findings.some((finding) => finding.severity === "error"), findings };
1999
+ // src/metadata.ts
2000
+ function stringMetadata(metadata, key) {
2001
+ const value = metadata?.[key];
2002
+ return typeof value === "string" ? value : void 0;
2003
+ }
2004
+
2005
+ // src/inspect.ts
2006
+ function inspectKnowledgeIndex(index, options = {}) {
2007
+ const now = options.now ?? /* @__PURE__ */ new Date();
2008
+ const findings = lintKnowledgeIndex(index);
2009
+ const degree = new Map(index.graph.nodes.map((node) => [node.id, node.inDegree + node.outDegree]));
2010
+ const sourceFreshness = index.sources.map((source) => inspectSourceFreshness(source, now));
2011
+ return {
2012
+ pageCount: index.pages.length,
2013
+ sourceCount: index.sources.length,
2014
+ expiredSourceCount: sourceFreshness.filter((source) => source.status === "expired").length,
2015
+ staleSourceCount: sourceFreshness.filter((source) => source.status !== "fresh").length,
2016
+ edgeCount: index.graph.edges.length,
2017
+ findingCount: findings.length,
2018
+ blockingFindingCount: findings.filter((finding) => finding.severity === "error").length,
2019
+ topPages: [...index.pages].sort((a, b) => (degree.get(b.id) ?? 0) - (degree.get(a.id) ?? 0)).slice(0, 10).map((page) => ({
2020
+ path: page.path,
2021
+ title: page.title,
2022
+ degree: degree.get(page.id) ?? 0,
2023
+ sources: page.sourceIds.length
2024
+ })),
2025
+ sourceFreshness,
2026
+ findings
2027
+ };
2028
+ }
2029
+ function inspectSourceFreshness(source, now) {
2030
+ const validUntil = source.validUntil ?? stringMetadata(source.metadata, "validUntil") ?? stringMetadata(source.metadata, "expiresAt");
2031
+ const lastVerifiedAt = source.lastVerifiedAt ?? stringMetadata(source.metadata, "lastVerifiedAt");
2032
+ const status = validUntil && Number.isFinite(Date.parse(validUntil)) ? Date.parse(validUntil) <= now.getTime() ? "expired" : "fresh" : "unknown";
2033
+ return { id: source.id, title: source.title, uri: source.uri, status, validUntil, lastVerifiedAt };
2034
+ }
2035
+ function explainKnowledgeTarget(index, target) {
2036
+ const page = index.pages.find(
2037
+ (candidate) => candidate.path === target || candidate.id === target || candidate.title.toLowerCase() === target.toLowerCase()
2038
+ );
2039
+ const inbound = page ? index.graph.edges.filter((edge) => edge.target === page.id).map(
2040
+ (edge) => index.pages.find((candidate) => candidate.id === edge.source)?.path ?? edge.source
2041
+ ) : [];
2042
+ const related = page ? searchKnowledge(index, `${page.title} ${page.tags.join(" ")}`, 6).filter((result) => result.page.id !== page.id).map((result) => ({
2043
+ path: result.page.path,
2044
+ title: result.page.title,
2045
+ score: result.score
2046
+ })) : searchKnowledge(index, target, 6).map((result) => ({
2047
+ path: result.page.path,
2048
+ title: result.page.title,
2049
+ score: result.score
2050
+ }));
2051
+ return {
2052
+ target,
2053
+ page,
2054
+ sources: page ? index.sources.filter((source) => page.sourceIds.includes(source.id)).map((source) => ({ id: source.id, title: source.title, uri: source.uri })) : [],
2055
+ links: page?.outLinks ?? [],
2056
+ inbound,
2057
+ related
2058
+ };
2059
2059
  }
2060
2060
 
2061
2061
  export {
2062
2062
  textSourceAdapter,
2063
2063
  mediaTypeFor,
2064
- stringMetadata,
2065
2064
  writeFileDurableWithinRoot,
2066
2065
  writeJsonDurableWithinRoot,
2067
2066
  renameDurable,
@@ -2075,17 +2074,15 @@ export {
2075
2074
  finishKnowledgeFileTransaction,
2076
2075
  rollbackKnowledgeFileTransaction,
2077
2076
  assertKnowledgeMutationPath,
2077
+ WIKILINK_REGEX,
2078
+ extractWikilinks,
2079
+ normalizeLinkTarget,
2080
+ buildKnowledgeGraph,
2078
2081
  withKnowledgeMutation,
2079
2082
  inspectPendingKnowledgeMutation,
2080
2083
  recoverPendingKnowledgeMutation,
2081
2084
  withKnowledgeRead,
2082
2085
  acquireDurableFileLock,
2083
- parseFrontmatter,
2084
- formatFrontmatter,
2085
- WIKILINK_REGEX,
2086
- extractWikilinks,
2087
- normalizeLinkTarget,
2088
- buildKnowledgeGraph,
2089
2086
  SourceAnchorSchema,
2090
2087
  SourceRecordSchema,
2091
2088
  KnowledgePageSchema,
@@ -2094,6 +2091,8 @@ export {
2094
2091
  KnowledgeIndexSchema,
2095
2092
  KnowledgeEventSchema,
2096
2093
  KnowledgeBaseCandidateSchema,
2094
+ parseFrontmatter,
2095
+ formatFrontmatter,
2097
2096
  layoutFor,
2098
2097
  SCAFFOLD_PAGE_BASENAMES,
2099
2098
  isScaffoldPath,
@@ -2108,12 +2107,13 @@ export {
2108
2107
  buildKnowledgeIndex,
2109
2108
  writeKnowledgeIndex,
2110
2109
  lintKnowledgeIndex,
2111
- inspectKnowledgeIndex,
2112
- explainKnowledgeTarget,
2110
+ validateKnowledgeIndex,
2113
2111
  isSafeKnowledgePath,
2114
2112
  parseKnowledgeWriteBlocks,
2115
2113
  applyKnowledgeWriteBlocks,
2116
2114
  applyKnowledgeWriteBlocksFile,
2117
- validateKnowledgeIndex
2115
+ stringMetadata,
2116
+ inspectKnowledgeIndex,
2117
+ explainKnowledgeTarget
2118
2118
  };
2119
- //# sourceMappingURL=chunk-QID2QVR5.js.map
2119
+ //# sourceMappingURL=chunk-R7ADUA44.js.map