| import { Type } from "@sinclair/typebox"; |
|
|
| import type { OpenClawConfig } from "../../config/config.js"; |
| import { getMemorySearchManager } from "../../memory/index.js"; |
| import { resolveSessionAgentId } from "../agent-scope.js"; |
| import { resolveMemorySearchConfig } from "../memory-search.js"; |
| import type { AnyAgentTool } from "./common.js"; |
| import { jsonResult, readNumberParam, readStringParam } from "./common.js"; |
|
|
| const MemorySearchSchema = Type.Object({ |
| query: Type.String(), |
| maxResults: Type.Optional(Type.Number()), |
| minScore: Type.Optional(Type.Number()), |
| }); |
|
|
| const MemoryGetSchema = Type.Object({ |
| path: Type.String(), |
| from: Type.Optional(Type.Number()), |
| lines: Type.Optional(Type.Number()), |
| }); |
|
|
| export function createMemorySearchTool(options: { |
| config?: OpenClawConfig; |
| agentSessionKey?: string; |
| }): AnyAgentTool | null { |
| const cfg = options.config; |
| if (!cfg) { |
| return null; |
| } |
| const agentId = resolveSessionAgentId({ |
| sessionKey: options.agentSessionKey, |
| config: cfg, |
| }); |
| if (!resolveMemorySearchConfig(cfg, agentId)) { |
| return null; |
| } |
| return { |
| label: "Memory Search", |
| name: "memory_search", |
| description: |
| "Mandatory recall step: semantically search MEMORY.md + memory/*.md (and optional session transcripts) before answering questions about prior work, decisions, dates, people, preferences, or todos; returns top snippets with path + lines.", |
| parameters: MemorySearchSchema, |
| execute: async (_toolCallId, params) => { |
| const query = readStringParam(params, "query", { required: true }); |
| const maxResults = readNumberParam(params, "maxResults"); |
| const minScore = readNumberParam(params, "minScore"); |
| const { manager, error } = await getMemorySearchManager({ |
| cfg, |
| agentId, |
| }); |
| if (!manager) { |
| return jsonResult({ results: [], disabled: true, error }); |
| } |
| try { |
| const results = await manager.search(query, { |
| maxResults, |
| minScore, |
| sessionKey: options.agentSessionKey, |
| }); |
| const status = manager.status(); |
| return jsonResult({ |
| results, |
| provider: status.provider, |
| model: status.model, |
| fallback: status.fallback, |
| }); |
| } catch (err) { |
| const message = err instanceof Error ? err.message : String(err); |
| return jsonResult({ results: [], disabled: true, error: message }); |
| } |
| }, |
| }; |
| } |
|
|
| export function createMemoryGetTool(options: { |
| config?: OpenClawConfig; |
| agentSessionKey?: string; |
| }): AnyAgentTool | null { |
| const cfg = options.config; |
| if (!cfg) { |
| return null; |
| } |
| const agentId = resolveSessionAgentId({ |
| sessionKey: options.agentSessionKey, |
| config: cfg, |
| }); |
| if (!resolveMemorySearchConfig(cfg, agentId)) { |
| return null; |
| } |
| return { |
| label: "Memory Get", |
| name: "memory_get", |
| description: |
| "Safe snippet read from MEMORY.md, memory/*.md, or configured memorySearch.extraPaths with optional from/lines; use after memory_search to pull only the needed lines and keep context small.", |
| parameters: MemoryGetSchema, |
| execute: async (_toolCallId, params) => { |
| const relPath = readStringParam(params, "path", { required: true }); |
| const from = readNumberParam(params, "from", { integer: true }); |
| const lines = readNumberParam(params, "lines", { integer: true }); |
| const { manager, error } = await getMemorySearchManager({ |
| cfg, |
| agentId, |
| }); |
| if (!manager) { |
| return jsonResult({ path: relPath, text: "", disabled: true, error }); |
| } |
| try { |
| const result = await manager.readFile({ |
| relPath, |
| from: from ?? undefined, |
| lines: lines ?? undefined, |
| }); |
| return jsonResult(result); |
| } catch (err) { |
| const message = err instanceof Error ? err.message : String(err); |
| return jsonResult({ path: relPath, text: "", disabled: true, error: message }); |
| } |
| }, |
| }; |
| } |
|
|