@tangle-network/agent-knowledge 1.2.0 → 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 +95 -0
- package/dist/{chunk-TXNYP4WI.js → chunk-4PNXQ2NT.js} +1 -1
- package/dist/chunk-4PNXQ2NT.js.map +1 -0
- package/dist/{chunk-JLCQ6O7W.js → chunk-HKYD765Q.js} +574 -441
- 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 +686 -406
- package/dist/index.js +591 -168
- 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-DTUp66Gr.d.ts → types-CAeh7Lwb.d.ts} +1 -1
- package/dist/viz/index.d.ts +1 -1
- package/dist/viz/index.js +1 -1
- package/package.json +18 -12
- package/dist/chunk-JLCQ6O7W.js.map +0 -1
- package/dist/chunk-TXNYP4WI.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -32,126 +32,113 @@ import {
|
|
|
32
32
|
parseKnowledgeWriteBlocks,
|
|
33
33
|
reciprocalRankFusion,
|
|
34
34
|
searchKnowledge,
|
|
35
|
-
sha256,
|
|
36
|
-
slugify,
|
|
37
35
|
sourceRegistryPath,
|
|
38
|
-
stableId,
|
|
39
36
|
textSourceAdapter,
|
|
40
37
|
tokenizeQuery,
|
|
41
38
|
validateKnowledgeIndex,
|
|
42
39
|
writeJson,
|
|
43
40
|
writeKnowledgeIndex,
|
|
44
41
|
writeSourceRegistry
|
|
45
|
-
} 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";
|
|
46
64
|
|
|
47
|
-
// src/
|
|
48
|
-
function
|
|
49
|
-
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;
|
|
50
111
|
return {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
|
57
119
|
};
|
|
58
120
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
async getSource(id) {
|
|
72
|
-
return clone(this.sources.get(id) ?? null);
|
|
73
|
-
}
|
|
74
|
-
async listSources() {
|
|
75
|
-
return [...this.sources.values()].map(clone);
|
|
76
|
-
}
|
|
77
|
-
async putPage(page) {
|
|
78
|
-
this.pages.set(page.id, clone(page));
|
|
79
|
-
}
|
|
80
|
-
async getPage(idOrPath) {
|
|
81
|
-
return clone(this.pages.get(idOrPath) ?? [...this.pages.values()].find((page) => page.path === idOrPath) ?? null);
|
|
82
|
-
}
|
|
83
|
-
async listPages() {
|
|
84
|
-
return [...this.pages.values()].map(clone);
|
|
85
|
-
}
|
|
86
|
-
async putIndex(index) {
|
|
87
|
-
this.index = clone(index);
|
|
88
|
-
}
|
|
89
|
-
async getIndex() {
|
|
90
|
-
if (this.index) return clone(this.index);
|
|
91
|
-
const pages = await this.listPages();
|
|
92
|
-
const sources = await this.listSources();
|
|
93
|
-
return { root: "memory", generatedAt: (/* @__PURE__ */ new Date()).toISOString(), sources, pages, graph: buildKnowledgeGraph(pages) };
|
|
94
|
-
}
|
|
95
|
-
async putEvent(event) {
|
|
96
|
-
this.events.push(clone(event));
|
|
97
|
-
}
|
|
98
|
-
async listEvents(query = {}) {
|
|
99
|
-
let out = this.events;
|
|
100
|
-
if (query.type) out = out.filter((event) => event.type === query.type);
|
|
101
|
-
if (query.target) out = out.filter((event) => event.target === query.target);
|
|
102
|
-
out = [...out].sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
103
|
-
return out.slice(-(query.limit ?? out.length)).map(clone);
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
var FileSystemKbStore = class extends MemoryKbStore {
|
|
107
|
-
constructor(dir) {
|
|
108
|
-
super();
|
|
109
|
-
this.dir = dir;
|
|
110
|
-
}
|
|
111
|
-
dir;
|
|
112
|
-
async putIndex(index) {
|
|
113
|
-
await super.putIndex(index);
|
|
114
|
-
await writeJson2(join(this.dir, "index.json"), index);
|
|
115
|
-
}
|
|
116
|
-
async getIndex() {
|
|
117
|
-
try {
|
|
118
|
-
return JSON.parse(await readFile(join(this.dir, "index.json"), "utf8"));
|
|
119
|
-
} catch {
|
|
120
|
-
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`);
|
|
121
132
|
}
|
|
133
|
+
map.set(fragment.id, fragment);
|
|
122
134
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const events = await this.listEvents();
|
|
126
|
-
await writeJson2(join(this.dir, "events.json"), events);
|
|
135
|
+
if (dropped > 0) {
|
|
136
|
+
warnings.push(`${side}: dropped ${dropped} unverifiable fragment(s) before diff`);
|
|
127
137
|
}
|
|
128
|
-
};
|
|
129
|
-
async function writeJson2(path, value) {
|
|
130
|
-
await mkdir(dirname(path), { recursive: true });
|
|
131
|
-
await writeFile(path, JSON.stringify(value, null, 2) + "\n", "utf8");
|
|
138
|
+
return { map, warnings };
|
|
132
139
|
}
|
|
133
|
-
function
|
|
134
|
-
return
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// src/discovery.ts
|
|
138
|
-
function createLocalDiscoveryDispatcher(worker) {
|
|
139
|
-
return {
|
|
140
|
-
async dispatch(tasks, options = {}) {
|
|
141
|
-
const concurrency = Math.max(1, options.concurrency ?? 4);
|
|
142
|
-
const results = [];
|
|
143
|
-
let cursor = 0;
|
|
144
|
-
async function runNext() {
|
|
145
|
-
while (cursor < tasks.length) {
|
|
146
|
-
if (options.signal?.aborted) throw new Error("Discovery dispatch aborted");
|
|
147
|
-
const task = tasks[cursor++];
|
|
148
|
-
results.push(await worker.run(task, options.signal));
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
await Promise.all(Array.from({ length: Math.min(concurrency, tasks.length) }, runNext));
|
|
152
|
-
return results.sort((a, b) => tasks.findIndex((task) => task.id === a.taskId) - tasks.findIndex((task) => task.id === b.taskId));
|
|
153
|
-
}
|
|
154
|
-
};
|
|
140
|
+
function dedup(items) {
|
|
141
|
+
return [...new Set(items)];
|
|
155
142
|
}
|
|
156
143
|
|
|
157
144
|
// src/chunking.ts
|
|
@@ -198,12 +185,17 @@ function splitSections(body, bodyOffset) {
|
|
|
198
185
|
const lines = body.split("\n");
|
|
199
186
|
const sections = [];
|
|
200
187
|
const headings = {};
|
|
201
|
-
let current = {
|
|
188
|
+
let current = {
|
|
189
|
+
lines: [],
|
|
190
|
+
start: bodyOffset,
|
|
191
|
+
headingPath: ""
|
|
192
|
+
};
|
|
202
193
|
let cursor = bodyOffset;
|
|
203
194
|
let fence = null;
|
|
204
195
|
const flush = () => {
|
|
205
196
|
const text = current.lines.join("\n");
|
|
206
|
-
if (text.trim() !== "")
|
|
197
|
+
if (text.trim() !== "")
|
|
198
|
+
sections.push({ text, start: current.start, headingPath: current.headingPath });
|
|
207
199
|
};
|
|
208
200
|
for (let i = 0; i < lines.length; i++) {
|
|
209
201
|
const line = lines[i];
|
|
@@ -283,66 +275,25 @@ ${chunk.text}`;
|
|
|
283
275
|
return out;
|
|
284
276
|
}
|
|
285
277
|
|
|
286
|
-
// src/
|
|
287
|
-
|
|
288
|
-
runMultiShotOptimization
|
|
289
|
-
} from "@tangle-network/agent-eval";
|
|
290
|
-
async function runKnowledgeBaseOptimization(config) {
|
|
291
|
-
return runMultiShotOptimization({
|
|
292
|
-
...config,
|
|
293
|
-
target: config.target ?? "agent-knowledge-base"
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
function knowledgeVariantFromCandidate(candidate, options = {}) {
|
|
278
|
+
// src/discovery.ts
|
|
279
|
+
function createLocalDiscoveryDispatcher(worker) {
|
|
297
280
|
return {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
const traceEvidence = releaseTraceEvidenceFromMultiShotTrials(trials);
|
|
314
|
-
const runRecords = (options.runRecords ?? [
|
|
315
|
-
...result.gate?.candidateRuns ?? [],
|
|
316
|
-
...result.gate?.baselineRuns ?? []
|
|
317
|
-
]).map(validateRunRecord);
|
|
318
|
-
const scorecard = evaluateReleaseConfidence({
|
|
319
|
-
target: "agent-knowledge-base",
|
|
320
|
-
candidateId: result.promotedVariant.id,
|
|
321
|
-
baselineId: "baseline",
|
|
322
|
-
traces: traceEvidence,
|
|
323
|
-
runs: runRecords,
|
|
324
|
-
gateDecision: result.gate?.decision ?? null,
|
|
325
|
-
thresholds: {
|
|
326
|
-
requireCorpus: false,
|
|
327
|
-
requireHoldout: Boolean(result.gate),
|
|
328
|
-
minHoldoutRuns: result.gate ? 1 : 0,
|
|
329
|
-
minSearchRuns: 1,
|
|
330
|
-
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
|
+
);
|
|
331
296
|
}
|
|
332
|
-
});
|
|
333
|
-
const release = {
|
|
334
|
-
id: stableId("krel", `${result.promotedVariant.id}:${options.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()}`),
|
|
335
|
-
candidateId: result.promotedVariant.id,
|
|
336
|
-
createdAt: options.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
337
|
-
promoted: scorecard.status !== "fail" && result.promotedVariant.id === result.searchBestVariant.id,
|
|
338
|
-
scorecard,
|
|
339
|
-
runRecordIds: runRecords.map((record) => record.runId)
|
|
340
|
-
};
|
|
341
|
-
return {
|
|
342
|
-
release,
|
|
343
|
-
scorecard,
|
|
344
|
-
candidateRuns: result.gate?.candidateRuns ?? [],
|
|
345
|
-
baselineRuns: result.gate?.baselineRuns ?? []
|
|
346
297
|
};
|
|
347
298
|
}
|
|
348
299
|
|
|
@@ -383,7 +334,11 @@ function buildEvalKnowledgeBundle(options) {
|
|
|
383
334
|
userAnswers: options.userAnswers,
|
|
384
335
|
evidenceIds: requirements.flatMap((requirement) => requirement.evidenceIds),
|
|
385
336
|
claimIds: [],
|
|
386
|
-
wikiPageIds: unique(
|
|
337
|
+
wikiPageIds: unique(
|
|
338
|
+
requirements.flatMap(
|
|
339
|
+
(requirement) => pageIdsFromResults(searchResultsByRequirement[requirement.id] ?? [])
|
|
340
|
+
)
|
|
341
|
+
),
|
|
387
342
|
metadata: options.metadata
|
|
388
343
|
});
|
|
389
344
|
const questions = userQuestionsForKnowledgeGaps(report.blockingMissingRequirements);
|
|
@@ -440,7 +395,9 @@ function requirementFromSearch(index, spec, results, now) {
|
|
|
440
395
|
}
|
|
441
396
|
function sourceFreshness(sources, now) {
|
|
442
397
|
if (sources.length === 0) return { score: 0, expiredSourceIds: [] };
|
|
443
|
-
const validUntilValues = sources.map(
|
|
398
|
+
const validUntilValues = sources.map(
|
|
399
|
+
(source) => source.validUntil ?? stringMetadata(source.metadata, "validUntil") ?? stringMetadata(source.metadata, "expiresAt")
|
|
400
|
+
).filter(isIsoDate);
|
|
444
401
|
const lastVerifiedValues = sources.map((source) => source.lastVerifiedAt ?? stringMetadata(source.metadata, "lastVerifiedAt")).filter(isIsoDate);
|
|
445
402
|
const expiredSourceIds = sources.filter((source) => {
|
|
446
403
|
const validUntil = source.validUntil ?? stringMetadata(source.metadata, "validUntil") ?? stringMetadata(source.metadata, "expiresAt");
|
|
@@ -476,6 +433,450 @@ function round(value) {
|
|
|
476
433
|
return Math.round(value * 1e3) / 1e3;
|
|
477
434
|
}
|
|
478
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
|
+
|
|
479
880
|
// src/research-loop.ts
|
|
480
881
|
import {
|
|
481
882
|
blockingKnowledgeEval,
|
|
@@ -509,7 +910,9 @@ function createKnowledgeControlLoopAdapter(options) {
|
|
|
509
910
|
};
|
|
510
911
|
},
|
|
511
912
|
validate({ state }) {
|
|
512
|
-
const errorFindings = state.validation.findings.filter(
|
|
913
|
+
const errorFindings = state.validation.findings.filter(
|
|
914
|
+
(finding) => finding.severity === "error"
|
|
915
|
+
);
|
|
513
916
|
const evals = [
|
|
514
917
|
objectiveEval({
|
|
515
918
|
id: "knowledge-valid",
|
|
@@ -636,18 +1039,24 @@ function readinessFor(options, index) {
|
|
|
636
1039
|
}
|
|
637
1040
|
export {
|
|
638
1041
|
FileSystemKbStore,
|
|
1042
|
+
IRS_DIMENSION_HINTS,
|
|
639
1043
|
KnowledgeBaseCandidateSchema,
|
|
640
1044
|
KnowledgeEventSchema,
|
|
641
1045
|
KnowledgeGraphEdgeSchema,
|
|
642
1046
|
KnowledgeGraphNodeSchema,
|
|
643
1047
|
KnowledgeIndexSchema,
|
|
644
1048
|
KnowledgePageSchema,
|
|
1049
|
+
KnowledgeProposalParseError,
|
|
1050
|
+
MAX_RESPONSE_BYTES,
|
|
1051
|
+
MIN_REQUEST_GAP_MS,
|
|
645
1052
|
MemoryKbStore,
|
|
1053
|
+
POLITE_USER_AGENT,
|
|
646
1054
|
READINESS_SPEC_DEFAULTS,
|
|
647
1055
|
SCAFFOLD_PAGE_BASENAMES,
|
|
648
1056
|
SourceAnchorSchema,
|
|
649
1057
|
SourceRecordSchema,
|
|
650
1058
|
WIKILINK_REGEX,
|
|
1059
|
+
__resetHttpThrottle,
|
|
651
1060
|
addSourcePath,
|
|
652
1061
|
addSourceText,
|
|
653
1062
|
applyKnowledgeWriteBlocks,
|
|
@@ -656,14 +1065,24 @@ export {
|
|
|
656
1065
|
buildKnowledgeGraph,
|
|
657
1066
|
buildKnowledgeIndex,
|
|
658
1067
|
chunkMarkdown,
|
|
1068
|
+
createCornellLiiSource,
|
|
1069
|
+
createD1FreshnessStoreStub,
|
|
1070
|
+
createFileSystemFreshnessStore,
|
|
1071
|
+
createIrsPublicationsSource,
|
|
659
1072
|
createKnowledgeControlLoopAdapter,
|
|
660
1073
|
createKnowledgeEvent,
|
|
661
1074
|
createLocalDiscoveryDispatcher,
|
|
1075
|
+
createStateSosSource,
|
|
662
1076
|
defineReadinessSpec,
|
|
1077
|
+
detectChanges,
|
|
663
1078
|
explainKnowledgeTarget,
|
|
1079
|
+
extractLinks,
|
|
664
1080
|
extractWikilinks,
|
|
1081
|
+
firstMatch,
|
|
665
1082
|
formatFrontmatter,
|
|
1083
|
+
htmlToText,
|
|
666
1084
|
initKnowledgeBase,
|
|
1085
|
+
innerHtmlById,
|
|
667
1086
|
inspectKnowledgeIndex,
|
|
668
1087
|
isSafeKnowledgePath,
|
|
669
1088
|
isScaffoldPath,
|
|
@@ -673,10 +1092,14 @@ export {
|
|
|
673
1092
|
lintKnowledgeIndex,
|
|
674
1093
|
loadKnowledgePages,
|
|
675
1094
|
loadSourceRegistry,
|
|
1095
|
+
looksLikeBlockPage,
|
|
676
1096
|
mediaTypeFor,
|
|
677
1097
|
normalizeLinkTarget,
|
|
678
1098
|
parseFrontmatter,
|
|
679
1099
|
parseKnowledgeWriteBlocks,
|
|
1100
|
+
politeFetch,
|
|
1101
|
+
proposeFromFinding,
|
|
1102
|
+
proposeFromFindings,
|
|
680
1103
|
reciprocalRankFusion,
|
|
681
1104
|
runKnowledgeBaseOptimization,
|
|
682
1105
|
runKnowledgeResearchLoop,
|