Files
2026-05-30 14:16:04 -05:00

129 lines
3.8 KiB
TypeScript

import { Type } from "@sinclair/typebox"
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry"
import type { BmClient } from "../bm-client.ts"
import { log } from "../logger.ts"
export function registerContextTool(
api: OpenClawPluginApi,
client: BmClient,
): void {
api.registerTool(
{
name: "build_context",
label: "Build Context",
description:
"Navigate the Basic Memory knowledge graph via memory:// URLs. " +
"Returns the target note plus related notes connected through the graph. " +
"Use this to follow relations and discover connected concepts.",
parameters: Type.Object({
url: Type.String({
description:
'Memory URL to navigate, e.g. "memory://agents/decisions" or "projects/my-project"',
}),
depth: Type.Optional(
Type.Number({
description: "How many relation hops to follow (default: 1)",
}),
),
project: Type.Optional(
Type.String({
description: "Target project name (defaults to current project)",
}),
),
}),
async execute(
_toolCallId: string,
params: { url: string; depth?: number; project?: string },
) {
const depth = params.depth ?? 1
log.debug(`build_context: url="${params.url}" depth=${depth}`)
try {
const ctx = await client.buildContext(
params.url,
depth,
params.project,
)
if (!ctx.results || ctx.results.length === 0) {
return {
content: [
{
type: "text" as const,
text: `No context found for "${params.url}".`,
},
],
details: {
url: params.url,
depth,
resultCount: 0,
},
}
}
const sections: string[] = []
for (const result of ctx.results) {
const primary = result.primary_result
sections.push(`## ${primary.title}\n${primary.content}`)
if (result.observations?.length > 0) {
const obs = result.observations
.map((o) => `- [${o.category}] ${o.content}`)
.join("\n")
sections.push(`### Observations\n${obs}`)
}
if (result.related_results?.length > 0) {
const rels = result.related_results
.map((r) => {
if (r.type === "relation") {
return `- ${r.relation_type} → **${r.to_entity}**`
}
const label = r.relation_type
? `${r.relation_type} → **${r.title}**`
: `**${r.title}**`
return r.permalink
? `- ${label} (${r.permalink})`
: `- ${label}`
})
.join("\n")
sections.push(`### Related\n${rels}`)
}
}
return {
content: [
{
type: "text" as const,
text: sections.join("\n\n"),
},
],
details: {
url: params.url,
depth,
resultCount: ctx.results.length,
},
}
} catch (err) {
log.error("build_context failed", err)
return {
content: [
{
type: "text" as const,
text: `Failed to build context for "${params.url}". Check logs for details.`,
},
],
details: {
url: params.url,
depth,
error: "build_context_failed",
},
}
}
},
},
{ name: "build_context" },
)
}