@tangle-network/agent-knowledge 1.1.1 → 1.3.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/dist/index.js CHANGED
@@ -10,6 +10,7 @@ import {
10
10
  SourceRecordSchema,
11
11
  WIKILINK_REGEX,
12
12
  addSourcePath,
13
+ addSourceText,
13
14
  applyKnowledgeWriteBlocks,
14
15
  applyKnowledgeWriteBlocksFile,
15
16
  buildKnowledgeGraph,
@@ -31,126 +32,113 @@ import {
31
32
  parseKnowledgeWriteBlocks,
32
33
  reciprocalRankFusion,
33
34
  searchKnowledge,
34
- sha256,
35
- slugify,
36
35
  sourceRegistryPath,
37
- stableId,
38
36
  textSourceAdapter,
39
37
  tokenizeQuery,
40
38
  validateKnowledgeIndex,
41
39
  writeJson,
42
40
  writeKnowledgeIndex,
43
41
  writeSourceRegistry
44
- } from "./chunk-JWGMOXAT.js";
42
+ } from "./chunk-HKYD765Q.js";
43
+ import {
44
+ IRS_DIMENSION_HINTS,
45
+ MAX_RESPONSE_BYTES,
46
+ MIN_REQUEST_GAP_MS,
47
+ POLITE_USER_AGENT,
48
+ __resetHttpThrottle,
49
+ createCornellLiiSource,
50
+ createIrsPublicationsSource,
51
+ createStateSosSource,
52
+ extractLinks,
53
+ firstMatch,
54
+ htmlToText,
55
+ innerHtmlById,
56
+ looksLikeBlockPage,
57
+ politeFetch
58
+ } from "./chunk-WCYW2GDA.js";
59
+ import {
60
+ sha256,
61
+ slugify,
62
+ stableId
63
+ } from "./chunk-YMKHCTS2.js";
45
64
 
