@tangle-network/agent-knowledge 2.0.0 → 3.0.0

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/README.md CHANGED
@@ -343,7 +343,7 @@ console.log(promoted.promoted)
343
343
  Calling it again with the same `runId` resumes interrupted work.
344
344
  `withKnowledgeImprovementCandidate` materializes the measured bytes in an isolated temporary directory for the callback, checks them again afterward, and removes the directory.
345
345
  `promoteKnowledgeCandidate` applies only the frozen bytes identified by the approved candidate reference, and refuses if the live base changed.
346
- The current release intentionally accepts only its strict run-state format; incomplete runs created by 1.x must be completed or restarted before upgrading.
346
+ The current release accepts one strict run-state format; incomplete runs created before 3.0 must be completed or restarted before upgrading.
347
347
  The exact candidate workflow requires Linux; other knowledge, retrieval, and evaluation APIs remain cross-platform.
348
348
 
349
349
  If a required phase is missing its hook, the loop throws.
@@ -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";
@@ -341,7 +429,6 @@ var transactionEntrySchema = z.object({
341
429
  afterMode: fileModeSchema.optional()
342
430
  }).strict();
343
431
  var transactionSchema = z.object({
344
- schemaVersion: z.literal(1),
345
432
  kind: z.literal("knowledge-file-transaction"),
346
433
  transactionId: z.string().uuid(),
347
434
  purpose: z.string().min(1),
@@ -349,7 +436,6 @@ var transactionSchema = z.object({
349
436
  entries: z.array(transactionEntrySchema).min(1)
350
437
  }).strict();
351
438
  var transactionDirectionSchema = z.object({
352
- schemaVersion: z.literal(1),
353
439
  transactionId: z.string().uuid(),
354
440
  direction: z.literal("rollback")
355
441
  }).strict();
@@ -418,7 +504,6 @@ async function prepareKnowledgeFileTransaction(input) {
418
504
  }
419
505
  }
420
506
  const transaction = transactionSchema.parse({
421
- schemaVersion: 1,
422
507
  kind: "knowledge-file-transaction",
423
508
  transactionId: randomUUID2(),
424
509
  purpose: input.purpose,
@@ -607,7 +692,6 @@ async function rollbackKnowledgeFileTransaction(input) {
607
692
  const direction = await readTransactionDirection(transactionDir, transaction);
608
693
  if (direction !== "rollback") {
609
694
  await writeJsonDurable(join(transactionDir, "direction.json"), {
610
- schemaVersion: 1,
611
695
  transactionId: transaction.transactionId,
612
696
  direction: "rollback"
613
697
  });
@@ -1097,156 +1181,6 @@ function isOdd(value) {
1097
1181
  return value % 2 === 1;
1098
1182
  }
1099
1183
 
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
1184
  // src/schemas.ts
1251
1185
  import { z as z2 } from "zod";
1252
1186
  var SourceAnchorSchema = z2.object({
@@ -1370,6 +1304,68 @@ var KnowledgeBaseCandidateSchema = z2.object({
1370
1304
  metadata: z2.record(z2.string(), z2.unknown()).optional()
1371
1305
  });
1372
1306
 
1307
+ // src/frontmatter.ts
1308
+ function parseFrontmatter(content) {
1309
+ const normalized = content.replace(/\r\n/g, "\n");
1310
+ if (!normalized.startsWith("---\n")) return { frontmatter: {}, body: normalized };
1311
+ const end = normalized.indexOf("\n---", 4);
1312
+ if (end < 0) return { frontmatter: {}, body: normalized };
1313
+ const raw = normalized.slice(4, end);
1314
+ const after = normalized.slice(end).replace(/^\n---\s*\n?/, "");
1315
+ return { frontmatter: parseSimpleYaml(raw), body: after };
1316
+ }
1317
+ function formatFrontmatter(frontmatter, body) {
1318
+ const lines = Object.entries(frontmatter).filter(([, value]) => value !== void 0).flatMap(([key, value]) => formatYamlField(key, value));
1319
+ if (lines.length === 0) return body;
1320
+ return `---
1321
+ ${lines.join("\n")}
1322
+ ---
1323
+ ${body.replace(/^\n+/, "")}`;
1324
+ }
1325
+ function parseSimpleYaml(raw) {
1326
+ const out = {};
1327
+ const lines = raw.split("\n");
1328
+ for (let i = 0; i < lines.length; i++) {
1329
+ const line = lines[i];
1330
+ const scalar = /^([A-Za-z0-9_-]+):\s*(.*)$/.exec(line);
1331
+ if (!scalar) continue;
1332
+ const key = scalar[1];
1333
+ const rest = scalar[2].trim();
1334
+ if (rest === "") {
1335
+ const items = [];
1336
+ while (i + 1 < lines.length) {
1337
+ const item = /^\s*-\s*(.+?)\s*$/.exec(lines[i + 1]);
1338
+ if (!item) break;
1339
+ items.push(unquote(item[1]));
1340
+ i++;
1341
+ }
1342
+ out[key] = items;
1343
+ continue;
1344
+ }
1345
+ if (rest.startsWith("[") && rest.endsWith("]")) {
1346
+ out[key] = rest.slice(1, -1).split(",").map((part) => unquote(part.trim())).filter(Boolean);
1347
+ } else if (rest === "true" || rest === "false") {
1348
+ out[key] = rest === "true";
1349
+ } else if (/^-?\d+(?:\.\d+)?$/.test(rest)) {
1350
+ out[key] = Number(rest);
1351
+ } else {
1352
+ out[key] = unquote(rest);
1353
+ }
1354
+ }
1355
+ return out;
1356
+ }
1357
+ function formatYamlField(key, value) {
1358
+ if (Array.isArray(value)) {
1359
+ return [`${key}:`, ...value.map((item) => ` - ${String(item)}`)];
1360
+ }
1361
+ if (typeof value === "string") return [`${key}: ${value}`];
1362
+ if (typeof value === "number" || typeof value === "boolean") return [`${key}: ${String(value)}`];
1363
+ return [`${key}: ${JSON.stringify(value)}`];
1364
+ }
1365
+ function unquote(value) {
1366
+ return value.replace(/^['"]|['"]$/g, "");
1367
+ }
1368
+
1373
1369
  // src/store.ts
1374
1370
  import { join as join3 } from "path";
1375
1371
  function layoutFor(root) {
@@ -1869,66 +1865,31 @@ function extractSourceRefs(text) {
1869
1865
  return refs;
1870
1866
  }
1871
1867
 
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
- };
1868
+ // src/validate.ts
1869
+ function validateKnowledgeIndex(index, options = {}) {
1870
+ const findings = [...lintKnowledgeIndex(index)];
1871
+ const parsed = KnowledgeIndexSchema.safeParse(index);
1872
+ if (!parsed.success) {
1873
+ findings.push({
1874
+ type: "missing-frontmatter",
1875
+ severity: "error",
1876
+ message: parsed.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ")
1877
+ });
1878
+ }
1879
+ if (options.strict) {
1880
+ for (const page of index.pages) {
1881
+ if (isScaffoldPath(page.path)) continue;
1882
+ if (!page.frontmatter.id || !page.frontmatter.title) {
1883
+ findings.push({
1884
+ type: "missing-frontmatter",
1885
+ severity: "error",
1886
+ page: page.path,
1887
+ message: "Strict mode requires id and title frontmatter."
1888
+ });
1889
+ }
1890
+ }
1891
+ }
1892
+ return { ok: !findings.some((finding) => finding.severity === "error"), findings };
1932
1893
  }
1933
1894
 
1934
1895
  // src/write-protocol.ts
@@ -2031,37 +1992,71 @@ async function applyKnowledgeWriteBlocksFile(root, proposalPath) {
2031
1992
  return applyKnowledgeWriteBlocks(root, await readFile3(proposalPath, "utf8"));
2032
1993
  }
2033
1994
 
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 };
1995
+ // src/metadata.ts
1996
+ function stringMetadata(metadata, key) {
1997
+ const value = metadata?.[key];
1998
+ return typeof value === "string" ? value : void 0;
1999
+ }
2000
+
2001
+ // src/inspect.ts
2002
+ function inspectKnowledgeIndex(index, options = {}) {
2003
+ const now = options.now ?? /* @__PURE__ */ new Date();
2004
+ const findings = lintKnowledgeIndex(index);
2005
+ const degree = new Map(index.graph.nodes.map((node) => [node.id, node.inDegree + node.outDegree]));
2006
+ const sourceFreshness = index.sources.map((source) => inspectSourceFreshness(source, now));
2007
+ return {
2008
+ pageCount: index.pages.length,
2009
+ sourceCount: index.sources.length,
2010
+ expiredSourceCount: sourceFreshness.filter((source) => source.status === "expired").length,
2011
+ staleSourceCount: sourceFreshness.filter((source) => source.status !== "fresh").length,
2012
+ edgeCount: index.graph.edges.length,
2013
+ findingCount: findings.length,
2014
+ blockingFindingCount: findings.filter((finding) => finding.severity === "error").length,
2015
+ topPages: [...index.pages].sort((a, b) => (degree.get(b.id) ?? 0) - (degree.get(a.id) ?? 0)).slice(0, 10).map((page) => ({
2016
+ path: page.path,
2017
+ title: page.title,
2018
+ degree: degree.get(page.id) ?? 0,
2019
+ sources: page.sourceIds.length
2020
+ })),
2021
+ sourceFreshness,
2022
+ findings
2023
+ };
2024
+ }
2025
+ function inspectSourceFreshness(source, now) {
2026
+ const validUntil = source.validUntil ?? stringMetadata(source.metadata, "validUntil") ?? stringMetadata(source.metadata, "expiresAt");
2027
+ const lastVerifiedAt = source.lastVerifiedAt ?? stringMetadata(source.metadata, "lastVerifiedAt");
2028
+ const status = validUntil && Number.isFinite(Date.parse(validUntil)) ? Date.parse(validUntil) <= now.getTime() ? "expired" : "fresh" : "unknown";
2029
+ return { id: source.id, title: source.title, uri: source.uri, status, validUntil, lastVerifiedAt };
2030
+ }
2031
+ function explainKnowledgeTarget(index, target) {
2032
+ const page = index.pages.find(
2033
+ (candidate) => candidate.path === target || candidate.id === target || candidate.title.toLowerCase() === target.toLowerCase()
2034
+ );
2035
+ const inbound = page ? index.graph.edges.filter((edge) => edge.target === page.id).map(
2036
+ (edge) => index.pages.find((candidate) => candidate.id === edge.source)?.path ?? edge.source
2037
+ ) : [];
2038
+ const related = page ? searchKnowledge(index, `${page.title} ${page.tags.join(" ")}`, 6).filter((result) => result.page.id !== page.id).map((result) => ({
2039
+ path: result.page.path,
2040
+ title: result.page.title,
2041
+ score: result.score
2042
+ })) : searchKnowledge(index, target, 6).map((result) => ({
2043
+ path: result.page.path,
2044
+ title: result.page.title,
2045
+ score: result.score
2046
+ }));
2047
+ return {
2048
+ target,
2049
+ page,
2050
+ sources: page ? index.sources.filter((source) => page.sourceIds.includes(source.id)).map((source) => ({ id: source.id, title: source.title, uri: source.uri })) : [],
2051
+ links: page?.outLinks ?? [],
2052
+ inbound,
2053
+ related
2054
+ };
2059
2055
  }
2060
2056
 
2061
2057
  export {
2062
2058
  textSourceAdapter,
2063
2059
  mediaTypeFor,
2064
- stringMetadata,
2065
2060
  writeFileDurableWithinRoot,
2066
2061
  writeJsonDurableWithinRoot,
2067
2062
  renameDurable,
@@ -2075,17 +2070,15 @@ export {
2075
2070
  finishKnowledgeFileTransaction,
2076
2071
  rollbackKnowledgeFileTransaction,
2077
2072
  assertKnowledgeMutationPath,
2073
+ WIKILINK_REGEX,
2074
+ extractWikilinks,
2075
+ normalizeLinkTarget,
2076
+ buildKnowledgeGraph,
2078
2077
  withKnowledgeMutation,
2079
2078
  inspectPendingKnowledgeMutation,
2080
2079
  recoverPendingKnowledgeMutation,
2081
2080
  withKnowledgeRead,
2082
2081
  acquireDurableFileLock,
2083
- parseFrontmatter,
2084
- formatFrontmatter,
2085
- WIKILINK_REGEX,
2086
- extractWikilinks,
2087
- normalizeLinkTarget,
2088
- buildKnowledgeGraph,
2089
2082
  SourceAnchorSchema,
2090
2083
  SourceRecordSchema,
2091
2084
  KnowledgePageSchema,
@@ -2094,6 +2087,8 @@ export {
2094
2087
  KnowledgeIndexSchema,
2095
2088
  KnowledgeEventSchema,
2096
2089
  KnowledgeBaseCandidateSchema,
2090
+ parseFrontmatter,
2091
+ formatFrontmatter,
2097
2092
  layoutFor,
2098
2093
  SCAFFOLD_PAGE_BASENAMES,
2099
2094
  isScaffoldPath,
@@ -2108,12 +2103,13 @@ export {
2108
2103
  buildKnowledgeIndex,
2109
2104
  writeKnowledgeIndex,
2110
2105
  lintKnowledgeIndex,
2111
- inspectKnowledgeIndex,
2112
- explainKnowledgeTarget,
2106
+ validateKnowledgeIndex,
2113
2107
  isSafeKnowledgePath,
2114
2108
  parseKnowledgeWriteBlocks,
2115
2109
  applyKnowledgeWriteBlocks,
2116
2110
  applyKnowledgeWriteBlocksFile,
2117
- validateKnowledgeIndex
2111
+ stringMetadata,
2112
+ inspectKnowledgeIndex,
2113
+ explainKnowledgeTarget
2118
2114
  };
2119
- //# sourceMappingURL=chunk-QID2QVR5.js.map
2115
+ //# sourceMappingURL=chunk-2BJ5JR3L.js.map