@sesamespace/hivemind 0.10.0 → 0.11.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/.pnpmrc.json +1 -0
- package/AUTO-DEBUG-DESIGN.md +267 -0
- package/AUTOMATIC-MEMORY-MANAGEMENT.md +109 -0
- package/DASHBOARD-PLAN.md +206 -0
- package/MEMORY-ENHANCEMENT-PLAN.md +211 -0
- package/TOOL-USE-DESIGN.md +173 -0
- package/dist/{chunk-FBQBBAPZ.js → chunk-4C6B2AMB.js} +2 -2
- package/dist/{chunk-FK6WYXRM.js → chunk-4YXOQGQC.js} +2 -2
- package/dist/{chunk-IXBIAX76.js → chunk-K6KL2VD6.js} +2 -2
- package/dist/{chunk-BHCDOHSK.js → chunk-LYL5GG2F.js} +3 -3
- package/dist/{chunk-M3A2WRXM.js → chunk-OB6OXLPC.js} +430 -2
- package/dist/chunk-OB6OXLPC.js.map +1 -0
- package/dist/{chunk-DPLCEMEC.js → chunk-ZA4NWNS6.js} +2 -2
- package/dist/commands/fleet.js +3 -3
- package/dist/commands/init.js +3 -3
- package/dist/commands/start.js +3 -3
- package/dist/commands/watchdog.js +3 -3
- package/dist/dashboard.html +100 -60
- package/dist/index.js +2 -2
- package/dist/main.js +6 -6
- package/dist/start.js +1 -1
- package/docs/TOOL-PARITY-PLAN.md +191 -0
- package/package.json +23 -24
- package/src/memory/dashboard-integration.ts +295 -0
- package/src/memory/index.ts +187 -0
- package/src/memory/performance-test.ts +208 -0
- package/src/memory/processors/agent-sync.ts +312 -0
- package/src/memory/processors/command-learner.ts +298 -0
- package/src/memory/processors/memory-api-client.ts +105 -0
- package/src/memory/processors/message-flow-integration.ts +168 -0
- package/src/memory/processors/research-digester.ts +204 -0
- package/test-caitlin-access.md +11 -0
- package/dist/chunk-M3A2WRXM.js.map +0 -1
- package/install.sh +0 -162
- package/packages/memory/Cargo.lock +0 -6480
- package/packages/memory/Cargo.toml +0 -21
- package/packages/memory/src/src/context.rs +0 -179
- package/packages/memory/src/src/embeddings.rs +0 -51
- package/packages/memory/src/src/main.rs +0 -887
- package/packages/memory/src/src/promotion.rs +0 -808
- package/packages/memory/src/src/scoring.rs +0 -142
- package/packages/memory/src/src/store.rs +0 -460
- package/packages/memory/src/src/tasks.rs +0 -321
- /package/dist/{chunk-FBQBBAPZ.js.map → chunk-4C6B2AMB.js.map} +0 -0
- /package/dist/{chunk-FK6WYXRM.js.map → chunk-4YXOQGQC.js.map} +0 -0
- /package/dist/{chunk-IXBIAX76.js.map → chunk-K6KL2VD6.js.map} +0 -0
- /package/dist/{chunk-BHCDOHSK.js.map → chunk-LYL5GG2F.js.map} +0 -0
- /package/dist/{chunk-DPLCEMEC.js.map → chunk-ZA4NWNS6.js.map} +0 -0
|
@@ -7874,6 +7874,428 @@ ${output || err.message}`;
|
|
|
7874
7874
|
);
|
|
7875
7875
|
}
|
|
7876
7876
|
|
|
7877
|
+
// packages/runtime/src/tools/learn.ts
|
|
7878
|
+
function slugify(topic) {
|
|
7879
|
+
return topic.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
|
|
7880
|
+
}
|
|
7881
|
+
async function discoverResearch(topic, depth, registry) {
|
|
7882
|
+
if (!registry.has("web_search")) {
|
|
7883
|
+
throw new Error("web_search tool not available \u2014 is BRAVE_API_KEY set?");
|
|
7884
|
+
}
|
|
7885
|
+
const sources = [];
|
|
7886
|
+
const primary = await registry.execute("web_search", { query: topic, count: 8 });
|
|
7887
|
+
if (primary.startsWith("Error:")) throw new Error(primary);
|
|
7888
|
+
sources.push(...parseSearchResults(primary));
|
|
7889
|
+
if (depth === "deep") {
|
|
7890
|
+
const refinedQueries = [
|
|
7891
|
+
`"${topic}" tutorial`,
|
|
7892
|
+
`"${topic}" best practices`
|
|
7893
|
+
];
|
|
7894
|
+
for (const q of refinedQueries) {
|
|
7895
|
+
const result = await registry.execute("web_search", { query: q, count: 5 });
|
|
7896
|
+
if (!result.startsWith("Error:")) {
|
|
7897
|
+
sources.push(...parseSearchResults(result));
|
|
7898
|
+
}
|
|
7899
|
+
}
|
|
7900
|
+
}
|
|
7901
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7902
|
+
return sources.filter((s) => {
|
|
7903
|
+
if (seen.has(s.url)) return false;
|
|
7904
|
+
seen.add(s.url);
|
|
7905
|
+
return true;
|
|
7906
|
+
});
|
|
7907
|
+
}
|
|
7908
|
+
function parseSearchResults(raw) {
|
|
7909
|
+
const entries = [];
|
|
7910
|
+
const blocks = raw.split(/\n\n(?=\d+\.\s)/);
|
|
7911
|
+
for (const block of blocks) {
|
|
7912
|
+
const titleMatch = block.match(/\*\*(.+?)\*\*/);
|
|
7913
|
+
const urlMatch = block.match(/\s+(https?:\/\/\S+)/);
|
|
7914
|
+
const lines = block.split("\n");
|
|
7915
|
+
const snippet = lines.length >= 3 ? lines.slice(2).join(" ").trim() : "";
|
|
7916
|
+
if (titleMatch && urlMatch) {
|
|
7917
|
+
entries.push({
|
|
7918
|
+
title: titleMatch[1],
|
|
7919
|
+
url: urlMatch[1],
|
|
7920
|
+
snippet
|
|
7921
|
+
});
|
|
7922
|
+
}
|
|
7923
|
+
}
|
|
7924
|
+
return entries;
|
|
7925
|
+
}
|
|
7926
|
+
async function exploreResearch(topic, sources, depth, focusAreas, registry, llm) {
|
|
7927
|
+
if (!registry.has("web_fetch")) {
|
|
7928
|
+
throw new Error("web_fetch tool not available");
|
|
7929
|
+
}
|
|
7930
|
+
const maxSources = depth === "deep" ? 6 : 3;
|
|
7931
|
+
const toFetch = sources.slice(0, maxSources);
|
|
7932
|
+
const findings = [];
|
|
7933
|
+
for (const source of toFetch) {
|
|
7934
|
+
try {
|
|
7935
|
+
const content = await registry.execute("web_fetch", {
|
|
7936
|
+
url: source.url,
|
|
7937
|
+
max_chars: 15e3
|
|
7938
|
+
});
|
|
7939
|
+
if (content.startsWith("Fetch failed:") || content.startsWith("Fetch error:")) {
|
|
7940
|
+
continue;
|
|
7941
|
+
}
|
|
7942
|
+
const focusClause = focusAreas.length > 0 ? `Focus especially on: ${focusAreas.join(", ")}.` : "";
|
|
7943
|
+
const messages = [
|
|
7944
|
+
{
|
|
7945
|
+
role: "system",
|
|
7946
|
+
content: "You are a research assistant. Extract key facts concisely. Return a numbered list of 5-10 key points, one per line."
|
|
7947
|
+
},
|
|
7948
|
+
{
|
|
7949
|
+
role: "user",
|
|
7950
|
+
content: `Extract 5-10 key facts about "${topic}" from this text. ${focusClause}
|
|
7951
|
+
|
|
7952
|
+
Source: ${source.title} (${source.url})
|
|
7953
|
+
|
|
7954
|
+
${content.slice(0, 12e3)}`
|
|
7955
|
+
}
|
|
7956
|
+
];
|
|
7957
|
+
const response = await llm.chat(messages);
|
|
7958
|
+
const keyPoints = response.content.split("\n").map((l) => l.replace(/^\d+[\.\)]\s*/, "").trim()).filter((l) => l.length > 0);
|
|
7959
|
+
findings.push({ source: `${source.title} \u2014 ${source.url}`, keyPoints });
|
|
7960
|
+
} catch {
|
|
7961
|
+
}
|
|
7962
|
+
}
|
|
7963
|
+
return findings;
|
|
7964
|
+
}
|
|
7965
|
+
async function discoverCodebase(topic, registry) {
|
|
7966
|
+
if (!registry.has("shell")) {
|
|
7967
|
+
throw new Error("shell tool not available");
|
|
7968
|
+
}
|
|
7969
|
+
const sources = [];
|
|
7970
|
+
let targetDir = topic;
|
|
7971
|
+
if (topic.startsWith("http://") || topic.startsWith("https://") || topic.startsWith("git@")) {
|
|
7972
|
+
const slug = slugify(topic.split("/").pop()?.replace(".git", "") || "repo");
|
|
7973
|
+
const cloneDir = `/tmp/learn-${slug}`;
|
|
7974
|
+
const cloneResult = await registry.execute("shell", {
|
|
7975
|
+
command: `rm -rf ${cloneDir} && git clone --depth 1 ${topic} ${cloneDir} 2>&1 | tail -1`
|
|
7976
|
+
});
|
|
7977
|
+
if (cloneResult.toLowerCase().includes("fatal")) {
|
|
7978
|
+
throw new Error(`Failed to clone: ${cloneResult}`);
|
|
7979
|
+
}
|
|
7980
|
+
targetDir = cloneDir;
|
|
7981
|
+
}
|
|
7982
|
+
const fileList = await registry.execute("shell", {
|
|
7983
|
+
command: `find ${targetDir} -type f \\( -name '*.ts' -o -name '*.tsx' -o -name '*.js' -o -name '*.jsx' -o -name '*.py' -o -name '*.rs' -o -name '*.go' -o -name '*.java' \\) ! -path '*/node_modules/*' ! -path '*/.git/*' ! -path '*/dist/*' ! -path '*/build/*' | head -50`
|
|
7984
|
+
});
|
|
7985
|
+
for (const line of fileList.split("\n").filter(Boolean)) {
|
|
7986
|
+
sources.push({ title: line.split("/").pop() || line, url: line, snippet: "" });
|
|
7987
|
+
}
|
|
7988
|
+
if (registry.has("read_file")) {
|
|
7989
|
+
const readmeResult = await registry.execute("read_file", { path: `${targetDir}/README.md` });
|
|
7990
|
+
if (!readmeResult.startsWith("Error:")) {
|
|
7991
|
+
sources.unshift({ title: "README.md", url: `${targetDir}/README.md`, snippet: readmeResult.slice(0, 200) });
|
|
7992
|
+
}
|
|
7993
|
+
}
|
|
7994
|
+
const manifestCmd = `cat ${targetDir}/package.json 2>/dev/null || cat ${targetDir}/Cargo.toml 2>/dev/null || cat ${targetDir}/go.mod 2>/dev/null || cat ${targetDir}/pyproject.toml 2>/dev/null || echo ""`;
|
|
7995
|
+
const manifestResult = await registry.execute("shell", { command: manifestCmd });
|
|
7996
|
+
if (manifestResult.trim()) {
|
|
7997
|
+
sources.unshift({ title: "manifest", url: `${targetDir}/package.json`, snippet: manifestResult.slice(0, 200) });
|
|
7998
|
+
}
|
|
7999
|
+
return sources;
|
|
8000
|
+
}
|
|
8001
|
+
async function exploreCodebase(topic, sources, depth, focusAreas, registry, llm) {
|
|
8002
|
+
const maxFiles = depth === "deep" ? 12 : 5;
|
|
8003
|
+
const toRead = sources.slice(0, maxFiles);
|
|
8004
|
+
const findings = [];
|
|
8005
|
+
for (const source of toRead) {
|
|
8006
|
+
try {
|
|
8007
|
+
let content;
|
|
8008
|
+
if (registry.has("read_file")) {
|
|
8009
|
+
content = await registry.execute("read_file", { path: source.url });
|
|
8010
|
+
} else {
|
|
8011
|
+
content = await registry.execute("shell", { command: `cat "${source.url}" 2>/dev/null | head -200` });
|
|
8012
|
+
}
|
|
8013
|
+
if (!content || content.startsWith("Error:")) continue;
|
|
8014
|
+
const focusClause = focusAreas.length > 0 ? `Pay special attention to: ${focusAreas.join(", ")}.` : "";
|
|
8015
|
+
const messages = [
|
|
8016
|
+
{
|
|
8017
|
+
role: "system",
|
|
8018
|
+
content: "You are a code analyst. Analyze the given code file concisely. Return a numbered list of key observations."
|
|
8019
|
+
},
|
|
8020
|
+
{
|
|
8021
|
+
role: "user",
|
|
8022
|
+
content: `Analyze this code file from a project about "${topic}". Extract: purpose, key exports, patterns used, dependencies. ${focusClause}
|
|
8023
|
+
|
|
8024
|
+
File: ${source.title}
|
|
8025
|
+
|
|
8026
|
+
${content.slice(0, 1e4)}`
|
|
8027
|
+
}
|
|
8028
|
+
];
|
|
8029
|
+
const response = await llm.chat(messages);
|
|
8030
|
+
const keyPoints = response.content.split("\n").map((l) => l.replace(/^\d+[\.\)]\s*/, "").trim()).filter((l) => l.length > 0);
|
|
8031
|
+
findings.push({ source: source.title, keyPoints });
|
|
8032
|
+
} catch {
|
|
8033
|
+
}
|
|
8034
|
+
}
|
|
8035
|
+
return findings;
|
|
8036
|
+
}
|
|
8037
|
+
async function synthesize(topic, findings, focusAreas, llm) {
|
|
8038
|
+
const findingText = findings.map((f) => `### ${f.source}
|
|
8039
|
+
${f.keyPoints.map((p) => `- ${p}`).join("\n")}`).join("\n\n");
|
|
8040
|
+
const focusClause = focusAreas.length > 0 ? `
|
|
8041
|
+
Focus areas: ${focusAreas.join(", ")}` : "";
|
|
8042
|
+
const messages = [
|
|
8043
|
+
{
|
|
8044
|
+
role: "system",
|
|
8045
|
+
content: [
|
|
8046
|
+
"You are a knowledge synthesizer. Distill research findings into structured, actionable knowledge.",
|
|
8047
|
+
"Output format:",
|
|
8048
|
+
"## Core Concepts",
|
|
8049
|
+
"(key ideas and definitions)",
|
|
8050
|
+
"## Key Relationships",
|
|
8051
|
+
"(how concepts connect)",
|
|
8052
|
+
"## Important Details",
|
|
8053
|
+
"(specifics worth remembering)",
|
|
8054
|
+
"## Practical Implications",
|
|
8055
|
+
"(how to apply this knowledge)",
|
|
8056
|
+
"",
|
|
8057
|
+
"Be concise but thorough. Maximum 8000 characters."
|
|
8058
|
+
].join("\n")
|
|
8059
|
+
},
|
|
8060
|
+
{
|
|
8061
|
+
role: "user",
|
|
8062
|
+
content: `Synthesize all findings about "${topic}" into structured knowledge.${focusClause}
|
|
8063
|
+
|
|
8064
|
+
${findingText}`
|
|
8065
|
+
}
|
|
8066
|
+
];
|
|
8067
|
+
const response = await llm.chat(messages);
|
|
8068
|
+
return response.content.slice(0, 8e3);
|
|
8069
|
+
}
|
|
8070
|
+
async function storeInMemory(contextName, topic, findings, synthesis, daemonUrl) {
|
|
8071
|
+
const storedIds = [];
|
|
8072
|
+
try {
|
|
8073
|
+
await fetch(`${daemonUrl}/api/contexts`, {
|
|
8074
|
+
method: "POST",
|
|
8075
|
+
headers: { "Content-Type": "application/json" },
|
|
8076
|
+
body: JSON.stringify({
|
|
8077
|
+
name: contextName,
|
|
8078
|
+
description: `Learned knowledge: ${topic}`
|
|
8079
|
+
})
|
|
8080
|
+
});
|
|
8081
|
+
} catch {
|
|
8082
|
+
}
|
|
8083
|
+
for (const finding of findings) {
|
|
8084
|
+
const content = `[Source: ${finding.source}]
|
|
8085
|
+
${finding.keyPoints.join("\n")}`;
|
|
8086
|
+
try {
|
|
8087
|
+
const resp = await fetch(`${daemonUrl}/api/episodes`, {
|
|
8088
|
+
method: "POST",
|
|
8089
|
+
headers: { "Content-Type": "application/json" },
|
|
8090
|
+
body: JSON.stringify({
|
|
8091
|
+
context_name: contextName,
|
|
8092
|
+
role: "system",
|
|
8093
|
+
content
|
|
8094
|
+
})
|
|
8095
|
+
});
|
|
8096
|
+
if (resp.ok) {
|
|
8097
|
+
const data = await resp.json();
|
|
8098
|
+
if (data.id) storedIds.push(data.id);
|
|
8099
|
+
}
|
|
8100
|
+
} catch {
|
|
8101
|
+
}
|
|
8102
|
+
}
|
|
8103
|
+
try {
|
|
8104
|
+
const resp = await fetch(`${daemonUrl}/api/episodes`, {
|
|
8105
|
+
method: "POST",
|
|
8106
|
+
headers: { "Content-Type": "application/json" },
|
|
8107
|
+
body: JSON.stringify({
|
|
8108
|
+
context_name: contextName,
|
|
8109
|
+
role: "assistant",
|
|
8110
|
+
content: `[SYNTHESIS: ${topic}]
|
|
8111
|
+
${synthesis}`
|
|
8112
|
+
})
|
|
8113
|
+
});
|
|
8114
|
+
if (resp.ok) {
|
|
8115
|
+
const data = await resp.json();
|
|
8116
|
+
if (data.id) storedIds.push(data.id);
|
|
8117
|
+
}
|
|
8118
|
+
} catch {
|
|
8119
|
+
}
|
|
8120
|
+
try {
|
|
8121
|
+
await fetch(`${daemonUrl}/api/promotion/run?context=${encodeURIComponent(contextName)}`, {
|
|
8122
|
+
method: "POST"
|
|
8123
|
+
});
|
|
8124
|
+
} catch {
|
|
8125
|
+
}
|
|
8126
|
+
return storedIds;
|
|
8127
|
+
}
|
|
8128
|
+
function registerLearnTools(registry, config) {
|
|
8129
|
+
let llm = null;
|
|
8130
|
+
if (config.llmConfig) {
|
|
8131
|
+
try {
|
|
8132
|
+
llm = new LLMClient({
|
|
8133
|
+
...config.llmConfig,
|
|
8134
|
+
max_tokens: 2048
|
|
8135
|
+
});
|
|
8136
|
+
} catch {
|
|
8137
|
+
}
|
|
8138
|
+
}
|
|
8139
|
+
let synthesisLlm = null;
|
|
8140
|
+
if (config.llmConfig) {
|
|
8141
|
+
try {
|
|
8142
|
+
synthesisLlm = new LLMClient({
|
|
8143
|
+
...config.llmConfig,
|
|
8144
|
+
max_tokens: 4096
|
|
8145
|
+
});
|
|
8146
|
+
} catch {
|
|
8147
|
+
}
|
|
8148
|
+
}
|
|
8149
|
+
registry.register(
|
|
8150
|
+
"learn",
|
|
8151
|
+
[
|
|
8152
|
+
"Deeply research a topic and store organized knowledge in memory.",
|
|
8153
|
+
"Supports two modes: 'research' (web search + fetch) and 'codebase' (local/remote repo analysis).",
|
|
8154
|
+
"Findings are synthesized via LLM and stored as L2 episodes in a dedicated memory context,",
|
|
8155
|
+
"eligible for L3 promotion. Use this when you need thorough understanding of a topic."
|
|
8156
|
+
].join(" "),
|
|
8157
|
+
{
|
|
8158
|
+
type: "object",
|
|
8159
|
+
properties: {
|
|
8160
|
+
topic: {
|
|
8161
|
+
type: "string",
|
|
8162
|
+
description: "What to learn about (e.g., 'WebSocket protocol', '/path/to/repo', 'https://github.com/user/repo')"
|
|
8163
|
+
},
|
|
8164
|
+
type: {
|
|
8165
|
+
type: "string",
|
|
8166
|
+
enum: ["research", "codebase"],
|
|
8167
|
+
description: "Learning type: 'research' for web-based topics, 'codebase' for analyzing code (default: research)"
|
|
8168
|
+
},
|
|
8169
|
+
depth: {
|
|
8170
|
+
type: "string",
|
|
8171
|
+
enum: ["shallow", "deep"],
|
|
8172
|
+
description: "How many sources to explore: shallow (3 sources) or deep (6+ sources) (default: shallow)"
|
|
8173
|
+
},
|
|
8174
|
+
context: {
|
|
8175
|
+
type: "string",
|
|
8176
|
+
description: "Custom memory context name (default: auto-generated learn-{slug})"
|
|
8177
|
+
},
|
|
8178
|
+
focus_areas: {
|
|
8179
|
+
type: "array",
|
|
8180
|
+
items: { type: "string" },
|
|
8181
|
+
description: "Specific aspects to focus on (e.g., ['performance', 'security'])"
|
|
8182
|
+
},
|
|
8183
|
+
store: {
|
|
8184
|
+
type: "boolean",
|
|
8185
|
+
description: "Whether to persist findings in memory (default: true)"
|
|
8186
|
+
}
|
|
8187
|
+
},
|
|
8188
|
+
required: ["topic"]
|
|
8189
|
+
},
|
|
8190
|
+
async (params) => {
|
|
8191
|
+
const topic = params.topic;
|
|
8192
|
+
const type = params.type || "research";
|
|
8193
|
+
const depth = params.depth || "shallow";
|
|
8194
|
+
const contextName = params.context || `learn-${slugify(topic)}`;
|
|
8195
|
+
const focusAreas = params.focus_areas || [];
|
|
8196
|
+
const shouldStore = params.store !== false;
|
|
8197
|
+
if (!llm || !synthesisLlm) {
|
|
8198
|
+
return "Error: LLM not configured \u2014 learn tool requires an LLM for synthesis. Check your llm config.";
|
|
8199
|
+
}
|
|
8200
|
+
try {
|
|
8201
|
+
let sources;
|
|
8202
|
+
if (type === "codebase") {
|
|
8203
|
+
sources = await discoverCodebase(topic, registry);
|
|
8204
|
+
} else {
|
|
8205
|
+
sources = await discoverResearch(topic, depth, registry);
|
|
8206
|
+
}
|
|
8207
|
+
if (sources.length === 0) {
|
|
8208
|
+
return `No sources found for "${topic}". Try a different query or check connectivity.`;
|
|
8209
|
+
}
|
|
8210
|
+
let findings;
|
|
8211
|
+
if (type === "codebase") {
|
|
8212
|
+
findings = await exploreCodebase(topic, sources, depth, focusAreas, registry, llm);
|
|
8213
|
+
} else {
|
|
8214
|
+
findings = await exploreResearch(topic, sources, depth, focusAreas, registry, llm);
|
|
8215
|
+
}
|
|
8216
|
+
if (findings.length === 0) {
|
|
8217
|
+
return `Found ${sources.length} sources but could not extract meaningful findings for "${topic}".`;
|
|
8218
|
+
}
|
|
8219
|
+
const synthesis = await synthesize(topic, findings, focusAreas, synthesisLlm);
|
|
8220
|
+
let storageNote = "";
|
|
8221
|
+
if (shouldStore) {
|
|
8222
|
+
const storedIds = await storeInMemory(
|
|
8223
|
+
contextName,
|
|
8224
|
+
topic,
|
|
8225
|
+
findings,
|
|
8226
|
+
synthesis,
|
|
8227
|
+
config.memoryDaemonUrl
|
|
8228
|
+
);
|
|
8229
|
+
storageNote = storedIds.length > 0 ? `
|
|
8230
|
+
|
|
8231
|
+
---
|
|
8232
|
+
Stored ${storedIds.length} episodes in memory context "${contextName}". Use learn_recall to retrieve later.` : "\n\n---\nNote: Could not store in memory (daemon may be offline). Knowledge is shown above but not persisted.";
|
|
8233
|
+
}
|
|
8234
|
+
return `# Learned: ${topic}
|
|
8235
|
+
|
|
8236
|
+
Sources explored: ${findings.length}
|
|
8237
|
+
Type: ${type} | Depth: ${depth}
|
|
8238
|
+
|
|
8239
|
+
${synthesis}${storageNote}`;
|
|
8240
|
+
} catch (err) {
|
|
8241
|
+
return `Learn failed: ${err.message}`;
|
|
8242
|
+
}
|
|
8243
|
+
}
|
|
8244
|
+
);
|
|
8245
|
+
registry.register(
|
|
8246
|
+
"learn_recall",
|
|
8247
|
+
"Retrieve knowledge from a previous learn session. Searches across learn-* memory contexts for stored findings and synthesis.",
|
|
8248
|
+
{
|
|
8249
|
+
type: "object",
|
|
8250
|
+
properties: {
|
|
8251
|
+
topic: {
|
|
8252
|
+
type: "string",
|
|
8253
|
+
description: "Search query to find previously learned knowledge"
|
|
8254
|
+
},
|
|
8255
|
+
context: {
|
|
8256
|
+
type: "string",
|
|
8257
|
+
description: "Specific learn context to search (e.g., 'learn-websocket-protocol'). If omitted, searches all learn contexts."
|
|
8258
|
+
}
|
|
8259
|
+
},
|
|
8260
|
+
required: ["topic"]
|
|
8261
|
+
},
|
|
8262
|
+
async (params) => {
|
|
8263
|
+
const topic = params.topic;
|
|
8264
|
+
const context = params.context;
|
|
8265
|
+
const daemonUrl = config.memoryDaemonUrl;
|
|
8266
|
+
try {
|
|
8267
|
+
if (context) {
|
|
8268
|
+
const resp2 = await fetch(`${daemonUrl}/api/search`, {
|
|
8269
|
+
method: "POST",
|
|
8270
|
+
headers: { "Content-Type": "application/json" },
|
|
8271
|
+
body: JSON.stringify({ query: topic, context_name: context, top_k: 10 })
|
|
8272
|
+
});
|
|
8273
|
+
if (!resp2.ok) return `Memory search failed: ${resp2.status}`;
|
|
8274
|
+
const data2 = await resp2.json();
|
|
8275
|
+
if (!data2.results?.length) return `No learned knowledge found for "${topic}" in context "${context}".`;
|
|
8276
|
+
return data2.results.map((r, i) => `${i + 1}. [score: ${r.score.toFixed(3)}] [${r.role}]
|
|
8277
|
+
${r.content.slice(0, 500)}`).join("\n\n");
|
|
8278
|
+
}
|
|
8279
|
+
const resp = await fetch(`${daemonUrl}/api/search/cross-context`, {
|
|
8280
|
+
method: "POST",
|
|
8281
|
+
headers: { "Content-Type": "application/json" },
|
|
8282
|
+
body: JSON.stringify({ query: topic, top_k: 15 })
|
|
8283
|
+
});
|
|
8284
|
+
if (!resp.ok) return `Memory cross-search failed: ${resp.status}`;
|
|
8285
|
+
const data = await resp.json();
|
|
8286
|
+
const learnResults = data.results?.filter((r) => r.context_name.startsWith("learn-")) || [];
|
|
8287
|
+
if (learnResults.length === 0) {
|
|
8288
|
+
return `No previously learned knowledge found for "${topic}". Use the learn tool first.`;
|
|
8289
|
+
}
|
|
8290
|
+
return learnResults.map((r, i) => `${i + 1}. [score: ${r.score.toFixed(3)}] [${r.context_name}] [${r.role}]
|
|
8291
|
+
${r.content.slice(0, 500)}`).join("\n\n");
|
|
8292
|
+
} catch (err) {
|
|
8293
|
+
return `Memory daemon unreachable: ${err.message}`;
|
|
8294
|
+
}
|
|
8295
|
+
}
|
|
8296
|
+
);
|
|
8297
|
+
}
|
|
8298
|
+
|
|
7877
8299
|
// packages/runtime/src/tools/register.ts
|
|
7878
8300
|
import { resolve as resolve18 } from "path";
|
|
7879
8301
|
import { mkdirSync as mkdirSync13, existsSync as existsSync18 } from "fs";
|
|
@@ -7904,6 +8326,11 @@ function registerAllTools(hivemindHome, config) {
|
|
|
7904
8326
|
registerMacOSTools(registry, workspaceDir);
|
|
7905
8327
|
registerDataTools(registry, workspaceDir);
|
|
7906
8328
|
registerCodingAgentTools(registry, workspaceDir);
|
|
8329
|
+
registerLearnTools(registry, {
|
|
8330
|
+
llmConfig: config?.llmConfig,
|
|
8331
|
+
memoryDaemonUrl: config?.memoryDaemonUrl || "http://localhost:3434",
|
|
8332
|
+
workspaceDir
|
|
8333
|
+
});
|
|
7907
8334
|
return registry;
|
|
7908
8335
|
}
|
|
7909
8336
|
|
|
@@ -8206,7 +8633,8 @@ async function startPipeline(configPath) {
|
|
|
8206
8633
|
workspace: config.agent.workspace || "workspace",
|
|
8207
8634
|
braveApiKey: process.env.BRAVE_API_KEY,
|
|
8208
8635
|
memoryDaemonUrl: config.memory.daemon_url,
|
|
8209
|
-
configPath
|
|
8636
|
+
configPath,
|
|
8637
|
+
llmConfig: config.llm
|
|
8210
8638
|
});
|
|
8211
8639
|
const workspaceDir = resolve20(hivemindHome, config.agent.workspace || "workspace");
|
|
8212
8640
|
const skillsEngine = new SkillsEngine(workspaceDir, toolRegistry);
|
|
@@ -8987,4 +9415,4 @@ smol-toml/dist/index.js:
|
|
|
8987
9415
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
8988
9416
|
*)
|
|
8989
9417
|
*/
|
|
8990
|
-
//# sourceMappingURL=chunk-
|
|
9418
|
+
//# sourceMappingURL=chunk-OB6OXLPC.js.map
|