46
- // src/events.ts
47
- function createKnowledgeEvent(input) {
48
- const createdAt = (input.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
65
+ // src/changes.ts
66
+ function detectChanges(prev, next, options = {}) {
67
+ const skipUnverifiable = options.skipUnverifiable ?? true;
68
+ const warnings = [];
69
+ const { map: prevMap, warnings: prevWarn } = indexFragments(prev, skipUnverifiable, "prev");
70
+ const { map: nextMap, warnings: nextWarn } = indexFragments(next, skipUnverifiable, "next");
71
+ warnings.push(...prevWarn, ...nextWarn);
72
+ const changes = [];
73
+ const seen = /* @__PURE__ */ new Set();
74
+ for (const [id, nextFragment] of nextMap) {
75
+ seen.add(id);
76
+ const prevFragment = prevMap.get(id);
77
+ if (!prevFragment) {
78
+ changes.push({
79
+ fragmentId: id,
80
+ kind: "added",
81
+ diff: { after: nextFragment.body },
82
+ affectedDimensions: dedup(nextFragment.dimensionHints),
83
+ url: nextFragment.provenance.url,
84
+ detectedAt: nextFragment.provenance.sourceUpdatedAt
85
+ });
86
+ continue;
87
+ }
88
+ if (prevFragment.bodyHash !== nextFragment.bodyHash) {
89
+ changes.push({
90
+ fragmentId: id,
91
+ kind: "modified",
92
+ diff: { before: prevFragment.body, after: nextFragment.body },
93
+ affectedDimensions: dedup([...prevFragment.dimensionHints, ...nextFragment.dimensionHints]),
94
+ url: nextFragment.provenance.url,
95
+ detectedAt: nextFragment.provenance.sourceUpdatedAt
96
+ });
97
+ }
98
+ }
99
+ for (const [id, prevFragment] of prevMap) {
100
+ if (seen.has(id)) continue;
101
+ changes.push({
102
+ fragmentId: id,
103
+ kind: "removed",
104
+ diff: { before: prevFragment.body },
105
+ affectedDimensions: dedup(prevFragment.dimensionHints),
106
+ url: prevFragment.provenance.url,
107
+ detectedAt: prevFragment.provenance.sourceUpdatedAt
108
+ });
109
+ }
110
+ const filtered = options.filterDimensions ? changes.filter((c) => c.affectedDimensions.some((d) => options.filterDimensions?.includes(d))) : changes;
49
111
  return {
50
- id: stableId("evt", `${input.type}:${input.target ?? ""}:${createdAt}:${JSON.stringify(input.metadata ?? {})}`),
51
- type: input.type,
52
- createdAt,
53
- actor: input.actor,
54
- target: input.target,
55
- metadata: input.metadata
112
+ changes: filtered,
113
+ summary: {
114
+ added: filtered.filter((c) => c.kind === "added").length,
115
+ removed: filtered.filter((c) => c.kind === "removed").length,
116
+ modified: filtered.filter((c) => c.kind === "modified").length
117
+ },
118
+ warnings
56
119
  };
57
120
  }
58
-
59
- // src/kb-store.ts
60
- import { mkdir, readFile, writeFile } from "fs/promises";
61
- import { dirname, join } from "path";
62
- var MemoryKbStore = class {
63
- sources = /* @__PURE__ */ new Map();
64
- pages = /* @__PURE__ */ new Map();
65
- events = [];
66
- index = null;
67
- async putSource(source) {
68
- this.sources.set(source.id, clone(source));
69
- }
70
- async getSource(id) {
71
- return clone(this.sources.get(id) ?? null);
72
- }
73
- async listSources() {
74
- return [...this.sources.values()].map(clone);
75
- }
76
- async putPage(page) {
77
- this.pages.set(page.id, clone(page));
78
- }
79
- async getPage(idOrPath) {
80
- return clone(this.pages.get(idOrPath) ?? [...this.pages.values()].find((page) => page.path === idOrPath) ?? null);
81
- }
82
- async listPages() {
83
- return [...this.pages.values()].map(clone);
84
- }
85
- async putIndex(index) {
86
- this.index = clone(index);
87
- }
88
- async getIndex() {
89
- if (this.index) return clone(this.index);
90
- const pages = await this.listPages();
91
- const sources = await this.listSources();
92
- return { root: "memory", generatedAt: (/* @__PURE__ */ new Date()).toISOString(), sources, pages, graph: buildKnowledgeGraph(pages) };
93
- }
94
- async putEvent(event) {
95
- this.events.push(clone(event));
96
- }
97
- async listEvents(query = {}) {
98
- let out = this.events;
99
- if (query.type) out = out.filter((event) => event.type === query.type);
100
- if (query.target) out = out.filter((event) => event.target === query.target);
101
- out = [...out].sort((a, b) => a.createdAt.localeCompare(b.createdAt));
102
- return out.slice(-(query.limit ?? out.length)).map(clone);
103
- }
104
- };
105
- var FileSystemKbStore = class extends MemoryKbStore {
106
- constructor(dir) {
107
- super();
108
- this.dir = dir;
109
- }
110
- dir;
111
- async putIndex(index) {
112
- await super.putIndex(index);
113
- await writeJson2(join(this.dir, "index.json"), index);
114
- }
115
- async getIndex() {
116
- try {
117
- return JSON.parse(await readFile(join(this.dir, "index.json"), "utf8"));
118
- } catch {
119
- return await super.getIndex();
121
+ function indexFragments(fragments, skipUnverifiable, side) {
122
+ const map = /* @__PURE__ */ new Map();
123
+ const warnings = [];
124
+ let dropped = 0;
125
+ for (const fragment of fragments) {
126
+ if (skipUnverifiable && !fragment.provenance.verifiable) {
127
+ dropped += 1;
128
+ continue;
129
+ }
130
+ if (map.has(fragment.id)) {
131
+ warnings.push(`${side}: duplicate fragment id ${fragment.id} \u2014 keeping last`);
120
132
  }
133
+ map.set(fragment.id, fragment);
121
134
  }
122
- async putEvent(event) {
123
- await super.putEvent(event);
124
- const events = await this.listEvents();
125
- await writeJson2(join(this.dir, "events.json"), events);
135
+ if (dropped > 0) {
136
+ warnings.push(`${side}: dropped ${dropped} unverifiable fragment(s) before diff`);
126
137
  }
127
- };
128
- async function writeJson2(path, value) {
129
- await mkdir(dirname(path), { recursive: true });
130
- await writeFile(path, JSON.stringify(value, null, 2) + "\n", "utf8");
138
+ return { map, warnings };
131
139
  }
132
- function clone(value) {
133
- return value == null ? value : JSON.parse(JSON.stringify(value));
134
- }
135
-
136
- // src/discovery.ts
137
- function createLocalDiscoveryDispatcher(worker) {
138
- return {
139
- async dispatch(tasks, options = {}) {
140
- const concurrency = Math.max(1, options.concurrency ?? 4);
141
- const results = [];
142
- let cursor = 0;
143
- async function runNext() {
144
- while (cursor < tasks.length) {
145
- if (options.signal?.aborted) throw new Error("Discovery dispatch aborted");
146
- const task = tasks[cursor++];
147
- results.push(await worker.run(task, options.signal));
148
- }
149
- }
150
- await Promise.all(Array.from({ length: Math.min(concurrency, tasks.length) }, runNext));
151
- return results.sort((a, b) => tasks.findIndex((task) => task.id === a.taskId) - tasks.findIndex((task) => task.id === b.taskId));
152
- }
153
- };
140
+ function dedup(items) {
141
+ return [...new Set(items)];
154
142
  }
155
143
 
156
144
  // src/chunking.ts
@@ -197,12 +185,17 @@ function splitSections(body, bodyOffset) {
197
185
  const lines = body.split("\n");
198
186
  const sections = [];
199
187
  const headings = {};
200
- let current = { lines: [], start: bodyOffset, headingPath: "" };
188
+ let current = {
189
+ lines: [],
190
+ start: bodyOffset,
191
+ headingPath: ""
192
+ };
201
193
  let cursor = bodyOffset;
202
194
  let fence = null;
203
195
  const flush = () => {
204
196
  const text = current.lines.join("\n");
205
- if (text.trim() !== "") sections.push({ text, start: current.start, headingPath: current.headingPath });
197
+ if (text.trim() !== "")
198
+ sections.push({ text, start: current.start, headingPath: current.headingPath });
206
199
  };
207
200
  for (let i = 0; i < lines.length; i++) {
208
201
  const line = lines[i];
@@ -282,66 +275,25 @@ ${chunk.text}`;
282
275
  return out;
283
276
  }
284
277
 
285
- // src/optimization.ts
286
- import {
287
- runMultiShotOptimization
288
- } from "@tangle-network/agent-eval";
289
- async function runKnowledgeBaseOptimization(config) {
290
- return runMultiShotOptimization({
291
- ...config,
292
- target: config.target ?? "agent-knowledge-base"
293
- });
294
- }
295
- function knowledgeVariantFromCandidate(candidate, options = {}) {
278
+ // src/discovery.ts
279
+ function createLocalDiscoveryDispatcher(worker) {
296
280
  return {
297
- id: options.id ?? candidate.id,
298
- label: options.label ?? candidate.id,
299
- generation: options.generation ?? 0,
300
- payload: candidate
301
- };
302
- }
303
-
304
- // src/release.ts
305
- import {
306
- evaluateReleaseConfidence,
307
- releaseTraceEvidenceFromMultiShotTrials,
308
- validateRunRecord
309
- } from "@tangle-network/agent-eval";
310
- function knowledgeReleaseReportFromOptimization(result, options = {}) {
311
- const trials = result.evolution.generations.flatMap((generation) => generation.trials);
312
- const traceEvidence = releaseTraceEvidenceFromMultiShotTrials(trials);
313
- const runRecords = (options.runRecords ?? [
314
- ...result.gate?.candidateRuns ?? [],
315
- ...result.gate?.baselineRuns ?? []
316
- ]).map(validateRunRecord);
317
- const scorecard = evaluateReleaseConfidence({
318
- target: "agent-knowledge-base",
319
- candidateId: result.promotedVariant.id,
320
- baselineId: "baseline",
321
- traces: traceEvidence,
322
- runs: runRecords,
323
- gateDecision: result.gate?.decision ?? null,
324
- thresholds: {
325
- requireCorpus: false,
326
- requireHoldout: Boolean(result.gate),
327
- minHoldoutRuns: result.gate ? 1 : 0,
328
- minSearchRuns: 1,
329
- minMeanScore: options.minScore ?? 0.7
281
+ async dispatch(tasks, options = {}) {
282
+ const concurrency = Math.max(1, options.concurrency ?? 4);
283
+ const results = [];
284
+ let cursor = 0;
285
+ async function runNext() {
286
+ while (cursor < tasks.length) {
287
+ if (options.signal?.aborted) throw new Error("Discovery dispatch aborted");
288
+ const task = tasks[cursor++];
289
+ results.push(await worker.run(task, options.signal));
290
+ }
291
+ }
292
+ await Promise.all(Array.from({ length: Math.min(concurrency, tasks.length) }, runNext));
293
+ return results.sort(
294
+ (a, b) => tasks.findIndex((task) => task.id === a.taskId) - tasks.findIndex((task) => task.id === b.taskId)
295
+ );
330
296
  }
331
- });
332
- const release = {
333
- id: stableId("krel", `${result.promotedVariant.id}:${options.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()}`),
334
- candidateId: result.promotedVariant.id,
335
- createdAt: options.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
336
- promoted: scorecard.status !== "fail" && result.promotedVariant.id === result.searchBestVariant.id,
337
- scorecard,
338
- runRecordIds: runRecords.map((record) => record.runId)
339
- };
340
- return {
341
- release,
342
- scorecard,
343
- candidateRuns: result.gate?.candidateRuns ?? [],
344
- baselineRuns: result.gate?.baselineRuns ?? []
345
297
  };
346
298
  }
347
299
 
@@ -351,6 +303,22 @@ import {
351
303
  scoreKnowledgeReadiness,
352
304
  userQuestionsForKnowledgeGaps
353
305
  } from "@tangle-network/agent-eval";
306
+ var READINESS_SPEC_DEFAULTS = {
307
+ category: "domain_specific",
308
+ acquisitionMode: "search_web",
309
+ importance: "high",
310
+ freshness: "monthly",
311
+ sensitivity: "public",
312
+ confidenceNeeded: 0.7,
313
+ minSources: 1,
314
+ minHits: 2
315
+ };
316
+ function defineReadinessSpec(input) {
317
+ return {
318
+ ...READINESS_SPEC_DEFAULTS,
319
+ ...input
320
+ };
321
+ }
354
322
  function buildEvalKnowledgeBundle(options) {
355
323
  const searchLimit = options.searchLimit ?? 5;
356
324
  const now = options.now ?? /* @__PURE__ */ new Date();
@@ -366,7 +334,11 @@ function buildEvalKnowledgeBundle(options) {
366
334
  userAnswers: options.userAnswers,
367
335
  evidenceIds: requirements.flatMap((requirement) => requirement.evidenceIds),
368
336
  claimIds: [],
369
- wikiPageIds: unique(requirements.flatMap((requirement) => pageIdsFromResults(searchResultsByRequirement[requirement.id] ?? []))),
337
+ wikiPageIds: unique(
338
+ requirements.flatMap(
339
+ (requirement) => pageIdsFromResults(searchResultsByRequirement[requirement.id] ?? [])
340
+ )
341
+ ),
370
342
  metadata: options.metadata
371
343
  });
372
344
  const questions = userQuestionsForKnowledgeGaps(report.blockingMissingRequirements);
@@ -423,7 +395,9 @@ function requirementFromSearch(index, spec, results, now) {
423
395
  }
424
396
  function sourceFreshness(sources, now) {
425
397
  if (sources.length === 0) return { score: 0, expiredSourceIds: [] };
426
- const validUntilValues = sources.map((source) => source.validUntil ?? stringMetadata(source.metadata, "validUntil") ?? stringMetadata(source.metadata, "expiresAt")).filter(isIsoDate);
398
+ const validUntilValues = sources.map(
399
+ (source) => source.validUntil ?? stringMetadata(source.metadata, "validUntil") ?? stringMetadata(source.metadata, "expiresAt")
400
+ ).filter(isIsoDate);
427
401
  const lastVerifiedValues = sources.map((source) => source.lastVerifiedAt ?? stringMetadata(source.metadata, "lastVerifiedAt")).filter(isIsoDate);
428
402
  const expiredSourceIds = sources.filter((source) => {
429
403
  const validUntil = source.validUntil ?? stringMetadata(source.metadata, "validUntil") ?? stringMetadata(source.metadata, "expiresAt");
@@ -458,32 +432,657 @@ function unique(items) {
458
432
  function round(value) {
459
433
  return Math.round(value * 1e3) / 1e3;
460
434
  }
435
+
436
+ // src/events.ts
437
+ function createKnowledgeEvent(input) {
438
+ const createdAt = (input.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
439
+ return {
440
+ id: stableId(
441
+ "evt",
442
+ `${input.type}:${input.target ?? ""}:${createdAt}:${JSON.stringify(input.metadata ?? {})}`
443
+ ),
444
+ type: input.type,
445
+ createdAt,
446
+ actor: input.actor,
447
+ target: input.target,
448
+ metadata: input.metadata
449
+ };
450
+ }
451
+
452
+ // src/freshness.ts
453
+ import { mkdir, readFile, writeFile } from "fs/promises";
454
+ import { dirname, join } from "path";
455
+ function createFileSystemFreshnessStore(options) {
456
+ const path = join(options.root, ".agent-knowledge", "freshness.json");
457
+ let writeQueue = Promise.resolve();
458
+ const read = async () => {
459
+ try {
460
+ const text = await readFile(path, "utf8");
461
+ const parsed = JSON.parse(text);
462
+ return parsed.records ?? {};
463
+ } catch {
464
+ return {};
465
+ }
466
+ };
467
+ const write = async (records) => {
468
+ await mkdir(dirname(path), { recursive: true });
469
+ await writeFile(path, `${JSON.stringify({ records }, null, 2)}
470
+ `, "utf8");
471
+ };
472
+ return {
473
+ async last(key) {
474
+ const records = await read();
475
+ const record = records[buildKey(key)];
476
+ return record ? new Date(record.lastRefreshedAt) : null;
477
+ },
478
+ async mark(input) {
479
+ writeQueue = writeQueue.then(async () => {
480
+ const records = await read();
481
+ records[buildKey(input)] = {
482
+ workspaceId: input.workspaceId,
483
+ sourceId: input.sourceId,
484
+ lastRefreshedAt: input.when.toISOString(),
485
+ contentHash: input.contentHash
486
+ };
487
+ await write(records);
488
+ });
489
+ await writeQueue;
490
+ },
491
+ async stale(input) {
492
+ const last = await this.last(input);
493
+ if (!last) return true;
494
+ const now = input.now ?? /* @__PURE__ */ new Date();
495
+ return now.getTime() - last.getTime() > input.ttlMs;
496
+ },
497
+ async list(workspaceId) {
498
+ const records = await read();
499
+ return Object.values(records).filter((r) => r.workspaceId === workspaceId);
500
+ }
501
+ };
502
+ }
503
+ function createD1FreshnessStoreStub(adapter) {
504
+ return {
505
+ async last(key) {
506
+ const record = await adapter.get(key.workspaceId, key.sourceId);
507
+ return record ? new Date(record.lastRefreshedAt) : null;
508
+ },
509
+ async mark(input) {
510
+ await adapter.upsert({
511
+ workspaceId: input.workspaceId,
512
+ sourceId: input.sourceId,
513
+ lastRefreshedAt: input.when.toISOString(),
514
+ contentHash: input.contentHash
515
+ });
516
+ },
517
+ async stale(input) {
518
+ const last = await this.last(input);
519
+ if (!last) return true;
520
+ const now = input.now ?? /* @__PURE__ */ new Date();
521
+ return now.getTime() - last.getTime() > input.ttlMs;
522
+ },
523
+ async list(workspaceId) {
524
+ return adapter.listByWorkspace(workspaceId);
525
+ }
526
+ };
527
+ }
528
+ function buildKey(key) {
529
+ return `${key.workspaceId}::${key.sourceId}`;
530
+ }
531
+
532
+ // src/kb-store.ts
533
+ import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
534
+ import { dirname as dirname2, join as join2 } from "path";
535
+ var MemoryKbStore = class {
536
+ sources = /* @__PURE__ */ new Map();
537
+ pages = /* @__PURE__ */ new Map();
538
+ events = [];
539
+ index = null;
540
+ async putSource(source) {
541
+ this.sources.set(source.id, clone(source));
542
+ }
543
+ async getSource(id) {
544
+ return clone(this.sources.get(id) ?? null);
545
+ }
546
+ async listSources() {
547
+ return [...this.sources.values()].map(clone);
548
+ }
549
+ async putPage(page) {
550
+ this.pages.set(page.id, clone(page));
551
+ }
552
+ async getPage(idOrPath) {
553
+ return clone(
554
+ this.pages.get(idOrPath) ?? [...this.pages.values()].find((page) => page.path === idOrPath) ?? null
555
+ );
556
+ }
557
+ async listPages() {
558
+ return [...this.pages.values()].map(clone);
559
+ }
560
+ async putIndex(index) {
561
+ this.index = clone(index);
562
+ }
563
+ async getIndex() {
564
+ if (this.index) return clone(this.index);
565
+ const pages = await this.listPages();
566
+ const sources = await this.listSources();
567
+ return {
568
+ root: "memory",
569
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
570
+ sources,
571
+ pages,
572
+ graph: buildKnowledgeGraph(pages)
573
+ };
574
+ }
575
+ async putEvent(event) {
576
+ this.events.push(clone(event));
577
+ }
578
+ async listEvents(query = {}) {
579
+ let out = this.events;
580
+ if (query.type) out = out.filter((event) => event.type === query.type);
581
+ if (query.target) out = out.filter((event) => event.target === query.target);
582
+ out = [...out].sort((a, b) => a.createdAt.localeCompare(b.createdAt));
583
+ return out.slice(-(query.limit ?? out.length)).map(clone);
584
+ }
585
+ };
586
+ var FileSystemKbStore = class extends MemoryKbStore {
587
+ constructor(dir) {
588
+ super();
589
+ this.dir = dir;
590
+ }
591
+ dir;
592
+ async putIndex(index) {
593
+ await super.putIndex(index);
594
+ await writeJson2(join2(this.dir, "index.json"), index);
595
+ }
596
+ async getIndex() {
597
+ try {
598
+ return JSON.parse(await readFile2(join2(this.dir, "index.json"), "utf8"));
599
+ } catch {
600
+ return await super.getIndex();
601
+ }
602
+ }
603
+ async putEvent(event) {
604
+ await super.putEvent(event);
605
+ const events = await this.listEvents();
606
+ await writeJson2(join2(this.dir, "events.json"), events);
607
+ }
608
+ };
609
+ async function writeJson2(path, value) {
610
+ await mkdir2(dirname2(path), { recursive: true });
611
+ await writeFile2(path, `${JSON.stringify(value, null, 2)}
612
+ `, "utf8");
613
+ }
614
+ function clone(value) {
615
+ return value == null ? value : JSON.parse(JSON.stringify(value));
616
+ }
617
+
618
+ // src/optimization.ts
619
+ import {
620
+ runMultiShotOptimization
621
+ } from "@tangle-network/agent-eval";
622
+ async function runKnowledgeBaseOptimization(config) {
623
+ return runMultiShotOptimization({
624
+ ...config,
625
+ target: config.target ?? "agent-knowledge-base"
626
+ });
627
+ }
628
+ function knowledgeVariantFromCandidate(candidate, options = {}) {
629
+ return {
630
+ id: options.id ?? candidate.id,
631
+ label: options.label ?? candidate.id,
632
+ generation: options.generation ?? 0,
633
+ payload: candidate
634
+ };
635
+ }
636
+
637
+ // src/propose-from-finding.ts
638
+ var KnowledgeProposalParseError = class extends Error {
639
+ constructor(findingId, subject, message) {
640
+ super(`proposeFromFinding(${findingId}, subject=${subject}): ${message}`);
641
+ this.findingId = findingId;
642
+ this.subject = subject;
643
+ this.name = "KnowledgeProposalParseError";
644
+ }
645
+ findingId;
646
+ subject;
647
+ };
648
+ function proposeFromFinding(finding) {
649
+ if (!finding.subject) return null;
650
+ if (!finding.subject.startsWith("agent-knowledge:")) return null;
651
+ const rest = finding.subject.slice("agent-knowledge:".length);
652
+ const [kindPart, ...locusParts] = rest.split(":");
653
+ const locus = locusParts.join(":");
654
+ if (!kindPart || !locus) {
655
+ throw new KnowledgeProposalParseError(
656
+ finding.finding_id,
657
+ finding.subject,
658
+ "expected `agent-knowledge:<kind>:<locus>` shape"
659
+ );
660
+ }
661
+ const baseMeta = {
662
+ severity: finding.severity,
663
+ confidence: finding.confidence,
664
+ evidence_uri: finding.evidence_refs[0]?.uri,
665
+ analyst_id: finding.analyst_id
666
+ };
667
+ switch (kindPart) {
668
+ case "wiki":
669
+ return wikiProposal(finding, locus, baseMeta);
670
+ case "claim":
671
+ return claimProposal(finding, locus, baseMeta);
672
+ case "raw":
673
+ return liftRawProposal(finding, locus, baseMeta);
674
+ case "stale":
675
+ return markStaleProposal(finding, locus, baseMeta);
676
+ default:
677
+ throw new KnowledgeProposalParseError(
678
+ finding.finding_id,
679
+ finding.subject,
680
+ `unknown kind "${kindPart}" (expected one of: wiki | claim | raw | stale)`
681
+ );
682
+ }
683
+ }
684
+ function wikiProposal(finding, locus, metadata) {
685
+ const hashIdx = locus.indexOf("#");
686
+ const pageSlug = hashIdx >= 0 ? locus.slice(0, hashIdx) : locus;
687
+ const heading = hashIdx >= 0 ? locus.slice(hashIdx + 1) : null;
688
+ const path = `knowledge/${ensureSlug(pageSlug)}.md`;
689
+ const body = renderWikiBody(finding, pageSlug, heading);
690
+ return {
691
+ id: `prop-${finding.finding_id}`,
692
+ sourceFindingId: finding.finding_id,
693
+ kind: heading ? "append-section" : "create-page",
694
+ locus: pageSlug,
695
+ writeBlocks: [{ path, content: body }],
696
+ metadata
697
+ };
698
+ }
699
+ function claimProposal(finding, locus, metadata) {
700
+ const refs = finding.evidence_refs.filter((r) => r.uri).map((r) => ({
701
+ sourceId: `analyst-finding:${finding.finding_id}`,
702
+ anchorId: r.uri,
703
+ quote: r.excerpt
704
+ }));
705
+ const claim = {
706
+ id: `claim-${finding.finding_id}`,
707
+ text: finding.recommended_action ?? finding.claim,
708
+ refs,
709
+ confidence: finding.confidence,
710
+ status: "draft",
711
+ metadata: {
712
+ analyst_id: finding.analyst_id,
713
+ source_finding_id: finding.finding_id,
714
+ topic: locus
715
+ }
716
+ };
717
+ return {
718
+ id: `prop-${finding.finding_id}`,
719
+ sourceFindingId: finding.finding_id,
720
+ kind: "create-claim",
721
+ locus,
722
+ writeBlocks: [],
723
+ claim,
724
+ metadata
725
+ };
726
+ }
727
+ function liftRawProposal(finding, sourceId, metadata) {
728
+ const path = `knowledge/${ensureSlug(sourceId)}.md`;
729
+ const body = [
730
+ "---",
731
+ `title: ${sourceId}`,
732
+ `source: ${sourceId}`,
733
+ `status: draft`,
734
+ `lifted_from_finding: ${finding.finding_id}`,
735
+ "---",
736
+ "",
737
+ "## Why this page exists",
738
+ "",
739
+ finding.claim,
740
+ "",
741
+ ...finding.rationale ? ["## Rationale", "", finding.rationale, ""] : [],
742
+ ...finding.recommended_action ? ["## Recommended action", "", finding.recommended_action, ""] : []
743
+ ].join("\n");
744
+ return {
745
+ id: `prop-${finding.finding_id}`,
746
+ sourceFindingId: finding.finding_id,
747
+ kind: "lift-raw",
748
+ locus: sourceId,
749
+ writeBlocks: [{ path, content: body }],
750
+ metadata
751
+ };
752
+ }
753
+ function markStaleProposal(finding, pageSlug, metadata) {
754
+ const path = `knowledge/${ensureSlug(pageSlug)}.stale.md`;
755
+ const body = [
756
+ "---",
757
+ `title: ${pageSlug} (marked stale)`,
758
+ `status: superseded`,
759
+ `superseded_by_finding: ${finding.finding_id}`,
760
+ `confidence: ${finding.confidence}`,
761
+ "---",
762
+ "",
763
+ "## Why marked stale",
764
+ "",
765
+ finding.claim,
766
+ "",
767
+ ...finding.rationale ? ["## Evidence", "", finding.rationale, ""] : [],
768
+ ...finding.recommended_action ? ["## Action", "", finding.recommended_action, ""] : []
769
+ ].join("\n");
770
+ return {
771
+ id: `prop-${finding.finding_id}`,
772
+ sourceFindingId: finding.finding_id,
773
+ kind: "mark-stale",
774
+ locus: pageSlug,
775
+ writeBlocks: [{ path, content: body }],
776
+ metadata
777
+ };
778
+ }
779
+ function proposeFromFindings(findings) {
780
+ const proposals = [];
781
+ const errors = [];
782
+ let skipped = 0;
783
+ for (const f of findings) {
784
+ try {
785
+ const p = proposeFromFinding(f);
786
+ if (p) proposals.push(p);
787
+ else skipped += 1;
788
+ } catch (err) {
789
+ if (err instanceof KnowledgeProposalParseError) errors.push(err);
790
+ else throw err;
791
+ }
792
+ }
793
+ return { proposals, skipped, errors };
794
+ }
795
+ function ensureSlug(s) {
796
+ return s.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 200) || "untitled";
797
+ }
798
+ function renderWikiBody(finding, slug, heading) {
799
+ const title = humanize(slug);
800
+ if (heading) {
801
+ return [
802
+ `## ${heading}`,
803
+ "",
804
+ finding.claim,
805
+ "",
806
+ ...finding.rationale ? ["### Rationale", "", finding.rationale, ""] : [],
807
+ ...finding.recommended_action ? ["### Action", "", finding.recommended_action, ""] : [],
808
+ `_Drafted from finding ${finding.finding_id} (confidence ${finding.confidence.toFixed(2)})._`
809
+ ].join("\n");
810
+ }
811
+ return [
812
+ "---",
813
+ `title: ${title}`,
814
+ `status: draft`,
815
+ `drafted_from_finding: ${finding.finding_id}`,
816
+ `confidence: ${finding.confidence}`,
817
+ "---",
818
+ "",
819
+ `# ${title}`,
820
+ "",
821
+ finding.claim,
822
+ "",
823
+ ...finding.rationale ? ["## Rationale", "", finding.rationale, ""] : [],
824
+ ...finding.recommended_action ? ["## Recommended action", "", finding.recommended_action, ""] : []
825
+ ].join("\n");
826
+ }
827
+ function humanize(slug) {
828
+ return slug.split("-").filter(Boolean).map((w) => w[0]?.toUpperCase() + w.slice(1)).join(" ") || slug;
829
+ }
830
+
831
+ // src/release.ts
832
+ import {
833
+ evaluateReleaseConfidence,
834
+ releaseTraceEvidenceFromMultiShotTrials,
835
+ validateRunRecord
836
+ } from "@tangle-network/agent-eval";
837
+ function knowledgeReleaseReportFromOptimization(result, options = {}) {
838
+ const trials = result.evolution.generations.flatMap(
839
+ (generation) => generation.trials
840
+ );
841
+ const traceEvidence = releaseTraceEvidenceFromMultiShotTrials(trials);
842
+ const runRecords = (options.runRecords ?? [
843
+ ...result.gate?.candidateRuns ?? [],
844
+ ...result.gate?.baselineRuns ?? []
845
+ ]).map(validateRunRecord);
846
+ const scorecard = evaluateReleaseConfidence({
847
+ target: "agent-knowledge-base",
848
+ candidateId: result.promotedVariant.id,
849
+ baselineId: "baseline",
850
+ traces: traceEvidence,
851
+ runs: runRecords,
852
+ gateDecision: result.gate?.decision ?? null,
853
+ thresholds: {
854
+ requireCorpus: false,
855
+ requireHoldout: Boolean(result.gate),
856
+ minHoldoutRuns: result.gate ? 1 : 0,
857
+ minSearchRuns: 1,
858
+ minMeanScore: options.minScore ?? 0.7
859
+ }
860
+ });
861
+ const release = {
862
+ id: stableId(
863
+ "krel",
864
+ `${result.promotedVariant.id}:${options.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()}`
865
+ ),
866
+ candidateId: result.promotedVariant.id,
867
+ createdAt: options.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
868
+ promoted: scorecard.status !== "fail" && result.promotedVariant.id === result.searchBestVariant.id,
869
+ scorecard,
870
+ runRecordIds: runRecords.map((record) => record.runId)
871
+ };
872
+ return {
873
+ release,
874
+ scorecard,
875
+ candidateRuns: result.gate?.candidateRuns ?? [],
876
+ baselineRuns: result.gate?.baselineRuns ?? []
877
+ };
878
+ }
879
+
880
+ // src/research-loop.ts
881
+ import {
882
+ blockingKnowledgeEval,
883
+ objectiveEval
884
+ } from "@tangle-network/agent-eval";
885
+ function createKnowledgeControlLoopAdapter(options) {
886
+ let initialized = false;
887
+ const appliedSteps = [];
888
+ return {
889
+ intent: options.goal,
890
+ async observe({ history, abortSignal }) {
891
+ if (abortSignal.aborted) throw new Error("Knowledge control loop aborted");
892
+ if (!initialized) {
893
+ await initKnowledgeBase(options.root);
894
+ initialized = true;
895
+ }
896
+ const index = await buildKnowledgeIndex(options.root);
897
+ const validation = validateKnowledgeIndex(index, { strict: options.strict });
898
+ const lintFindings = lintKnowledgeIndex(index);
899
+ const readiness = readinessFor(options, index);
900
+ return {
901
+ root: options.root,
902
+ goal: options.goal,
903
+ iteration: history.length + 1,
904
+ index,
905
+ lintFindings,
906
+ validation,
907
+ readiness,
908
+ previousSteps: [...appliedSteps],
909
+ signal: abortSignal
910
+ };
911
+ },
912
+ validate({ state }) {
913
+ const errorFindings = state.validation.findings.filter(
914
+ (finding) => finding.severity === "error"
915
+ );
916
+ const evals = [
917
+ objectiveEval({
918
+ id: "knowledge-valid",
919
+ passed: state.validation.ok,
920
+ severity: "critical",
921
+ detail: state.validation.ok ? "Knowledge index is valid." : "Knowledge index has validation errors.",
922
+ metadata: { findings: state.validation.findings }
923
+ }),
924
+ objectiveEval({
925
+ id: "knowledge-lint-errors",
926
+ passed: errorFindings.length === 0,
927
+ severity: "error",
928
+ detail: errorFindings.length === 0 ? "No lint errors." : `${errorFindings.length} lint error(s).`,
929
+ metadata: { findings: errorFindings }
930
+ })
931
+ ];
932
+ if (state.readiness) evals.push(blockingKnowledgeEval(state.readiness.report));
933
+ return evals;
934
+ },
935
+ shouldStop() {
936
+ return { stop: false, pass: false, reason: "knowledge driver owns stop decisions" };
937
+ },
938
+ async act(action, ctx) {
939
+ const step = await applyKnowledgeResearchDecision(options, action, ctx.state.iteration);
940
+ appliedSteps.push(step);
941
+ return step;
942
+ }
943
+ };
944
+ }
945
+ async function runKnowledgeResearchLoop(options) {
946
+ const maxIterations = Math.max(1, options.maxIterations ?? 3);
947
+ await initKnowledgeBase(options.root);
948
+ const steps = [];
949
+ let index = await buildKnowledgeIndex(options.root);
950
+ let validation = validateKnowledgeIndex(index, { strict: options.strict });
951
+ let lintFindings = lintKnowledgeIndex(index);
952
+ let readiness = readinessFor(options, index);
953
+ let done = false;
954
+ for (let iteration = 1; iteration <= maxIterations; iteration++) {
955
+ if (options.signal?.aborted) throw new Error("Knowledge research loop aborted");
956
+ const decision = await options.step({
957
+ root: options.root,
958
+ goal: options.goal,
959
+ iteration,
960
+ index,
961
+ lintFindings,
962
+ validation,
963
+ readiness,
964
+ previousSteps: steps,
965
+ signal: options.signal
966
+ });
967
+ done = Boolean(decision.done);
968
+ const step = await applyKnowledgeResearchDecision(options, decision, iteration);
969
+ index = await buildKnowledgeIndex(options.root);
970
+ validation = step.validation;
971
+ lintFindings = step.lintFindings;
972
+ readiness = step.readiness;
973
+ steps.push(step);
974
+ await options.onStep?.(step);
975
+ if (done) break;
976
+ if (!step.applied && step.addedSources.length === 0) break;
977
+ }
978
+ return {
979
+ root: options.root,
980
+ goal: options.goal,
981
+ iterations: steps.length,
982
+ done,
983
+ index,
984
+ lintFindings,
985
+ validation,
986
+ readiness,
987
+ steps
988
+ };
989
+ }
990
+ async function applyKnowledgeResearchDecision(options, decision, iteration = 1) {
991
+ const addedSources = [];
992
+ for (const sourcePath of decision.sourcePaths ?? []) {
993
+ addedSources.push(...await addSourcePath(options.root, sourcePath, options.sourceOptions));
994
+ }
995
+ for (const sourceText of decision.sourceTexts ?? []) {
996
+ addedSources.push(await addSourceText(options.root, sourceText, options.sourceOptions));
997
+ }
998
+ const applied = decision.proposalText ? await applyKnowledgeWriteBlocks(options.root, decision.proposalText) : void 0;
999
+ const index = await buildKnowledgeIndex(options.root);
1000
+ const validation = validateKnowledgeIndex(index, { strict: options.strict });
1001
+ const lintFindings = lintKnowledgeIndex(index);
1002
+ const readiness = readinessFor(options, index);
1003
+ const done = Boolean(decision.done);
1004
+ const event = createKnowledgeEvent({
1005
+ type: "research.iteration",
1006
+ actor: options.actor,
1007
+ target: options.root,
1008
+ metadata: {
1009
+ goal: options.goal,
1010
+ iteration,
1011
+ done,
1012
+ addedSourceCount: addedSources.length,
1013
+ written: applied?.written,
1014
+ warningCount: applied?.warnings.length ?? 0,
1015
+ errorCount: validation.findings.filter((finding) => finding.severity === "error").length
1016
+ }
1017
+ });
1018
+ return {
1019
+ iteration,
1020
+ notes: decision.notes,
1021
+ addedSources,
1022
+ applied,
1023
+ lintFindings,
1024
+ validation,
1025
+ readiness,
1026
+ event,
1027
+ done,
1028
+ metadata: decision.metadata
1029
+ };
1030
+ }
1031
+ function readinessFor(options, index) {
1032
+ if (!options.readinessSpecs?.length) return void 0;
1033
+ return buildEvalKnowledgeBundle({
1034
+ ...options.readiness ?? {},
1035
+ taskId: options.readinessTaskId ?? options.goal,
1036
+ index,
1037
+ specs: options.readinessSpecs
1038
+ });
1039
+ }
461
1040
  export {
462
1041
  FileSystemKbStore,
1042
+ IRS_DIMENSION_HINTS,
463
1043
  KnowledgeBaseCandidateSchema,
464
1044
  KnowledgeEventSchema,
465
1045
  KnowledgeGraphEdgeSchema,
466
1046
  KnowledgeGraphNodeSchema,
467
1047
  KnowledgeIndexSchema,
468
1048
  KnowledgePageSchema,
1049
+ KnowledgeProposalParseError,
1050
+ MAX_RESPONSE_BYTES,
1051
+ MIN_REQUEST_GAP_MS,
469
1052
  MemoryKbStore,
1053
+ POLITE_USER_AGENT,
1054
+ READINESS_SPEC_DEFAULTS,
470
1055
  SCAFFOLD_PAGE_BASENAMES,
471
1056
  SourceAnchorSchema,
472
1057
  SourceRecordSchema,
473
1058
  WIKILINK_REGEX,
1059
+ __resetHttpThrottle,
474
1060
  addSourcePath,
1061
+ addSourceText,
475
1062
  applyKnowledgeWriteBlocks,
476
1063
  applyKnowledgeWriteBlocksFile,
477
1064
  buildEvalKnowledgeBundle,
478
1065
  buildKnowledgeGraph,
479
1066
  buildKnowledgeIndex,
480
1067
  chunkMarkdown,
1068
+ createCornellLiiSource,
1069
+ createD1FreshnessStoreStub,
1070
+ createFileSystemFreshnessStore,
1071
+ createIrsPublicationsSource,
1072
+ createKnowledgeControlLoopAdapter,
481
1073
  createKnowledgeEvent,
482
1074
  createLocalDiscoveryDispatcher,
1075
+ createStateSosSource,
1076
+ defineReadinessSpec,
1077
+ detectChanges,
483
1078
  explainKnowledgeTarget,
1079
+ extractLinks,
484
1080
  extractWikilinks,
1081
+ firstMatch,
485
1082
  formatFrontmatter,
1083
+ htmlToText,
486
1084
  initKnowledgeBase,
1085
+ innerHtmlById,
487
1086
  inspectKnowledgeIndex,
488
1087
  isSafeKnowledgePath,
489
1088
  isScaffoldPath,
@@ -493,12 +1092,17 @@ export {
493
1092
  lintKnowledgeIndex,
494
1093
  loadKnowledgePages,
495
1094
  loadSourceRegistry,
1095
+ looksLikeBlockPage,
496
1096
  mediaTypeFor,
497
1097
  normalizeLinkTarget,
498
1098
  parseFrontmatter,
499
1099
  parseKnowledgeWriteBlocks,
1100
+ politeFetch,
1101
+ proposeFromFinding,
1102
+ proposeFromFindings,
500
1103
  reciprocalRankFusion,
501
1104
  runKnowledgeBaseOptimization,
1105
+ runKnowledgeResearchLoop,
502
1106
  searchKnowledge,
503
1107
  sha256,
504
1108
  slugify,