2026-01-12 11:22:56 +00:00
import { Type } from "@sinclair/typebox" ;
2026-01-30 03:15:10 +01:00
import type { OpenClawConfig } from "../../config/config.js" ;
2026-02-01 10:03:47 +09:00
import type { AnyAgentTool } from "./common.js" ;
2026-01-12 11:22:56 +00:00
import { getMemorySearchManager } from "../../memory/index.js" ;
import { resolveSessionAgentId } from "../agent-scope.js" ;
import { resolveMemorySearchConfig } from "../memory-search.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 : {
2026-01-30 03:15:10 +01:00
config? : OpenClawConfig ;
2026-01-12 11:22:56 +00:00
agentSessionKey? : string ;
} ) : AnyAgentTool | null {
const cfg = options . config ;
2026-01-31 16:19:20 +09:00
if ( ! cfg ) {
return null ;
}
2026-01-12 11:22:56 +00:00
const agentId = resolveSessionAgentId ( {
sessionKey : options.agentSessionKey ,
config : cfg ,
} ) ;
2026-01-31 16:19:20 +09:00
if ( ! resolveMemorySearchConfig ( cfg , agentId ) ) {
return null ;
}
2026-01-12 11:22:56 +00:00
return {
label : "Memory Search" ,
name : "memory_search" ,
description :
2026-01-17 18:53:48 +00:00
"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." ,
2026-01-12 11:22:56 +00:00
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 } ) ;
}
2026-01-17 09:45:45 +00:00
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 } ) ;
}
2026-01-12 11:22:56 +00:00
} ,
} ;
}
export function createMemoryGetTool ( options : {
2026-01-30 03:15:10 +01:00
config? : OpenClawConfig ;
2026-01-12 11:22:56 +00:00
agentSessionKey? : string ;
} ) : AnyAgentTool | null {
const cfg = options . config ;
2026-01-31 16:19:20 +09:00
if ( ! cfg ) {
return null ;
}
2026-01-12 11:22:56 +00:00
const agentId = resolveSessionAgentId ( {
sessionKey : options.agentSessionKey ,
config : cfg ,
} ) ;
2026-01-31 16:19:20 +09:00
if ( ! resolveMemorySearchConfig ( cfg , agentId ) ) {
return null ;
}
2026-01-12 11:22:56 +00:00
return {
label : "Memory Get" ,
name : "memory_get" ,
2026-01-12 23:29:44 +00:00
description :
2026-01-28 21:49:38 -05:00
"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." ,
2026-01-12 11:22:56 +00:00
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 } ) ;
}
2026-01-17 09:45:45 +00:00
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 } ) ;
}
2026-01-12 11:22:56 +00:00
} ,
} ;
}