11// Memory Core plugin module implements tools behavior.
22import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime" ;
3- import type { MemorySource } from "openclaw/plugin-sdk/memory-core-host-engine-storage" ;
3+ import type {
4+ MemorySource ,
5+ ResolvedMemoryBackendConfig ,
6+ } from "openclaw/plugin-sdk/memory-core-host-engine-storage" ;
47import {
58 asToolParamsRecord ,
69 jsonResult ,
@@ -19,6 +22,7 @@ import {
1922 resolveMemoryDreamingConfig ,
2023 resolveMemoryDeepDreamingConfig ,
2124} from "openclaw/plugin-sdk/memory-core-host-status" ;
25+ import { resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime" ;
2226import { asRecord } from "./dreaming-shared.js" ;
2327import { filterMemorySearchHitsBySessionVisibility } from "./session-search-visibility.js" ;
2428import { recordShortTermRecalls } from "./short-term-promotion.js" ;
@@ -46,6 +50,7 @@ type MemoryManagerContext = Awaited<ReturnType<typeof getMemoryManagerContextWit
4650type ActiveMemoryManagerContext = Extract < MemoryManagerContext , { manager : unknown } > ;
4751
4852const MEMORY_SEARCH_TOOL_TIMEOUT_MS = 15_000 ;
53+ const MEMORY_SEARCH_TOOL_QMD_TIMEOUT_OVERHEAD_MS = 5_000 ;
4954const MEMORY_SEARCH_TOOL_COOLDOWN_MS = 60_000 ;
5055
5156const memorySearchToolCooldowns = new Map < string , { until : number ; error : string } > ( ) ;
@@ -76,6 +81,23 @@ function recordMemorySearchToolCooldown(key: string, error: string): void {
7681 } ) ;
7782}
7883
84+ function resolveMemorySearchToolTimeoutMs ( params : {
85+ resolvedBackend ?: ResolvedMemoryBackendConfig ;
86+ requestedCorpus ?: "memory" | "wiki" | "all" | "sessions" ;
87+ } ) : number {
88+ if ( params . requestedCorpus === "wiki" || params . resolvedBackend ?. backend !== "qmd" ) {
89+ return MEMORY_SEARCH_TOOL_TIMEOUT_MS ;
90+ }
91+ const qmdTimeoutMs = params . resolvedBackend . qmd ?. limits . timeoutMs ;
92+ if ( qmdTimeoutMs === undefined || qmdTimeoutMs <= MEMORY_SEARCH_TOOL_TIMEOUT_MS ) {
93+ return MEMORY_SEARCH_TOOL_TIMEOUT_MS ;
94+ }
95+ return resolveTimerTimeoutMs (
96+ qmdTimeoutMs + MEMORY_SEARCH_TOOL_QMD_TIMEOUT_OVERHEAD_MS ,
97+ MEMORY_SEARCH_TOOL_TIMEOUT_MS ,
98+ ) ;
99+ }
100+
79101export const testing = {
80102 resetMemorySearchToolCooldowns ( ) {
81103 memorySearchToolCooldowns . clear ( ) ;
@@ -104,8 +126,9 @@ async function runMemorySearchToolWithDeadline<T>(params: {
104126 timeoutMs : number ;
105127 run : ( signal : AbortSignal ) => Promise < T > ;
106128} ) : Promise < { status : "ok" ; value : T } | { status : "unavailable" ; error : string } > {
129+ const timeoutMs = resolveTimerTimeoutMs ( params . timeoutMs , MEMORY_SEARCH_TOOL_TIMEOUT_MS ) ;
107130 const timeoutError = ( ) =>
108- new Error ( `memory_search timed out after ${ Math . round ( params . timeoutMs / 1000 ) } s` ) ;
131+ new Error ( `memory_search timed out after ${ Math . round ( timeoutMs / 1000 ) } s` ) ;
109132 // Abort the losing task when the deadline fires so in-flight embedding work
110133 // is cancelled instead of retrying orphaned for minutes after the tool
111134 // already returned "timed out" to the agent.
@@ -118,7 +141,7 @@ async function runMemorySearchToolWithDeadline<T>(params: {
118141 // timeout result with a provider-wrapped abort error.
119142 resolve ( "timeout" ) ;
120143 controller . abort ( timeoutError ( ) ) ;
121- } , params . timeoutMs ) ;
144+ } , timeoutMs ) ;
122145 timer . unref ?.( ) ;
123146 } ) ;
124147 const task = params . run ( controller . signal ) ;
@@ -393,6 +416,9 @@ export function createMemorySearchTool(options: {
393416 } ) ;
394417 const cooldown =
395418 requestedCorpus === "wiki" ? undefined : readMemorySearchToolCooldown ( cooldownKey ) ;
419+ const memoryRuntime =
420+ requestedCorpus === "wiki" ? undefined : await loadMemoryToolRuntime ( ) ;
421+ const resolvedBackend = memoryRuntime ?. resolveMemoryBackendConfig ( { cfg, agentId } ) ;
396422 let activeUnavailablePhase : "memory" | "supplement" | undefined ;
397423 let failedUnavailablePhase : "memory" | "supplement" | undefined ;
398424 const runUnavailablePhase = async < T > (
@@ -413,9 +439,11 @@ export function createMemorySearchTool(options: {
413439 } ;
414440
415441 const outcome = await runMemorySearchToolWithDeadline ( {
416- timeoutMs : MEMORY_SEARCH_TOOL_TIMEOUT_MS ,
442+ timeoutMs : resolveMemorySearchToolTimeoutMs ( {
443+ resolvedBackend,
444+ requestedCorpus,
445+ } ) ,
417446 run : async ( deadlineSignal ) => {
418- const { resolveMemoryBackendConfig } = await loadMemoryToolRuntime ( ) ;
419447 const shouldQuerySupplements = requestedCorpus === "wiki" || requestedCorpus === "all" ;
420448 const shouldQueryMemory = requestedCorpus !== "wiki" && ! cooldown ;
421449 if ( cooldown && ! shouldQuerySupplements ) {
@@ -555,12 +583,11 @@ export function createMemorySearchTool(options: {
555583 }
556584 const status = activeMemory . manager . status ( ) ;
557585 const decorated = decorateCitations ( rawResults , includeCitations ) ;
558- const resolved = resolveMemoryBackendConfig ( { cfg, agentId } ) ;
559586 const memoryResults =
560587 status . backend === "qmd"
561588 ? clampResultsByInjectedChars (
562589 decorated ,
563- resolved . qmd ?. limits . maxInjectedChars ,
590+ resolvedBackend ? .qmd ?. limits . maxInjectedChars ,
564591 )
565592 : decorated ;
566593 surfacedMemoryResults = memoryResults . map ( ( result ) => ( {
0 commit comments