@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/AGENTS.md +36 -0
- package/README.md +182 -0
- package/dist/{chunk-TXNYP4WI.js → chunk-4PNXQ2NT.js} +1 -1
- package/dist/chunk-4PNXQ2NT.js.map +1 -0
- package/dist/{chunk-JWGMOXAT.js → chunk-HKYD765Q.js} +599 -427
- package/dist/chunk-HKYD765Q.js.map +1 -0
- package/dist/chunk-WCYW2GDA.js +551 -0
- package/dist/chunk-WCYW2GDA.js.map +1 -0
- package/dist/chunk-YMKHCTS2.js +19 -0
- package/dist/chunk-YMKHCTS2.js.map +1 -0
- package/dist/cli.js +77 -34
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +705 -263
- package/dist/index.js +771 -167
- package/dist/index.js.map +1 -1
- package/dist/sources/index.d.ts +386 -0
- package/dist/sources/index.js +34 -0
- package/dist/sources/index.js.map +1 -0
- package/dist/{types-YkFkfwJG.d.ts → types-CAeh7Lwb.d.ts} +2 -2
- package/dist/viz/index.d.ts +1 -1
- package/dist/viz/index.js +1 -1
- package/package.json +11 -3
- package/dist/chunk-JWGMOXAT.js.map +0 -1
- package/dist/chunk-TXNYP4WI.js.map +0 -1
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-
|
|
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/
|
|
47
|
-
function
|
|
48
|
-
const
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
123
|
-
|
|
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
|
|
133
|
-
return
|
|
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 = {
|
|
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() !== "")
|
|
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/
|
|
286
|
-
|
|
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
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
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(
|
|
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(
|
|
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,
|