@tangle-network/agent-knowledge 1.6.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +114 -1
- package/dist/chunk-MU5CEOGE.js +387 -0
- package/dist/chunk-MU5CEOGE.js.map +1 -0
- package/dist/chunk-U6KQ4FS3.js +585 -0
- package/dist/chunk-U6KQ4FS3.js.map +1 -0
- package/dist/{chunk-HKYD765Q.js → chunk-WWY5FTKQ.js} +11 -14
- package/dist/chunk-WWY5FTKQ.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +265 -2
- package/dist/index.js +293 -9
- package/dist/index.js.map +1 -1
- package/dist/memory/index.d.ts +161 -0
- package/dist/memory/index.js +24 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/profiles/index.d.ts +1 -1
- package/dist/profiles/index.js +4 -361
- package/dist/profiles/index.js.map +1 -1
- package/docs/recursive-research-leaf.md +86 -0
- package/package.json +10 -4
- package/dist/chunk-HKYD765Q.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -33,13 +33,25 @@ import {
|
|
|
33
33
|
reciprocalRankFusion,
|
|
34
34
|
searchKnowledge,
|
|
35
35
|
sourceRegistryPath,
|
|
36
|
+
stringMetadata,
|
|
36
37
|
textSourceAdapter,
|
|
37
38
|
tokenizeQuery,
|
|
38
39
|
validateKnowledgeIndex,
|
|
39
40
|
writeJson,
|
|
40
41
|
writeKnowledgeIndex,
|
|
41
42
|
writeSourceRegistry
|
|
42
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-WWY5FTKQ.js";
|
|
44
|
+
import {
|
|
45
|
+
AgentMemoryHitSchema,
|
|
46
|
+
AgentMemoryKindSchema,
|
|
47
|
+
AgentMemoryScopeSchema,
|
|
48
|
+
AgentMemoryWriteInputSchema,
|
|
49
|
+
createNeo4jAgentMemoryAdapter,
|
|
50
|
+
defaultGetMemoryContext,
|
|
51
|
+
memoryHitToSourceRecord,
|
|
52
|
+
memoryWriteResultToSourceRecord,
|
|
53
|
+
renderMemoryContext
|
|
54
|
+
} from "./chunk-U6KQ4FS3.js";
|
|
43
55
|
import {
|
|
44
56
|
IRS_DIMENSION_HINTS,
|
|
45
57
|
MAX_RESPONSE_BYTES,
|
|
@@ -61,6 +73,9 @@ import {
|
|
|
61
73
|
slugify,
|
|
62
74
|
stableId
|
|
63
75
|
} from "./chunk-YMKHCTS2.js";
|
|
76
|
+
import {
|
|
77
|
+
researcherProfile
|
|
78
|
+
} from "./chunk-MU5CEOGE.js";
|
|
64
79
|
|
|
65
80
|
// src/changes.ts
|
|
66
81
|
function detectChanges(prev, next, options = {}) {
|
|
@@ -410,10 +425,6 @@ function sourceFreshness(sources, now) {
|
|
|
410
425
|
expiredSourceIds
|
|
411
426
|
};
|
|
412
427
|
}
|
|
413
|
-
function stringMetadata(metadata, key) {
|
|
414
|
-
const value = metadata?.[key];
|
|
415
|
-
return typeof value === "string" ? value : void 0;
|
|
416
|
-
}
|
|
417
428
|
function isIsoDate(value) {
|
|
418
429
|
return Boolean(value && Number.isFinite(Date.parse(value)));
|
|
419
430
|
}
|
|
@@ -826,6 +837,11 @@ function knowledgeReleaseReport(input) {
|
|
|
826
837
|
gateDecision: input.gateDecision ?? null,
|
|
827
838
|
thresholds: {
|
|
828
839
|
requireCorpus: false,
|
|
840
|
+
// The report gates on RunRecord-level outcomes, not on a separate scenario
|
|
841
|
+
// corpus (KnowledgeReleaseInput carries no dataset/scenarios). The scenario
|
|
842
|
+
// floor must therefore be 0 — otherwise the corpus axis fails closed on a
|
|
843
|
+
// dimension the report has no way to satisfy, masking the run-based gate.
|
|
844
|
+
minScenarioCount: 0,
|
|
829
845
|
requireHoldout: input.hasHoldout ?? false,
|
|
830
846
|
minHoldoutRuns: input.hasHoldout ? 1 : 0,
|
|
831
847
|
minSearchRuns: 1,
|
|
@@ -848,6 +864,19 @@ import {
|
|
|
848
864
|
blockingKnowledgeEval,
|
|
849
865
|
objectiveEval
|
|
850
866
|
} from "@tangle-network/agent-eval";
|
|
867
|
+
|
|
868
|
+
// src/readiness-helpers.ts
|
|
869
|
+
function readinessFor(options, index) {
|
|
870
|
+
if (!options.readinessSpecs?.length) return void 0;
|
|
871
|
+
return buildEvalKnowledgeBundle({
|
|
872
|
+
...options.readiness ?? {},
|
|
873
|
+
taskId: options.readinessTaskId ?? options.goal,
|
|
874
|
+
index,
|
|
875
|
+
specs: options.readinessSpecs
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
// src/research-loop.ts
|
|
851
880
|
function createKnowledgeControlLoopAdapter(options) {
|
|
852
881
|
let initialized = false;
|
|
853
882
|
const appliedSteps = [];
|
|
@@ -994,16 +1023,261 @@ async function applyKnowledgeResearchDecision(options, decision, iteration = 1)
|
|
|
994
1023
|
metadata: decision.metadata
|
|
995
1024
|
};
|
|
996
1025
|
}
|
|
997
|
-
|
|
998
|
-
|
|
1026
|
+
|
|
1027
|
+
// src/research-supervisor.ts
|
|
1028
|
+
import {
|
|
1029
|
+
supervise
|
|
1030
|
+
} from "@tangle-network/agent-runtime/loops";
|
|
1031
|
+
var RESEARCH_SUPERVISOR_SYSTEM_PROMPT = [
|
|
1032
|
+
"You are a research SUPERVISOR. You do not answer the research question yourself \u2014",
|
|
1033
|
+
"you create and manage a team of researcher workers that grow ONE shared",
|
|
1034
|
+
"knowledge base until it is ready.",
|
|
1035
|
+
"",
|
|
1036
|
+
"Each round:",
|
|
1037
|
+
" 1. Read the goal and the gaps the readiness gate still reports.",
|
|
1038
|
+
" 2. DECOMPOSE the open work into independent sub-topics. Spawn ONE researcher",
|
|
1039
|
+
" per sub-topic (spawn_agent) \u2014 split by sub-topic, never duplicate work.",
|
|
1040
|
+
" Spawn more researchers for a broad goal, fewer for a narrow one.",
|
|
1041
|
+
" 3. Wait for the researchers to settle (await_event). Steer or re-spawn for",
|
|
1042
|
+
" the sub-topics that came back thin.",
|
|
1043
|
+
" 4. STOP as soon as the deliverable check reports the knowledge base is ready.",
|
|
1044
|
+
"",
|
|
1045
|
+
"Conserve your budget: every spawn spends from one shared pool. Prefer a small",
|
|
1046
|
+
"number of well-scoped researchers over a large fanout that re-covers the same",
|
|
1047
|
+
"ground."
|
|
1048
|
+
].join("\n");
|
|
1049
|
+
function knowledgeReadinessDeliverable(options) {
|
|
1050
|
+
return {
|
|
1051
|
+
describe: `knowledge base at ${options.root} is ready for: ${options.goal}`,
|
|
1052
|
+
async check() {
|
|
1053
|
+
const index = await buildKnowledgeIndex(options.root);
|
|
1054
|
+
const bundle = readinessFor(options, index);
|
|
1055
|
+
return (bundle?.report.blockingMissingRequirements.length ?? 0) === 0;
|
|
1056
|
+
}
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
async function runResearchSupervisor(options) {
|
|
1060
|
+
await initKnowledgeBase(options.root);
|
|
1061
|
+
const { profile: workerProfile } = researcherProfile({ harness: options.harness });
|
|
1062
|
+
const baseInstructions = options.supervisorSystemPrompt ?? RESEARCH_SUPERVISOR_SYSTEM_PROMPT;
|
|
1063
|
+
const workerContract = workerProfile.prompt?.systemPrompt;
|
|
1064
|
+
const systemPrompt = workerContract ? `${baseInstructions}
|
|
1065
|
+
|
|
1066
|
+
Each researcher worker you spawn follows this contract:
|
|
1067
|
+
${workerContract}` : baseInstructions;
|
|
1068
|
+
const profile = {
|
|
1069
|
+
name: "research-supervisor",
|
|
1070
|
+
model: options.supervisorModel,
|
|
1071
|
+
systemPrompt
|
|
1072
|
+
};
|
|
1073
|
+
return supervise(profile, options.goal, {
|
|
1074
|
+
...options.superviseOptions,
|
|
1075
|
+
budget: options.budget,
|
|
1076
|
+
backend: options.backend,
|
|
1077
|
+
deliverable: knowledgeReadinessDeliverable(options)
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
// src/two-agent-research-loop.ts
|
|
1082
|
+
async function runTwoAgentResearchLoop(options) {
|
|
1083
|
+
const maxRounds = Math.max(1, options.maxRounds ?? 3);
|
|
1084
|
+
await initKnowledgeBase(options.root);
|
|
1085
|
+
const steps = [];
|
|
1086
|
+
let index = await buildKnowledgeIndex(options.root);
|
|
1087
|
+
let readiness = readinessFor(options, index);
|
|
1088
|
+
let ready = isReady(readiness?.report);
|
|
1089
|
+
let steer;
|
|
1090
|
+
for (let round2 = 1; round2 <= maxRounds && !ready; round2++) {
|
|
1091
|
+
if (options.signal?.aborted) throw new Error("Two-agent research loop aborted");
|
|
1092
|
+
const gaps = gapsFromReadiness(readiness);
|
|
1093
|
+
const workerContribution = await options.worker({
|
|
1094
|
+
root: options.root,
|
|
1095
|
+
goal: options.goal,
|
|
1096
|
+
round: round2,
|
|
1097
|
+
index,
|
|
1098
|
+
gaps,
|
|
1099
|
+
steer,
|
|
1100
|
+
readiness: requireReadiness(readiness, options),
|
|
1101
|
+
signal: options.signal
|
|
1102
|
+
});
|
|
1103
|
+
const accepted = [];
|
|
1104
|
+
const rejectedWorkerSources = [];
|
|
1105
|
+
const existingUris = new Set(
|
|
1106
|
+
index.sources.flatMap(
|
|
1107
|
+
(source) => typeof source.metadata?.originalUri === "string" ? [source.metadata.originalUri] : []
|
|
1108
|
+
)
|
|
1109
|
+
);
|
|
1110
|
+
for (const source of workerContribution.sources ?? []) {
|
|
1111
|
+
if (isDuplicate(source, existingUris, accepted)) {
|
|
1112
|
+
rejectedWorkerSources.push({ source, reason: "duplicate: already in the knowledge base" });
|
|
1113
|
+
continue;
|
|
1114
|
+
}
|
|
1115
|
+
const verdict = await options.driver.verifySource(source, {
|
|
1116
|
+
root: options.root,
|
|
1117
|
+
goal: options.goal,
|
|
1118
|
+
round: round2,
|
|
1119
|
+
index,
|
|
1120
|
+
gaps,
|
|
1121
|
+
acceptedThisRound: accepted,
|
|
1122
|
+
signal: options.signal
|
|
1123
|
+
});
|
|
1124
|
+
if (verdict.accept) accepted.push(source);
|
|
1125
|
+
else rejectedWorkerSources.push({ source, reason: verdict.reason });
|
|
1126
|
+
}
|
|
1127
|
+
const acceptedWorkerSources = await registerSources(options, accepted);
|
|
1128
|
+
const writtenPages = [];
|
|
1129
|
+
writtenPages.push(
|
|
1130
|
+
...await applyPages(options.root, workerContribution, acceptedWorkerSources)
|
|
1131
|
+
);
|
|
1132
|
+
index = await buildKnowledgeIndex(options.root);
|
|
1133
|
+
readiness = readinessFor(options, index);
|
|
1134
|
+
let driverSources = [];
|
|
1135
|
+
let driverNotes;
|
|
1136
|
+
if (options.driverResearches && options.driver.research) {
|
|
1137
|
+
const remainingGaps2 = gapsFromReadiness(readiness);
|
|
1138
|
+
const driverContribution = await options.driver.research({
|
|
1139
|
+
root: options.root,
|
|
1140
|
+
goal: options.goal,
|
|
1141
|
+
round: round2,
|
|
1142
|
+
index,
|
|
1143
|
+
remainingGaps: remainingGaps2,
|
|
1144
|
+
readiness: requireReadiness(readiness, options),
|
|
1145
|
+
signal: options.signal
|
|
1146
|
+
});
|
|
1147
|
+
driverNotes = driverContribution.notes;
|
|
1148
|
+
driverSources = await registerSources(options, driverContribution.sources ?? []);
|
|
1149
|
+
writtenPages.push(...await applyPages(options.root, driverContribution, driverSources));
|
|
1150
|
+
index = await buildKnowledgeIndex(options.root);
|
|
1151
|
+
readiness = readinessFor(options, index);
|
|
1152
|
+
}
|
|
1153
|
+
ready = isReady(readiness?.report);
|
|
1154
|
+
const remainingGaps = gapsFromReadiness(readiness);
|
|
1155
|
+
steer = ready ? void 0 : foldGaps(options.driver, remainingGaps);
|
|
1156
|
+
const step = {
|
|
1157
|
+
round: round2,
|
|
1158
|
+
gaps,
|
|
1159
|
+
acceptedWorkerSources,
|
|
1160
|
+
rejectedWorkerSources,
|
|
1161
|
+
driverSources,
|
|
1162
|
+
writtenPages,
|
|
1163
|
+
readiness,
|
|
1164
|
+
ready,
|
|
1165
|
+
event: createKnowledgeEvent({
|
|
1166
|
+
type: "research.iteration",
|
|
1167
|
+
actor: options.actor,
|
|
1168
|
+
target: options.root,
|
|
1169
|
+
metadata: {
|
|
1170
|
+
goal: options.goal,
|
|
1171
|
+
round: round2,
|
|
1172
|
+
ready,
|
|
1173
|
+
acceptedWorkerSourceCount: acceptedWorkerSources.length,
|
|
1174
|
+
rejectedWorkerSourceCount: rejectedWorkerSources.length,
|
|
1175
|
+
driverSourceCount: driverSources.length,
|
|
1176
|
+
writtenPageCount: writtenPages.length,
|
|
1177
|
+
remainingGapCount: remainingGaps.length
|
|
1178
|
+
}
|
|
1179
|
+
}),
|
|
1180
|
+
notes: { worker: workerContribution.notes, driver: driverNotes }
|
|
1181
|
+
};
|
|
1182
|
+
steps.push(step);
|
|
1183
|
+
await options.onRound?.(step);
|
|
1184
|
+
}
|
|
1185
|
+
return {
|
|
1186
|
+
root: options.root,
|
|
1187
|
+
goal: options.goal,
|
|
1188
|
+
rounds: steps.length,
|
|
1189
|
+
ready,
|
|
1190
|
+
index,
|
|
1191
|
+
readiness,
|
|
1192
|
+
steps
|
|
1193
|
+
};
|
|
1194
|
+
}
|
|
1195
|
+
function isReady(report) {
|
|
1196
|
+
if (!report) return false;
|
|
1197
|
+
return report.blockingMissingRequirements.length === 0;
|
|
1198
|
+
}
|
|
1199
|
+
function gapsFromReadiness(readiness) {
|
|
1200
|
+
if (!readiness) return [];
|
|
1201
|
+
const blocking = readiness.report.blockingMissingRequirements.map(
|
|
1202
|
+
(requirement) => gapFor(requirement, readiness, true)
|
|
1203
|
+
);
|
|
1204
|
+
const nonBlocking = readiness.report.nonBlockingGaps.map(
|
|
1205
|
+
(requirement) => gapFor(requirement, readiness, false)
|
|
1206
|
+
);
|
|
1207
|
+
return [...blocking, ...nonBlocking];
|
|
1208
|
+
}
|
|
1209
|
+
function gapFor(requirement, readiness, blocking) {
|
|
1210
|
+
const spec = readiness.requirements.find((entry) => entry.id === requirement.id);
|
|
1211
|
+
const query = typeof spec?.metadata?.query === "string" ? spec.metadata.query : requirement.description;
|
|
1212
|
+
return { id: requirement.id, description: requirement.description, query, blocking };
|
|
1213
|
+
}
|
|
1214
|
+
function foldGaps(driver, gaps) {
|
|
1215
|
+
if (gaps.length === 0) return void 0;
|
|
1216
|
+
if (driver.foldGaps) return driver.foldGaps(gaps);
|
|
1217
|
+
return [
|
|
1218
|
+
"The knowledge base is still missing the following. Prioritise these next round:",
|
|
1219
|
+
...gaps.map(
|
|
1220
|
+
(gap) => `- (${gap.blocking ? "blocking" : "soft"}) ${gap.description} [${gap.id}]`
|
|
1221
|
+
)
|
|
1222
|
+
].join("\n");
|
|
1223
|
+
}
|
|
1224
|
+
function isDuplicate(source, existingUris, accepted) {
|
|
1225
|
+
return existingUris.has(source.uri) || accepted.some((candidate) => candidate.uri === source.uri);
|
|
1226
|
+
}
|
|
1227
|
+
async function registerSources(options, sources) {
|
|
1228
|
+
const records = [];
|
|
1229
|
+
for (const source of sources) {
|
|
1230
|
+
records.push(await addSourceText(options.root, source, options.sourceOptions));
|
|
1231
|
+
}
|
|
1232
|
+
return records;
|
|
1233
|
+
}
|
|
1234
|
+
async function applyPages(root, contribution, acceptedSources) {
|
|
1235
|
+
if (acceptedSources.length === 0) return [];
|
|
1236
|
+
const parts = [];
|
|
1237
|
+
if (contribution.proposalText) parts.push(contribution.proposalText);
|
|
1238
|
+
const built = contribution.buildPages?.(acceptedSources);
|
|
1239
|
+
if (built) parts.push(built);
|
|
1240
|
+
if (parts.length === 0) return [];
|
|
1241
|
+
const applied = await applyKnowledgeWriteBlocks(root, parts.join("\n"));
|
|
1242
|
+
return applied.written;
|
|
1243
|
+
}
|
|
1244
|
+
function requireReadiness(readiness, options) {
|
|
1245
|
+
if (readiness) return readiness;
|
|
999
1246
|
return buildEvalKnowledgeBundle({
|
|
1000
1247
|
...options.readiness ?? {},
|
|
1001
1248
|
taskId: options.readinessTaskId ?? options.goal,
|
|
1002
|
-
index,
|
|
1003
|
-
specs:
|
|
1249
|
+
index: emptyIndex(options.root),
|
|
1250
|
+
specs: []
|
|
1004
1251
|
});
|
|
1005
1252
|
}
|
|
1253
|
+
function emptyIndex(root) {
|
|
1254
|
+
return {
|
|
1255
|
+
root,
|
|
1256
|
+
generatedAt: (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
1257
|
+
sources: [],
|
|
1258
|
+
pages: [],
|
|
1259
|
+
graph: { nodes: [], edges: [] }
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
function sourceMatchesGaps(source, index, gaps) {
|
|
1263
|
+
const haystack = `${source.title ?? ""}
|
|
1264
|
+
${source.text}`.toLowerCase();
|
|
1265
|
+
const hits = [];
|
|
1266
|
+
for (const gap of gaps) {
|
|
1267
|
+
for (const token of gap.query.toLowerCase().split(/\s+/).filter(Boolean)) {
|
|
1268
|
+
if (haystack.includes(token)) {
|
|
1269
|
+
hits.push(...searchKnowledge(index, gap.query, 1));
|
|
1270
|
+
break;
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
return hits;
|
|
1275
|
+
}
|
|
1006
1276
|
export {
|
|
1277
|
+
AgentMemoryHitSchema,
|
|
1278
|
+
AgentMemoryKindSchema,
|
|
1279
|
+
AgentMemoryScopeSchema,
|
|
1280
|
+
AgentMemoryWriteInputSchema,
|
|
1007
1281
|
FileSystemKbStore,
|
|
1008
1282
|
IRS_DIMENSION_HINTS,
|
|
1009
1283
|
KnowledgeBaseCandidateSchema,
|
|
@@ -1018,6 +1292,7 @@ export {
|
|
|
1018
1292
|
MemoryKbStore,
|
|
1019
1293
|
POLITE_USER_AGENT,
|
|
1020
1294
|
READINESS_SPEC_DEFAULTS,
|
|
1295
|
+
RESEARCH_SUPERVISOR_SYSTEM_PROMPT,
|
|
1021
1296
|
SCAFFOLD_PAGE_BASENAMES,
|
|
1022
1297
|
SourceAnchorSchema,
|
|
1023
1298
|
SourceRecordSchema,
|
|
@@ -1038,7 +1313,9 @@ export {
|
|
|
1038
1313
|
createKnowledgeControlLoopAdapter,
|
|
1039
1314
|
createKnowledgeEvent,
|
|
1040
1315
|
createLocalDiscoveryDispatcher,
|
|
1316
|
+
createNeo4jAgentMemoryAdapter,
|
|
1041
1317
|
createStateSosSource,
|
|
1318
|
+
defaultGetMemoryContext,
|
|
1042
1319
|
defineReadinessSpec,
|
|
1043
1320
|
detectChanges,
|
|
1044
1321
|
explainKnowledgeTarget,
|
|
@@ -1052,6 +1329,7 @@ export {
|
|
|
1052
1329
|
inspectKnowledgeIndex,
|
|
1053
1330
|
isSafeKnowledgePath,
|
|
1054
1331
|
isScaffoldPath,
|
|
1332
|
+
knowledgeReadinessDeliverable,
|
|
1055
1333
|
knowledgeReleaseReport,
|
|
1056
1334
|
layoutFor,
|
|
1057
1335
|
lintKnowledgeIndex,
|
|
@@ -1059,6 +1337,8 @@ export {
|
|
|
1059
1337
|
loadSourceRegistry,
|
|
1060
1338
|
looksLikeBlockPage,
|
|
1061
1339
|
mediaTypeFor,
|
|
1340
|
+
memoryHitToSourceRecord,
|
|
1341
|
+
memoryWriteResultToSourceRecord,
|
|
1062
1342
|
normalizeLinkTarget,
|
|
1063
1343
|
parseFrontmatter,
|
|
1064
1344
|
parseKnowledgeWriteBlocks,
|
|
@@ -1066,10 +1346,14 @@ export {
|
|
|
1066
1346
|
proposeFromFinding,
|
|
1067
1347
|
proposeFromFindings,
|
|
1068
1348
|
reciprocalRankFusion,
|
|
1349
|
+
renderMemoryContext,
|
|
1069
1350
|
runKnowledgeResearchLoop,
|
|
1351
|
+
runResearchSupervisor,
|
|
1352
|
+
runTwoAgentResearchLoop,
|
|
1070
1353
|
searchKnowledge,
|
|
1071
1354
|
sha256,
|
|
1072
1355
|
slugify,
|
|
1356
|
+
sourceMatchesGaps,
|
|
1073
1357
|
sourceRegistryPath,
|
|
1074
1358
|
stableId,
|
|
1075
1359
|
stripFrontmatter,
|